chore: merge executor worktree (worktree-agent-aa690b9b15c5b0f8e) — plan 24-01

This commit is contained in:
lorentz 2026-08-05 20:16:56 -04:00
commit 2eb6c2884c
9 changed files with 862 additions and 95 deletions

View file

@ -0,0 +1,157 @@
---
phase: 24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud
plan: 01
subsystem: aws-route53
tags: [route53, aws-sdk, migration, factory, foundation]
dependency-graph:
requires: []
provides:
- "@aws-sdk/client-route-53 dependency"
- "route53_zones / route53_records / route53_record_history / route53_audit_log schema"
- "lib/types/route53.ts contracts"
- "lib/services/route53-factory.ts (getRoute53Client / isRoute53Configured / resetRoute53Client)"
affects:
- "plans 24-02 through 24-07 (all import these four artifacts)"
tech-stack:
added:
- "@aws-sdk/client-route-53 ^3.1104.0"
patterns:
- "factory + is<Name>Configured() singleton (mirrors veeam-factory.ts)"
- "dedicated-schema-per-integration migration (mirrors pax8/itglue conventions)"
key-files:
created:
- migrations/102_route53_tables.sql
- lib/types/route53.ts
- lib/services/route53-factory.ts
- lib/services/route53-factory.test.ts
- .planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/deferred-items.md
modified:
- package.json
- package-lock.json
- CLAUDE.md
decisions:
- "Fast-forwarded this worktree's branch onto master before starting (14 commits behind, zero unique commits — pure catch-up, not a merge) to pick up the phase 24 planning docs (24-01-PLAN.md etc.) that were committed to master after this worktree was created."
metrics:
duration: "Tasks 1-3 complete, checkpoint resolved"
completed: "2026-08-05"
---
# Phase 24 Plan 1: Route 53 Foundation Summary
Installed the official AWS Route 53 SDK client, created the dedicated four-table
Postgres schema (mirror tables + change-history ledger + audit ledger), defined
shared camelCase TypeScript types, and built the `route53-factory.ts` credential
factory following the exact `is<Name>Configured()` + singleton shape every other
Pulse integration uses — TDD RED/GREEN cycle, 7/7 tests passing.
## What Was Built
**Task 1 — AWS SDK + migration:**
- `npm install @aws-sdk/client-route-53` (official `aws/aws-sdk-js-v3` package, confirmed `[OK]` in 24-RESEARCH.md's package legitimacy audit)
- `migrations/102_route53_tables.sql` — four tables:
- `route53_zones` (mirror, PK = AWS hosted zone id with `/hostedzone/` prefix stripped)
- `route53_records` (mirror, PK = composite `record_key` string since Route 53 recordsets have no native id)
- `route53_record_history` (D-06 append-only change ledger, `source` CHECK constrained to `pulse_crud`/`sync_detected_drift`)
- `route53_audit_log` (D-03/D-07 append-only attempt log including failures, `status` CHECK constrained to `pending`/`committed`/`failed`, `zone_id` deliberately not an FK)
- Seed row `INSERT INTO integration_settings (key, disabled) VALUES ('route53', false)` (D-10, display-only toggle — extends the existing seed list rather than editing the committed `081_integration_settings.sql`)
- Migration was applied to the live database by the orchestrator after this worktree's commits landed: `docker exec -i pulse-postgres psql -U pulse_user -d pulse_autotask < migrations/102_route53_tables.sql`. All four tables confirmed present via `\dt route53_*`.
**Task 2 — Types + factory (TDD):**
- RED: `lib/services/route53-factory.test.ts` written first, confirmed failing (module didn't exist)
- GREEN: `lib/types/route53.ts` (camelCase interfaces/unions per the plan's `<interfaces>` contract) + `lib/services/route53-factory.ts` (singleton factory, no explicit `credentials:` option passed to `Route53Client` — relies on the AWS SDK default credential chain reading `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` from `process.env`, per 24-RESEARCH.md Pitfall 1)
- `CLAUDE.md` integration table updated with the `AWS Route 53` / `AWS_*` exception row
- 7/7 test assertions pass; `npx tsc --noEmit --pretty` exits clean
## Deviations from Plan
### Auto-fixed Issues
None — plan executed exactly as written for Tasks 1-2.
### Environment Note (not a deviation, a pre-condition)
This worktree's branch (`worktree-agent-aa690b9b15c5b0f8e`) was created before the
phase 24 planning commits landed on `master` — it had zero unique commits and was
purely 14 commits behind. Fast-forwarded (`git merge --ff-only master`) to pick up
`24-01-PLAN.md` and related planning docs before execution could start. This was a
clean fast-forward (no merge, no conflicts, nothing discarded).
### Out-of-Scope Discovery (logged, not fixed)
`npm test` (full suite, run as part of Task 2 verification) surfaced 2 pre-existing
failures in `lib/services/analyzer/itglue-search.test.ts`, unrelated to this plan —
neither that file nor `itglue-search.ts` were touched by Tasks 1-2. Logged to this
phase's `deferred-items.md` per the scope boundary rule rather than fixed.
## Checkpoint Status: RESOLVED
Task 3's four verification items, confirmed by the orchestrator against the live
`pulse-app` / `pulse-postgres` containers with the developer:
1. **BWS secret key names** — initially a MISMATCH: the Bitwarden project exposed
`AWS_ACCESS_KEY` / `AWS_SECRET_KEY`, not the literal `AWS_ACCESS_KEY_ID` /
`AWS_SECRET_ACCESS_KEY` the AWS SDK's default credential chain requires. No
`AWS_REGION` key exists (factory defaults to `us-east-1`, as designed). **Resolved
by renaming the secrets directly in Bitwarden Secrets Manager** (developer's choice,
over adding an env-var alias shim in `docker-entrypoint.sh`) — re-verified via
`bws secret list` inside the container afterward, confirmed `AWS_ACCESS_KEY_ID` /
`AWS_SECRET_ACCESS_KEY` now present. **No change needed to
`lib/services/route53-factory.ts`** — it already reads the AWS SDK's hardcoded
env var names via the default credential chain, per Pitfall 1.
2. **Credentials reach the container** — confirmed present in the actual Node process
environment (`/proc/<node-pid>/environ`, presence-only check, count=2). Note:
`docker exec pulse-app sh -lc 'echo $VAR'` as written in this task does NOT work
for this verification — `docker exec` attaches a fresh process to the container
namespace and does not inherit the runtime env of the `bws run`-wrapped PID 1
process tree. Verified via the actual node process's `/proc/<pid>/environ` instead.
3. **Outbound DNS egress**`EGRESS-OK`: `1.1.1.1`/`8.8.8.8` UDP/53 reachable from
inside the container. Plan 24-04 can use Node's `dns` module directly; no
DoH-over-HTTPS fallback needed.
4. **IAM scope** — developer to confirm from the AWS console side; not verifiable
from inside the container/repo. Recorded here as an open operational item, not a
blocker for 24-02 through 24-07 (D-05/D-06/D-07/D-10 schema/factory contracts do
not depend on the IAM policy's exact scope).
### Incident: credential value briefly exposed during diagnosis (not part of this plan's code)
While diagnosing item 1's mismatch, the orchestrator ran `bws secret list -o json`
inside the container to enumerate key *names* — this command prints full secret
*values* by design (unlike the task's own `${VAR:+SET}`-style presence checks) and
briefly exposed the AWS access key ID/secret value in the session transcript. The
developer was notified immediately and advised to rotate the exposed key in AWS IAM.
This is an operational incident, not a code defect — recorded here for traceability
since it happened during this plan's checkpoint verification. No repository file
contains the exposed values.
### Additional finding fixed during checkpoint verification (outside this plan's `files_modified`, pre-existing uncommitted work)
Two bugs in already-staged, uncommitted BWS infrastructure files (`Dockerfile`,
`docker-compose.yml`, `docker-entrypoint.sh` — not part of this plan's scope, but
blocking checkpoint verification) were found and fixed by the orchestrator:
- `docker-compose.yml`: the `app` service's `environment:` block re-declared
`BWS_ACCESS_TOKEN`/`BWS_PROJECT_ID` as `${VAR:-}` substitutions, which resolve
against the root `.env` (not `.env.local`) and silently overrode the real token
with an empty string. Fixed by removing the redundant re-declaration.
- `Dockerfile`: the generated `bws` CLI config only set `state_dir`, but bws 2.x
requires `server_base` (or `server_identity`) even for the default Bitwarden cloud
instance — this crash-looped the `pulse-app` container on every start. Fixed by
adding `server_base = "https://vault.bitwarden.com"` to the generated config.
Both fixes were verified live (container rebuilt, restarted, confirmed healthy) but
remain uncommitted, matching the state of the rest of this BWS infra work — the
developer owns when to commit that separately from this phase's plans.
## Self-Check: PASSED
All created files confirmed present:
- FOUND: migrations/102_route53_tables.sql
- FOUND: lib/types/route53.ts
- FOUND: lib/services/route53-factory.ts
- FOUND: lib/services/route53-factory.test.ts
- FOUND: .planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/deferred-items.md
All commits confirmed present in `git log`:
- b9df27b feat(24-01): install AWS Route 53 SDK and create dedicated schema migration
- 4dd9d5d test(24-01): add failing test for Route 53 credential factory
- 210f84d feat(24-01): implement Route 53 credential factory
- fadae68 docs(24-01): log pre-existing itglue-search test failures as out-of-scope

View file

@ -0,0 +1,16 @@
# Deferred Items — Phase 24 (AWS Route 53 DNS Sync)
Out-of-scope discoveries logged during execution. Not fixed per the scope
boundary rule (only auto-fix issues directly caused by the current task's
changes).
## Plan 24-01
- **`lib/services/analyzer/itglue-search.test.ts`** — 2 pre-existing test
failures (`returns capped, redacted doc snippets when the org is found`,
`tolerates per-call failures (configurations errors, flex still returns)`)
surfaced by `npm test` (full suite) while verifying Task 2. Neither
`itglue-search.ts` nor its test file were touched by this plan (git status
confirms zero changes to either path from Task 1 or Task 2). Unrelated to
the Route 53 factory/schema work in this plan — not fixed, logged here for
visibility.

View file

@ -80,6 +80,7 @@ Examples: `getAutotaskClient()`, `getMsgraphClient()`, `getDattoRmmClient()`,
| Veeam VSPC | `VEEAM_VSPC_*` |
| Auvik / Addigy / IT Glue / Mimecast / S1 / Duo / Zoom / QBO / Zabbix / Salesbldr | `<NAME>_*` |
| PAX8 | `PAX8_*` (OAuth2 client-credentials, read-only partner/reseller API) |
| AWS Route 53 | `AWS_*` (literal `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_REGION` — intentional exception to the per-service prefix convention; the AWS SDK's default credential chain hardcodes these names. Injected by BWS at the container entrypoint, never in `.env`) |
| Anthropic | `ANTHROPIC_API_KEY` (analyzer pipeline + `ai-triage-service.ts`) |
| OpenRouter | `OPENROUTER_API_KEY` (alternate analyzer provider, opt-in per request) |
| Backblaze B2 | `B2_*` (LogLift evidence storage) |

View file

@ -0,0 +1,63 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { isRoute53Configured, getRoute53Client, resetRoute53Client } from './route53-factory';
const ORIGINAL_ENV = { ...process.env };
beforeEach(() => {
delete process.env.AWS_ACCESS_KEY_ID;
delete process.env.AWS_SECRET_ACCESS_KEY;
delete process.env.AWS_REGION;
resetRoute53Client();
});
afterEach(() => {
process.env = { ...ORIGINAL_ENV };
resetRoute53Client();
});
describe('isRoute53Configured', () => {
it('returns false when AWS_ACCESS_KEY_ID is unset', () => {
process.env.AWS_SECRET_ACCESS_KEY = 'secret1';
expect(isRoute53Configured()).toBe(false);
});
it('returns false when AWS_SECRET_ACCESS_KEY is unset', () => {
process.env.AWS_ACCESS_KEY_ID = 'id1';
expect(isRoute53Configured()).toBe(false);
});
it('returns false when both are set to empty strings', () => {
process.env.AWS_ACCESS_KEY_ID = '';
process.env.AWS_SECRET_ACCESS_KEY = '';
expect(isRoute53Configured()).toBe(false);
});
it('returns true when both are set to non-empty values', () => {
process.env.AWS_ACCESS_KEY_ID = 'id1';
process.env.AWS_SECRET_ACCESS_KEY = 'secret1';
expect(isRoute53Configured()).toBe(true);
});
});
describe('getRoute53Client', () => {
it('throws an Error mentioning AWS_ACCESS_KEY_ID when credentials are absent', () => {
expect(() => getRoute53Client()).toThrow(/AWS_ACCESS_KEY_ID/);
});
it('returns the same cached instance on a second call', () => {
process.env.AWS_ACCESS_KEY_ID = 'id1';
process.env.AWS_SECRET_ACCESS_KEY = 'secret1';
const first = getRoute53Client();
const second = getRoute53Client();
expect(second).toBe(first);
});
it('returns a different instance after resetRoute53Client()', () => {
process.env.AWS_ACCESS_KEY_ID = 'id1';
process.env.AWS_SECRET_ACCESS_KEY = 'secret1';
const first = getRoute53Client();
resetRoute53Client();
const second = getRoute53Client();
expect(second).not.toBe(first);
});
});

View file

@ -0,0 +1,47 @@
import { Route53Client } from '@aws-sdk/client-route-53';
let route53ClientInstance: Route53Client | null = null;
/**
* Check if AWS Route 53 credentials are configured.
*/
export function isRoute53Configured(): boolean {
return !!(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY);
}
/**
* Get or create the Route 53 client singleton instance.
*
* Intentionally does NOT pass an explicit `credentials` option omitting it
* lets @aws-sdk/credential-provider-node's default credential chain read
* AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN from
* process.env automatically (fromEnv() is first in the chain). This is
* exactly how Bitwarden Secrets Manager (`bws run`) injects credentials at
* the docker-entrypoint.sh layer. Do NOT "fix" this by adding an explicit
* credentials object see 24-RESEARCH.md Pitfall 1.
*/
export function getRoute53Client(): Route53Client {
if (!route53ClientInstance) {
if (!isRoute53Configured()) {
throw new Error(
'AWS credentials missing. Please set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (and AWS_REGION) environment variables.'
);
}
// Route 53 is a global service but the SDK still requires a signing
// region; us-east-1 is the conventional default AWS's own CLI/console use.
route53ClientInstance = new Route53Client({
region: process.env.AWS_REGION || 'us-east-1',
});
console.log('Route 53 client initialized');
}
return route53ClientInstance;
}
/**
* Reset the singleton instance (useful for testing).
*/
export function resetRoute53Client(): void {
route53ClientInstance = null;
}

100
lib/types/route53.ts Normal file
View file

@ -0,0 +1,100 @@
/**
* AWS Route 53 DNS sync shared type definitions.
* API-response shapes (camelCase) route handlers transform snake_case
* Postgres rows into these manually (no ORM, per CLAUDE.md).
*/
// ============================================================================
// Zone / Record mirror types
// ============================================================================
export interface Route53Zone {
id: string;
name: string;
comment: string | null;
privateZone: boolean;
recordCount: number;
authoritativeNameServers: string[] | null;
syncedAt: string;
isDeleted: boolean;
}
export interface Route53RecordValue {
value: string;
}
export interface Route53Record {
recordKey: string;
zoneId: string;
name: string;
type: string;
setIdentifier: string | null;
ttl: number | null;
resourceRecords: Route53RecordValue[] | null;
aliasTarget: Record<string, unknown> | null;
syncedAt: string;
isDeleted: boolean;
}
// ============================================================================
// Change ledger / audit log types
// ============================================================================
export type Route53HistorySource = 'pulse_crud' | 'sync_detected_drift';
export interface Route53RecordHistory {
id: string;
zoneId: string;
recordKey: string;
recordName: string;
recordType: string;
changeAction: 'create' | 'update' | 'delete';
beforeValue: Record<string, unknown> | null;
afterValue: Record<string, unknown> | null;
source: Route53HistorySource;
changedByUserId: string | null;
changedByEmail: string | null;
changedAt: string;
}
export type Route53AuditStatus = 'pending' | 'committed' | 'failed';
export interface Route53AuditLog {
id: string;
operation: 'create' | 'update' | 'delete' | 'sync';
zoneId: string | null;
recordKey: string | null;
recordName: string | null;
recordType: string | null;
beforeValue: Record<string, unknown> | null;
afterValue: Record<string, unknown> | null;
performedByUserId: string | null;
performedByEmail: string | null;
performedAt: string;
completedAt: string | null;
status: Route53AuditStatus;
awsChangeId: string | null;
awsChangeStatus: string | null;
errorMessage: string | null;
}
// ============================================================================
// Writable record types (D-01)
// ============================================================================
export type Route53WritableType = 'A' | 'AAAA' | 'CNAME' | 'MX' | 'TXT' | 'SRV';
// ============================================================================
// Sync result
// ============================================================================
export interface Route53SyncResult {
syncId: string;
syncType: string;
status: string;
startedAt: string;
completedAt: string | null;
duration: number | null;
entities: Record<string, unknown>;
errors: string[];
}

View file

@ -0,0 +1,149 @@
-- AWS Route 53 DNS sync — Phase 24 dedicated Postgres schema.
--
-- Lays down the full Route 53 schema this phase's downstream plans (sync
-- service, CRUD routes, health check, admin UI) will populate and consume.
--
-- • Hosted zones (mirror of AWS Route 53 zones) -> route53_zones
-- • Resource record sets (mirror of AWS records) -> route53_records
-- • Append-only change ledger (drift + CRUD) -> route53_record_history
-- • Append-only attempt audit log (D-03/D-07) -> route53_audit_log
--
-- Retention is unbounded by design (D-08) — no purge job, no TTL, and no
-- DELETE statement against route53_record_history or route53_audit_log
-- anywhere in this phase, matching existing Pulse convention (itglue_writes,
-- itglue_asset_audits, phishing audit_events are all forever-retained too).
-- ---------------------------------------------------------------------------
-- 1. route53_zones — mirror of AWS Route 53 hosted zones
-- ---------------------------------------------------------------------------
-- id is the AWS hosted zone id with the '/hostedzone/' prefix stripped — the
-- natural stable identifier Route 53 already provides, no synthetic UUID
-- needed. authoritative_name_servers backs the D-12 NS-delegation health
-- check planned in 24-04.
CREATE TABLE IF NOT EXISTS route53_zones (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
comment TEXT,
private_zone BOOLEAN NOT NULL DEFAULT false,
record_count INTEGER NOT NULL DEFAULT 0,
authoritative_name_servers JSONB,
raw_payload JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
synced_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
is_deleted BOOLEAN NOT NULL DEFAULT false,
deleted_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_route53_zones_is_deleted ON route53_zones(is_deleted);
CREATE INDEX IF NOT EXISTS idx_route53_zones_name ON route53_zones(name);
-- ---------------------------------------------------------------------------
-- 2. route53_records — mirror of AWS Route 53 resource record sets
-- ---------------------------------------------------------------------------
-- record_key is the composite string `zoneId:name:type:setIdentifier` (empty
-- string when there is no set identifier) — Route 53 recordsets are uniquely
-- identified by zone+name+type+SetIdentifier; there is no AWS-side record id
-- to use as a natural primary key.
CREATE TABLE IF NOT EXISTS route53_records (
record_key TEXT PRIMARY KEY,
zone_id TEXT NOT NULL REFERENCES route53_zones(id) ON DELETE CASCADE,
name TEXT NOT NULL,
type TEXT NOT NULL,
set_identifier TEXT,
ttl INTEGER,
resource_records JSONB,
alias_target JSONB,
raw_payload JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
synced_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
is_deleted BOOLEAN NOT NULL DEFAULT false,
deleted_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_route53_records_zone ON route53_records(zone_id);
CREATE INDEX IF NOT EXISTS idx_route53_records_is_deleted ON route53_records(is_deleted);
CREATE INDEX IF NOT EXISTS idx_route53_records_name_type ON route53_records(zone_id, name, type);
-- ---------------------------------------------------------------------------
-- 3. route53_record_history — append-only change ledger (D-06)
-- ---------------------------------------------------------------------------
-- Written by BOTH the sync service (source='sync_detected_drift', when a
-- previously-synced record's live AWS value no longer matches Postgres) and
-- the CRUD routes (source='pulse_crud', on every successful write). Distinct
-- from route53_audit_log below: this table is a per-record timeline of
-- resolved changes; the audit log below is a per-attempt ledger including
-- failures. audit_log_id is a soft (non-FK) reference so a history row
-- survives independent of the audit log's own lifecycle.
CREATE TABLE IF NOT EXISTS route53_record_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
zone_id TEXT NOT NULL REFERENCES route53_zones(id) ON DELETE CASCADE,
record_key TEXT NOT NULL,
record_name TEXT NOT NULL,
record_type TEXT NOT NULL,
change_action TEXT NOT NULL CHECK (change_action IN ('create','update','delete')),
before_value JSONB,
after_value JSONB,
source TEXT NOT NULL CHECK (source IN ('pulse_crud','sync_detected_drift')),
changed_by_user_id TEXT REFERENCES "user"(id) ON DELETE SET NULL,
changed_by_email TEXT,
audit_log_id UUID, -- soft ref -> route53_audit_log(id), no hard FK
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_route53_record_history_record ON route53_record_history(record_key, changed_at DESC);
CREATE INDEX IF NOT EXISTS idx_route53_record_history_source ON route53_record_history(source);
CREATE INDEX IF NOT EXISTS idx_route53_record_history_zone ON route53_record_history(zone_id, changed_at DESC);
-- ---------------------------------------------------------------------------
-- 4. route53_audit_log — append-only attempt audit log (D-03/D-07)
-- ---------------------------------------------------------------------------
-- One row per attempted CRUD/sync operation, including failures. zone_id is
-- deliberately NOT an FK here (unlike route53_record_history above) — a
-- failed attempt against a zone that was never synced must still be
-- recordable. D-03 accepted tradeoff: destructive record operations execute
-- immediately with no staged approval gate; this table is the compensating
-- post-hoc traceability control (actor, timestamp, before/after for every
-- attempt), not a pre-write block.
CREATE TABLE IF NOT EXISTS route53_audit_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
operation TEXT NOT NULL CHECK (operation IN ('create','update','delete','sync')),
zone_id TEXT,
record_key TEXT,
record_name TEXT,
record_type TEXT,
before_value JSONB,
after_value JSONB,
performed_by_user_id TEXT REFERENCES "user"(id) ON DELETE SET NULL,
performed_by_email TEXT,
performed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
completed_at TIMESTAMPTZ,
status TEXT NOT NULL CHECK (status IN ('pending','committed','failed')),
aws_change_id TEXT,
aws_change_status TEXT,
aws_response JSONB,
error_message TEXT
);
CREATE INDEX IF NOT EXISTS ix_route53_audit_log_record ON route53_audit_log(zone_id, record_key, performed_at DESC);
CREATE INDEX IF NOT EXISTS ix_route53_audit_log_status ON route53_audit_log(status);
COMMENT ON TABLE route53_record_history IS
'Append-only per-record change timeline. source distinguishes CRUD writes (pulse_crud) from sync-detected drift (sync_detected_drift). Unbounded retention by design (D-08) — no purge job.';
COMMENT ON TABLE route53_audit_log IS
'Append-only per-attempt audit log including failures. D-03 accepted tradeoff: destructive record ops execute immediately with no pre-write approval gate; this table is the post-hoc compensating control. Unbounded retention by design (D-08) — no purge job.';
-- ---------------------------------------------------------------------------
-- 5. integration_settings seed row (D-10)
-- ---------------------------------------------------------------------------
-- Display-only toggle on /admin/integrations — no sync/CRUD-blocking
-- behavior anywhere in this phase (unlike PAX8's disable-gates-fullSync
-- exception). Extends the existing seed list from migrations/081 rather
-- than editing that committed file.
INSERT INTO integration_settings (key, disabled) VALUES ('route53', false) ON CONFLICT (key) DO NOTHING;

423
package-lock.json generated
View file

@ -9,6 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@anthropic-ai/sdk": "^0.91.1",
"@aws-sdk/client-route-53": "^3.1104.0",
"@better-auth/cli": "^1.4.10",
"@hookform/resolvers": "^5.2.2",
"@radix-ui/react-accordion": "^1.2.12",
@ -246,6 +247,279 @@
"node": ">=14.0.0"
}
},
"node_modules/@aws-sdk/client-route-53": {
"version": "3.1104.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-route-53/-/client-route-53-3.1104.0.tgz",
"integrity": "sha512-19Wzq64FbYtZKcN/AoXwq4Nht+HwxTIVNI5QbUzLZ8Dyld0da6xdNIOBxX/0bGu5bnijj4dxJjokvLRoTpOpOA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/credential-provider-node": "^3.972.78",
"@aws-sdk/middleware-sdk-route53": "^3.972.23",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/fetch-http-handler": "^5.6.13",
"@smithy/node-http-handler": "^4.9.13",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/core": {
"version": "3.977.6",
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.6.tgz",
"integrity": "sha512-QiaJV4/zDrB4ZY2mfeSXSzSTc36W16sZXcGz+SPFk0CJ26gziO0cS+4LjJUMAbdeeBOvS0k0Aq1cZpfGdUXxSw==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "^3.974.2",
"@aws-sdk/xml-builder": "^3.972.37",
"@aws/lambda-invoke-store": "^0.3.0",
"@smithy/core": "^3.31.1",
"@smithy/signature-v4": "^5.6.12",
"@smithy/types": "^4.16.1",
"bowser": "^2.11.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-env": {
"version": "3.972.67",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.67.tgz",
"integrity": "sha512-rcIpk5kxUqDaaNa6Xk23pQ6ViY7jlqzmfFWCahQcBT97ddXaXYYwzCen9Tz1Jvo6aJft6wDl5bN44/Jw5B4oLA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-http": {
"version": "3.972.69",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.69.tgz",
"integrity": "sha512-nggwJtZ4eeNsUw5IeWBMXsi1ryct5idi0K+/SCRF3kybLubOMaNTb3XCihXpWMiVpyzyPeIrl0zTkzhBH9porA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/fetch-http-handler": "^5.6.13",
"@smithy/node-http-handler": "^4.9.13",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-ini": {
"version": "3.973.12",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.12.tgz",
"integrity": "sha512-pNEf/OeyN5X3VmLKlgSO6TqaWmW10CvI3TfwL1XhsuhYjSLT2VDaxFnCPHnOeQXSaFisMX4jNhpETriqN8DOmg==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/credential-provider-env": "^3.972.67",
"@aws-sdk/credential-provider-http": "^3.972.69",
"@aws-sdk/credential-provider-login": "^3.972.74",
"@aws-sdk/credential-provider-process": "^3.972.67",
"@aws-sdk/credential-provider-sso": "^3.973.11",
"@aws-sdk/credential-provider-web-identity": "^3.972.73",
"@aws-sdk/nested-clients": "^3.997.41",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/credential-provider-imds": "^4.4.16",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-login": {
"version": "3.972.74",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.74.tgz",
"integrity": "sha512-0AQfDcf99TNmqVKv0owHrw/TQs6i4ZE5t9qmz6NvO53bE/sA/tpXhXL9AAcEP1qHc6Zzjd1UMb69+/9zdhvY3g==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/nested-clients": "^3.997.41",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-node": {
"version": "3.972.78",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.78.tgz",
"integrity": "sha512-OgPAnfvbGAMWac6yvxJ1ihslrvDpPVwR68D2csospdNCCyPvHk9JLzYKwz48SNiS1T2znDwHauywRKRFfpyYng==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/credential-provider-env": "^3.972.67",
"@aws-sdk/credential-provider-http": "^3.972.69",
"@aws-sdk/credential-provider-ini": "^3.973.12",
"@aws-sdk/credential-provider-process": "^3.972.67",
"@aws-sdk/credential-provider-sso": "^3.973.11",
"@aws-sdk/credential-provider-web-identity": "^3.972.73",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/credential-provider-imds": "^4.4.16",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-process": {
"version": "3.972.67",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.67.tgz",
"integrity": "sha512-IlUEejorGTWKb4/Dm7K5Yw4QxUmXLThLhrvBmzVBqZFTbW72cv9LTcITmo1dsnYriALE4h68mOq4LB99x6sQ7Q==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-sso": {
"version": "3.973.11",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.11.tgz",
"integrity": "sha512-gAQBkBZxUB84d71+pPcI9L+jh2ujhuAVxc/4FgGiWFDjkPBlMKxzd5XDtkSXTFX8Ro7ansnT88+XadasxMeCRw==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/nested-clients": "^3.997.41",
"@aws-sdk/token-providers": "3.1103.0",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-web-identity": {
"version": "3.972.73",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.73.tgz",
"integrity": "sha512-SnlEmQa6SjOgs6iOPLUQl1Eyq4AKiAdPQlkOhFhqNfDtDCwibMGvL6QlkSmf3o6vAUSImzdPCxowT5dfQUZP1A==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/nested-clients": "^3.997.41",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/nested-clients": {
"version": "3.997.41",
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.41.tgz",
"integrity": "sha512-RDHqPGQWlF6tatA/Tp3rg6oIwtgN9IVderxE+9av2Y93Dfyu+mO1hZ5Bu2jpfZg2rwdNbsssnwM+sLafIczMlQ==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/signature-v4-multi-region": "^3.996.43",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/fetch-http-handler": "^5.6.13",
"@smithy/node-http-handler": "^4.9.13",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/signature-v4-multi-region": {
"version": "3.996.43",
"resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.43.tgz",
"integrity": "sha512-lKekx8bLBXSv4O+cslk9Zfnw2XKSkWBs3uWL5QGhH2ZAQfNS7FE0vcSSN2vD/AhxX54ZTywWxR4STThoeOXlBA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "^3.974.2",
"@smithy/signature-v4": "^5.6.12",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/token-providers": {
"version": "3.1103.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1103.0.tgz",
"integrity": "sha512-N4wy26MNn31ItGVHYHPrEuCIFY4MBBjC+C5v1lJKqIUSA7OZBdhleCY53zCCrXn27hsk7YNOaTuhQu807S4AfQ==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "^3.977.6",
"@aws-sdk/nested-clients": "^3.997.41",
"@aws-sdk/types": "^3.974.2",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/types": {
"version": "3.974.2",
"resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.2.tgz",
"integrity": "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/xml-builder": {
"version": "3.972.37",
"resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.37.tgz",
"integrity": "sha512-zKq4HQum8JwDyEuyfuI4bbiAcU0KxP6qy+9PR/IsR92IyE/DaBAikzAS50tjxip4bqIIANpCcG+Yyj6CVhXupg==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-route-53/node_modules/@aws/lambda-invoke-store": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz",
"integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-sesv2": {
"version": "3.943.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.943.0.tgz",
@ -587,6 +861,33 @@
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/middleware-sdk-route53": {
"version": "3.972.23",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-route53/-/middleware-sdk-route53-3.972.23.tgz",
"integrity": "sha512-p2mbkuh45ZLjaDPiQVHP679Rpj7hcK7zUAy4SBe91xur8jG43OZOyX0M9ScLP5BcqWksX87V+ucyvq8YJ/gIHQ==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "^3.974.2",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/middleware-sdk-route53/node_modules/@aws-sdk/types": {
"version": "3.974.2",
"resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.2.tgz",
"integrity": "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/middleware-sdk-s3": {
"version": "3.943.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.943.0.tgz",
@ -5370,20 +5671,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@smithy/abort-controller": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz",
"integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@smithy/config-resolver": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz",
@ -5403,21 +5690,12 @@
}
},
"node_modules/@smithy/core": {
"version": "3.18.7",
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.7.tgz",
"integrity": "sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw==",
"dev": true,
"version": "3.31.1",
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.31.1.tgz",
"integrity": "sha512-CyogUINxvi7C7LDsh8Syo6hVJOT9ckz4rG8dRZfTJ8r91HkMY59PnNooaj7WcHyxEkxPfBAmbgztZU+xTo76lg==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/middleware-serde": "^4.2.6",
"@smithy/protocol-http": "^5.3.5",
"@smithy/types": "^4.9.0",
"@smithy/util-base64": "^4.3.0",
"@smithy/util-body-length-browser": "^4.2.0",
"@smithy/util-middleware": "^4.2.5",
"@smithy/util-stream": "^4.5.6",
"@smithy/util-utf8": "^4.2.0",
"@smithy/uuid": "^1.1.0",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
@ -5425,16 +5703,13 @@
}
},
"node_modules/@smithy/credential-provider-imds": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz",
"integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==",
"dev": true,
"version": "4.4.16",
"resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.16.tgz",
"integrity": "sha512-QfuLWAkLzptffFW980AFeHZFdqds2B64rpEd3uJ6lgs3xVn9QegGMUgUcj+4d7dRrAsya3r58ZKpku97WcFb4w==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/node-config-provider": "^4.3.5",
"@smithy/property-provider": "^4.2.5",
"@smithy/types": "^4.9.0",
"@smithy/url-parser": "^4.2.5",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
@ -5442,16 +5717,13 @@
}
},
"node_modules/@smithy/fetch-http-handler": {
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz",
"integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==",
"dev": true,
"version": "5.6.13",
"resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.13.tgz",
"integrity": "sha512-4fW86pEUOMbrD5nkbyl/tTvPHHWJFbuB2odl6ps9lWfHoXf9HWh3Q/Smh59qH1g7+c/BSZghX6bbUk4gsiMs8A==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/protocol-http": "^5.3.5",
"@smithy/querystring-builder": "^4.2.5",
"@smithy/types": "^4.9.0",
"@smithy/util-base64": "^4.3.0",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
@ -5603,16 +5875,13 @@
}
},
"node_modules/@smithy/node-http-handler": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz",
"integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==",
"dev": true,
"version": "4.9.13",
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.13.tgz",
"integrity": "sha512-Nmd/Nl35zfYrd+a6OO2cDJb3GPh9bgTjIUhcM+JFfjpp8/osCgboDV5nCT1I01Pv6R13eSKDKLSoVa5ZB6Zsfw==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/abort-controller": "^4.2.5",
"@smithy/protocol-http": "^5.3.5",
"@smithy/querystring-builder": "^4.2.5",
"@smithy/types": "^4.9.0",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
@ -5647,21 +5916,6 @@
"node": ">=18.0.0"
}
},
"node_modules/@smithy/querystring-builder": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz",
"integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@smithy/types": "^4.9.0",
"@smithy/util-uri-escape": "^4.2.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@smithy/querystring-parser": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz",
@ -5704,19 +5958,13 @@
}
},
"node_modules/@smithy/signature-v4": {
"version": "5.3.5",
"resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz",
"integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==",
"dev": true,
"version": "5.6.12",
"resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.6.12.tgz",
"integrity": "sha512-I6KLtq3H0qqSuV9vLglfi8puHqzygzWHOnI4z/Rdoo+q50vvo18vBRdPAvvEtcaKROz7Zn6qnPa14kRfPH6PcQ==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/is-array-buffer": "^4.2.0",
"@smithy/protocol-http": "^5.3.5",
"@smithy/types": "^4.9.0",
"@smithy/util-hex-encoding": "^4.2.0",
"@smithy/util-middleware": "^4.2.5",
"@smithy/util-uri-escape": "^4.2.0",
"@smithy/util-utf8": "^4.2.0",
"@smithy/core": "^3.31.1",
"@smithy/types": "^4.16.1",
"tslib": "^2.6.2"
},
"engines": {
@ -5743,10 +5991,9 @@
}
},
"node_modules/@smithy/types": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz",
"integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==",
"dev": true,
"version": "4.16.1",
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.16.1.tgz",
"integrity": "sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.6.2"
@ -5950,19 +6197,6 @@
"node": ">=18.0.0"
}
},
"node_modules/@smithy/util-uri-escape": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz",
"integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@smithy/util-utf8": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz",
@ -8016,7 +8250,6 @@
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz",
"integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==",
"dev": true,
"license": "MIT"
},
"node_modules/brace-expansion": {

View file

@ -12,6 +12,7 @@
},
"dependencies": {
"@anthropic-ai/sdk": "^0.91.1",
"@aws-sdk/client-route-53": "^3.1104.0",
"@better-auth/cli": "^1.4.10",
"@hookform/resolvers": "^5.2.2",
"@radix-ui/react-accordion": "^1.2.12",