@agent-native/core 0.77.8 → 0.77.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +29 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/task-store.ts +25 -3
- package/corpus/core/src/agent/harness/store.ts +89 -55
- package/corpus/core/src/agent/observational-memory/store.ts +40 -14
- package/corpus/core/src/agent/run-store.ts +116 -59
- package/corpus/core/src/application-state/store.ts +36 -2
- package/corpus/core/src/audit/store.ts +37 -6
- package/corpus/core/src/browser-sessions/store.ts +31 -12
- package/corpus/core/src/chat-threads/store.ts +67 -3
- package/corpus/core/src/checkpoints/store.ts +14 -3
- package/corpus/core/src/cli/create.ts +9 -0
- package/corpus/core/src/collab/storage.ts +20 -2
- package/corpus/core/src/db/ddl-guard.ts +112 -0
- package/corpus/core/src/extensions/store.ts +76 -3
- package/corpus/core/src/integrations/a2a-continuations-store.ts +69 -26
- package/corpus/core/src/integrations/config-store.ts +19 -18
- package/corpus/core/src/integrations/pending-tasks-store.ts +40 -16
- package/corpus/core/src/integrations/remote-commands-store.ts +37 -23
- package/corpus/core/src/integrations/remote-devices-store.ts +64 -20
- package/corpus/core/src/integrations/remote-push-store.ts +75 -35
- package/corpus/core/src/integrations/remote-run-events-store.ts +38 -12
- package/corpus/core/src/integrations/thread-mapping-store.ts +11 -2
- package/corpus/core/src/mcp/connect-store.ts +53 -16
- package/corpus/core/src/mcp/oauth-store.ts +32 -7
- package/corpus/core/src/notifications/store.ts +20 -4
- package/corpus/core/src/oauth-tokens/store.ts +45 -2
- package/corpus/core/src/observability/store.ts +154 -47
- package/corpus/core/src/progress/store.ts +36 -21
- package/corpus/core/src/provider-api/corpus-jobs-store.ts +25 -5
- package/corpus/core/src/provider-api/custom-registry.ts +15 -4
- package/corpus/core/src/provider-api/quota-governor.ts +15 -3
- package/corpus/core/src/provider-api/staged-datasets-store.ts +22 -5
- package/corpus/core/src/resources/store.ts +66 -31
- package/corpus/core/src/secrets/storage.ts +18 -22
- package/corpus/core/src/server/agent-teams-run-queue.ts +22 -10
- package/corpus/core/src/server/auth.ts +22 -4
- package/corpus/core/src/server/better-auth-instance.ts +49 -15
- package/corpus/core/src/server/embed-session.ts +19 -4
- package/corpus/core/src/server/identity-sso-store.ts +40 -11
- package/corpus/core/src/server/recap-image-store.ts +28 -9
- package/corpus/core/src/settings/store.ts +14 -19
- package/corpus/core/src/usage/store.ts +51 -16
- package/corpus/core/src/workspace-connections/store.ts +200 -22
- package/corpus/templates/calendar/changelog/2026-06-25-booking-link-previews-now-show-the-content-directly-on-the-g.md +6 -0
- package/corpus/templates/calendar/server/lib/booking-og-image.ts +0 -2
- package/corpus/templates/forms/changelog/2026-06-25-form-previews-now-show-the-content-directly-on-the-grid-back.md +6 -0
- package/corpus/templates/forms/server/lib/form-og-image.ts +0 -2
- package/corpus/templates/plan/app/global.css +6 -11
- package/corpus/templates/plan/app/pages/PlansPage.tsx +12 -10
- package/corpus/templates/plan/changelog/2026-06-25-plan-access-request-pages-stay-visible-during-background-ref.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-25-plan-documents-keep-their-full-reading-width-below-1200px-an.md +6 -0
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +19 -3
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/agent/harness/store.d.ts.map +1 -1
- package/dist/agent/harness/store.js +63 -34
- package/dist/agent/harness/store.js.map +1 -1
- package/dist/agent/observational-memory/store.d.ts.map +1 -1
- package/dist/agent/observational-memory/store.js +30 -8
- package/dist/agent/observational-memory/store.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +103 -46
- package/dist/agent/run-store.js.map +1 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +28 -2
- package/dist/application-state/store.js.map +1 -1
- package/dist/audit/store.d.ts.map +1 -1
- package/dist/audit/store.js +18 -3
- package/dist/audit/store.js.map +1 -1
- package/dist/browser-sessions/store.d.ts.map +1 -1
- package/dist/browser-sessions/store.js +22 -7
- package/dist/browser-sessions/store.js.map +1 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +51 -3
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/checkpoints/store.d.ts.map +1 -1
- package/dist/checkpoints/store.js +12 -3
- package/dist/checkpoints/store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +9 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/collab/storage.d.ts.map +1 -1
- package/dist/collab/storage.js +14 -2
- package/dist/collab/storage.js.map +1 -1
- package/dist/db/ddl-guard.d.ts +63 -0
- package/dist/db/ddl-guard.d.ts.map +1 -1
- package/dist/db/ddl-guard.js +85 -0
- package/dist/db/ddl-guard.js.map +1 -1
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +32 -3
- package/dist/extensions/store.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +44 -24
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/config-store.d.ts.map +1 -1
- package/dist/integrations/config-store.js +17 -11
- package/dist/integrations/config-store.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +24 -16
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/remote-commands-store.d.ts.map +1 -1
- package/dist/integrations/remote-commands-store.js +29 -21
- package/dist/integrations/remote-commands-store.js.map +1 -1
- package/dist/integrations/remote-devices-store.d.ts.map +1 -1
- package/dist/integrations/remote-devices-store.js +33 -18
- package/dist/integrations/remote-devices-store.js.map +1 -1
- package/dist/integrations/remote-push-store.d.ts.map +1 -1
- package/dist/integrations/remote-push-store.js +53 -31
- package/dist/integrations/remote-push-store.js.map +1 -1
- package/dist/integrations/remote-run-events-store.d.ts.map +1 -1
- package/dist/integrations/remote-run-events-store.js +26 -10
- package/dist/integrations/remote-run-events-store.js.map +1 -1
- package/dist/integrations/thread-mapping-store.d.ts.map +1 -1
- package/dist/integrations/thread-mapping-store.js +10 -2
- package/dist/integrations/thread-mapping-store.js.map +1 -1
- package/dist/mcp/connect-store.d.ts.map +1 -1
- package/dist/mcp/connect-store.js +34 -16
- package/dist/mcp/connect-store.js.map +1 -1
- package/dist/mcp/oauth-store.d.ts.map +1 -1
- package/dist/mcp/oauth-store.js +22 -7
- package/dist/mcp/oauth-store.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/notifications/store.d.ts.map +1 -1
- package/dist/notifications/store.js +15 -3
- package/dist/notifications/store.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +33 -2
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/observability/store.d.ts.map +1 -1
- package/dist/observability/store.js +72 -29
- package/dist/observability/store.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/progress/store.d.ts.map +1 -1
- package/dist/progress/store.js +31 -16
- package/dist/progress/store.js.map +1 -1
- package/dist/provider-api/corpus-jobs-store.d.ts.map +1 -1
- package/dist/provider-api/corpus-jobs-store.js +16 -5
- package/dist/provider-api/corpus-jobs-store.js.map +1 -1
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js +14 -4
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/provider-api/quota-governor.d.ts.map +1 -1
- package/dist/provider-api/quota-governor.js +12 -3
- package/dist/provider-api/quota-governor.js.map +1 -1
- package/dist/provider-api/staged-datasets-store.d.ts.map +1 -1
- package/dist/provider-api/staged-datasets-store.js +16 -5
- package/dist/provider-api/staged-datasets-store.js.map +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +56 -25
- package/dist/resources/store.js.map +1 -1
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +10 -14
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-teams-run-queue.d.ts.map +1 -1
- package/dist/server/agent-teams-run-queue.js +15 -4
- package/dist/server/agent-teams-run-queue.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +16 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +55 -21
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +15 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/server/identity-sso-store.d.ts.map +1 -1
- package/dist/server/identity-sso-store.js +30 -11
- package/dist/server/identity-sso-store.js.map +1 -1
- package/dist/server/recap-image-store.d.ts.map +1 -1
- package/dist/server/recap-image-store.js +21 -7
- package/dist/server/recap-image-store.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +11 -13
- package/dist/settings/store.js.map +1 -1
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +36 -11
- package/dist/usage/store.js.map +1 -1
- package/dist/workspace-connections/store.d.ts.map +1 -1
- package/dist/workspace-connections/store.js +57 -7
- package/dist/workspace-connections/store.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"corpus-jobs-store.js","sourceRoot":"","sources":["../../src/provider-api/corpus-jobs-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Durable state for provider corpus jobs.\n *\n * Jobs are scoped by (app_id, owner_email), matching staged datasets. The\n * runner stores only request configuration, checkpoints, and compact search\n * hits so large provider corpora never enter chat context or filesystem\n * scratch files.\n */\n\nimport { getDbExec, intType, isPostgres, type DbExec } from \"../db/client.js\";\n\nexport type ProviderCorpusJobStatus =\n | \"running\"\n | \"paused\"\n | \"quota_wait\"\n | \"completed\"\n | \"failed\";\n\nexport interface ProviderCorpusJobRecord {\n id: string;\n appId: string;\n ownerEmail: string;\n name: string;\n mode: string;\n status: ProviderCorpusJobStatus;\n provider: string;\n request: Record<string, unknown>;\n pagination: Record<string, unknown> | null;\n batch: Record<string, unknown> | null;\n search: Record<string, unknown>;\n limits: Record<string, unknown>;\n checkpoint: Record<string, unknown>;\n pagesProcessed: number;\n batchesProcessed: number;\n itemsProcessed: number;\n matchedItems: number;\n totalHits: number;\n storedHits: number;\n error: string | null;\n nextResumeAt: number | null;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface CreateProviderCorpusJobOptions {\n id: string;\n appId: string;\n ownerEmail: string;\n name: string;\n mode: string;\n status: ProviderCorpusJobStatus;\n provider: string;\n request: Record<string, unknown>;\n pagination?: Record<string, unknown> | null;\n batch?: Record<string, unknown> | null;\n search: Record<string, unknown>;\n limits: Record<string, unknown>;\n checkpoint: Record<string, unknown>;\n}\n\nexport interface UpdateProviderCorpusJobOptions {\n id: string;\n appId: string;\n ownerEmail: string;\n status?: ProviderCorpusJobStatus;\n checkpoint?: Record<string, unknown>;\n pagesProcessed?: number;\n batchesProcessed?: number;\n itemsProcessed?: number;\n matchedItems?: number;\n totalHits?: number;\n storedHits?: number;\n error?: string | null;\n nextResumeAt?: number | null;\n}\n\nlet initPromise: Promise<void> | undefined;\n\nasync function ensureTables(): Promise<void> {\n if (!initPromise) {\n initPromise = (async () => {\n const db = getDbExec();\n const integerType = intType();\n await db.execute(`\n CREATE TABLE IF NOT EXISTS provider_corpus_jobs (\n id TEXT NOT NULL,\n app_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n name TEXT NOT NULL,\n mode TEXT NOT NULL,\n status TEXT NOT NULL,\n provider TEXT NOT NULL,\n request_json TEXT NOT NULL,\n pagination_json TEXT,\n batch_json TEXT,\n search_json TEXT NOT NULL,\n limits_json TEXT NOT NULL,\n checkpoint_json TEXT NOT NULL,\n pages_processed ${integerType} NOT NULL DEFAULT 0,\n batches_processed ${integerType} NOT NULL DEFAULT 0,\n items_processed ${integerType} NOT NULL DEFAULT 0,\n matched_items ${integerType} NOT NULL DEFAULT 0,\n total_hits ${integerType} NOT NULL DEFAULT 0,\n stored_hits ${integerType} NOT NULL DEFAULT 0,\n error TEXT,\n next_resume_at ${integerType},\n created_at ${integerType} NOT NULL,\n updated_at ${integerType} NOT NULL,\n PRIMARY KEY (id)\n )\n `);\n await db.execute(`\n CREATE TABLE IF NOT EXISTS provider_corpus_job_hits (\n job_id TEXT NOT NULL,\n hit_index ${integerType} NOT NULL,\n hit_data TEXT NOT NULL,\n PRIMARY KEY (job_id, hit_index)\n )\n `);\n if (isPostgres()) {\n await widenPostgresIntegerColumns(db);\n }\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_scope_idx ON provider_corpus_jobs (app_id, owner_email, updated_at)`,\n `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_status_idx ON provider_corpus_jobs (app_id, owner_email, status)`,\n `CREATE INDEX IF NOT EXISTS provider_corpus_job_hits_job_idx ON provider_corpus_job_hits (job_id)`,\n ]) {\n try {\n await db.execute(ddl);\n } catch {\n // Index already exists or the backend rejected best-effort indexing.\n }\n }\n })().catch((err) => {\n initPromise = undefined;\n throw err;\n });\n }\n return initPromise;\n}\n\nasync function widenPostgresIntegerColumns(db: DbExec): Promise<void> {\n const statements = [\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN pages_processed TYPE BIGINT USING pages_processed::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN batches_processed TYPE BIGINT USING batches_processed::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN items_processed TYPE BIGINT USING items_processed::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN matched_items TYPE BIGINT USING matched_items::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN total_hits TYPE BIGINT USING total_hits::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN stored_hits TYPE BIGINT USING stored_hits::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN next_resume_at TYPE BIGINT USING next_resume_at::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN created_at TYPE BIGINT USING created_at::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN updated_at TYPE BIGINT USING updated_at::bigint`,\n `ALTER TABLE provider_corpus_job_hits ALTER COLUMN hit_index TYPE BIGINT USING hit_index::bigint`,\n ];\n for (const sql of statements) {\n try {\n await db.execute(sql);\n } catch {\n // Best-effort compatibility for older deployments.\n }\n }\n}\n\nexport async function createProviderCorpusJob(\n options: CreateProviderCorpusJobOptions,\n): Promise<ProviderCorpusJobRecord> {\n await ensureTables();\n const db = getDbExec();\n const now = Date.now();\n await db.execute({\n sql: isPostgres()\n ? `\n INSERT INTO provider_corpus_jobs\n (id, app_id, owner_email, name, mode, status, provider, request_json, pagination_json, batch_json, search_json, limits_json, checkpoint_json, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n app_id = EXCLUDED.app_id,\n owner_email = EXCLUDED.owner_email,\n name = EXCLUDED.name,\n mode = EXCLUDED.mode,\n status = EXCLUDED.status,\n provider = EXCLUDED.provider,\n request_json = EXCLUDED.request_json,\n pagination_json = EXCLUDED.pagination_json,\n batch_json = EXCLUDED.batch_json,\n search_json = EXCLUDED.search_json,\n limits_json = EXCLUDED.limits_json,\n checkpoint_json = EXCLUDED.checkpoint_json,\n pages_processed = 0,\n batches_processed = 0,\n items_processed = 0,\n matched_items = 0,\n total_hits = 0,\n stored_hits = 0,\n error = NULL,\n next_resume_at = NULL,\n updated_at = EXCLUDED.updated_at\n WHERE provider_corpus_jobs.app_id = EXCLUDED.app_id\n AND provider_corpus_jobs.owner_email = EXCLUDED.owner_email\n `\n : `\n INSERT INTO provider_corpus_jobs\n (id, app_id, owner_email, name, mode, status, provider, request_json, pagination_json, batch_json, search_json, limits_json, checkpoint_json, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n app_id = excluded.app_id,\n owner_email = excluded.owner_email,\n name = excluded.name,\n mode = excluded.mode,\n status = excluded.status,\n provider = excluded.provider,\n request_json = excluded.request_json,\n pagination_json = excluded.pagination_json,\n batch_json = excluded.batch_json,\n search_json = excluded.search_json,\n limits_json = excluded.limits_json,\n checkpoint_json = excluded.checkpoint_json,\n pages_processed = 0,\n batches_processed = 0,\n items_processed = 0,\n matched_items = 0,\n total_hits = 0,\n stored_hits = 0,\n error = NULL,\n next_resume_at = NULL,\n updated_at = excluded.updated_at\n WHERE provider_corpus_jobs.app_id = excluded.app_id\n AND provider_corpus_jobs.owner_email = excluded.owner_email\n `,\n args: [\n options.id,\n options.appId,\n options.ownerEmail,\n options.name,\n options.mode,\n options.status,\n options.provider,\n JSON.stringify(options.request),\n jsonOrNull(options.pagination),\n jsonOrNull(options.batch),\n JSON.stringify(options.search),\n JSON.stringify(options.limits),\n JSON.stringify(options.checkpoint),\n now,\n now,\n ],\n });\n await db.execute({\n sql: `DELETE FROM provider_corpus_job_hits WHERE job_id = ?`,\n args: [options.id],\n });\n const job = await getProviderCorpusJob({\n id: options.id,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n if (!job) {\n // The scoped upsert above only updates a row that already belongs to this\n // (app_id, owner_email). A null read here means a job with this id exists\n // under a different owner, so the conflicting insert was skipped rather\n // than clobbering the other tenant's job.\n throw new Error(\n `Failed to create provider corpus job ${options.id}: a job with this id ` +\n `already exists for a different owner. Use a different jobId.`,\n );\n }\n return job;\n}\n\nexport async function getProviderCorpusJob(options: {\n id: string;\n appId: string;\n ownerEmail: string;\n}): Promise<ProviderCorpusJobRecord | null> {\n await ensureTables();\n const db = getDbExec();\n const { rows } = await db.execute({\n sql: `SELECT * FROM provider_corpus_jobs WHERE id = ? AND app_id = ? AND owner_email = ?`,\n args: [options.id, options.appId, options.ownerEmail],\n });\n return rows[0] ? rowToJob(rows[0]) : null;\n}\n\nexport async function listProviderCorpusJobs(options: {\n appId: string;\n ownerEmail: string;\n limit?: number;\n}): Promise<ProviderCorpusJobRecord[]> {\n await ensureTables();\n const db = getDbExec();\n const limit = Math.max(1, Math.min(options.limit ?? 20, 100));\n const { rows } = await db.execute({\n sql: `SELECT * FROM provider_corpus_jobs WHERE app_id = ? AND owner_email = ? ORDER BY updated_at DESC LIMIT ?`,\n args: [options.appId, options.ownerEmail, limit],\n });\n return rows.map(rowToJob);\n}\n\nexport async function updateProviderCorpusJob(\n options: UpdateProviderCorpusJobOptions,\n): Promise<ProviderCorpusJobRecord | null> {\n await ensureTables();\n const existing = await getProviderCorpusJob({\n id: options.id,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n if (!existing) return null;\n const next = {\n status: options.status ?? existing.status,\n checkpoint: options.checkpoint ?? existing.checkpoint,\n pagesProcessed: options.pagesProcessed ?? existing.pagesProcessed,\n batchesProcessed: options.batchesProcessed ?? existing.batchesProcessed,\n itemsProcessed: options.itemsProcessed ?? existing.itemsProcessed,\n matchedItems: options.matchedItems ?? existing.matchedItems,\n totalHits: options.totalHits ?? existing.totalHits,\n storedHits: options.storedHits ?? existing.storedHits,\n error: options.error === undefined ? existing.error : options.error,\n nextResumeAt:\n options.nextResumeAt === undefined\n ? existing.nextResumeAt\n : options.nextResumeAt,\n };\n const db = getDbExec();\n await db.execute({\n sql: `\n UPDATE provider_corpus_jobs SET\n status = ?,\n checkpoint_json = ?,\n pages_processed = ?,\n batches_processed = ?,\n items_processed = ?,\n matched_items = ?,\n total_hits = ?,\n stored_hits = ?,\n error = ?,\n next_resume_at = ?,\n updated_at = ?\n WHERE id = ? AND app_id = ? AND owner_email = ?\n `,\n args: [\n next.status,\n JSON.stringify(next.checkpoint),\n next.pagesProcessed,\n next.batchesProcessed,\n next.itemsProcessed,\n next.matchedItems,\n next.totalHits,\n next.storedHits,\n next.error,\n next.nextResumeAt,\n Date.now(),\n options.id,\n options.appId,\n options.ownerEmail,\n ],\n });\n return getProviderCorpusJob({\n id: options.id,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n}\n\nexport async function appendProviderCorpusJobHits(options: {\n jobId: string;\n startIndex: number;\n hits: Record<string, unknown>[];\n}): Promise<void> {\n if (options.hits.length === 0) return;\n await ensureTables();\n const db = getDbExec();\n // Insert in chunked multi-row statements rather than one round-trip per hit,\n // and ignore conflicts on (job_id, hit_index). The runner writes hits before\n // it advances the stored-hits counter and checkpoint, so a crash between the\n // two means resume re-fetches the same page and re-appends the same indices;\n // DO NOTHING makes that retry idempotent instead of a primary-key violation.\n const CHUNK = 100;\n for (let start = 0; start < options.hits.length; start += CHUNK) {\n const chunk = options.hits.slice(start, start + CHUNK);\n const placeholders = chunk.map(() => \"(?, ?, ?)\").join(\", \");\n const args: unknown[] = [];\n for (let j = 0; j < chunk.length; j++) {\n args.push(\n options.jobId,\n options.startIndex + start + j,\n JSON.stringify(chunk[j]),\n );\n }\n await db.execute({\n sql: `INSERT INTO provider_corpus_job_hits (job_id, hit_index, hit_data) VALUES ${placeholders} ON CONFLICT (job_id, hit_index) DO NOTHING`,\n args,\n });\n }\n}\n\nexport async function getProviderCorpusJobHits(options: {\n jobId: string;\n appId: string;\n ownerEmail: string;\n offset?: number;\n limit?: number;\n}): Promise<Record<string, unknown>[]> {\n await ensureTables();\n const job = await getProviderCorpusJob({\n id: options.jobId,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n if (!job) return [];\n const offset = Math.max(0, options.offset ?? 0);\n const limit = Math.max(1, Math.min(options.limit ?? 100, 1000));\n const db = getDbExec();\n const { rows } = await db.execute({\n sql: `SELECT hit_data FROM provider_corpus_job_hits WHERE job_id = ? ORDER BY hit_index ASC LIMIT ? OFFSET ?`,\n args: [options.jobId, limit, offset],\n });\n return rows.map((row) => parseJsonRecord(row.hit_data));\n}\n\nexport async function deleteProviderCorpusJob(options: {\n id: string;\n appId: string;\n ownerEmail: string;\n}): Promise<boolean> {\n await ensureTables();\n const existing = await getProviderCorpusJob(options);\n if (!existing) return false;\n const db = getDbExec();\n await db.execute({\n sql: `DELETE FROM provider_corpus_job_hits WHERE job_id = ?`,\n args: [options.id],\n });\n const result = await db.execute({\n sql: `DELETE FROM provider_corpus_jobs WHERE id = ? AND app_id = ? AND owner_email = ?`,\n args: [options.id, options.appId, options.ownerEmail],\n });\n return result.rowsAffected > 0;\n}\n\nfunction jsonOrNull(\n value: Record<string, unknown> | null | undefined,\n): string | null {\n return value ? JSON.stringify(value) : null;\n}\n\nfunction parseJsonRecord(value: unknown): Record<string, unknown> {\n if (typeof value !== \"string\") return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction parseNullableJsonRecord(\n value: unknown,\n): Record<string, unknown> | null {\n if (typeof value !== \"string\" || !value) return null;\n return parseJsonRecord(value);\n}\n\nfunction nullableNumber(value: unknown): number | null {\n if (value === null || value === undefined) return null;\n const number = Number(value);\n return Number.isFinite(number) ? number : null;\n}\n\nfunction rowToJob(row: Record<string, unknown>): ProviderCorpusJobRecord {\n return {\n id: String(row.id),\n appId: String(row.app_id),\n ownerEmail: String(row.owner_email),\n name: String(row.name),\n mode: String(row.mode),\n status: String(row.status) as ProviderCorpusJobStatus,\n provider: String(row.provider),\n request: parseJsonRecord(row.request_json),\n pagination: parseNullableJsonRecord(row.pagination_json),\n batch: parseNullableJsonRecord(row.batch_json),\n search: parseJsonRecord(row.search_json),\n limits: parseJsonRecord(row.limits_json),\n checkpoint: parseJsonRecord(row.checkpoint_json),\n pagesProcessed: Number(row.pages_processed ?? 0),\n batchesProcessed: Number(row.batches_processed ?? 0),\n itemsProcessed: Number(row.items_processed ?? 0),\n matchedItems: Number(row.matched_items ?? 0),\n totalHits: Number(row.total_hits ?? 0),\n storedHits: Number(row.stored_hits ?? 0),\n error: row.error == null ? null : String(row.error),\n nextResumeAt: nullableNumber(row.next_resume_at),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nexport function _resetProviderCorpusJobsStoreForTests(): void {\n initPromise = undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"corpus-jobs-store.js","sourceRoot":"","sources":["../../src/provider-api/corpus-jobs-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Durable state for provider corpus jobs.\n *\n * Jobs are scoped by (app_id, owner_email), matching staged datasets. The\n * runner stores only request configuration, checkpoints, and compact search\n * hits so large provider corpora never enter chat context or filesystem\n * scratch files.\n */\n\nimport { getDbExec, intType, isPostgres, type DbExec } from \"../db/client.js\";\nimport { ensureTableExists, ensureIndexExists } from \"../db/ddl-guard.js\";\n\nexport type ProviderCorpusJobStatus =\n | \"running\"\n | \"paused\"\n | \"quota_wait\"\n | \"completed\"\n | \"failed\";\n\nexport interface ProviderCorpusJobRecord {\n id: string;\n appId: string;\n ownerEmail: string;\n name: string;\n mode: string;\n status: ProviderCorpusJobStatus;\n provider: string;\n request: Record<string, unknown>;\n pagination: Record<string, unknown> | null;\n batch: Record<string, unknown> | null;\n search: Record<string, unknown>;\n limits: Record<string, unknown>;\n checkpoint: Record<string, unknown>;\n pagesProcessed: number;\n batchesProcessed: number;\n itemsProcessed: number;\n matchedItems: number;\n totalHits: number;\n storedHits: number;\n error: string | null;\n nextResumeAt: number | null;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface CreateProviderCorpusJobOptions {\n id: string;\n appId: string;\n ownerEmail: string;\n name: string;\n mode: string;\n status: ProviderCorpusJobStatus;\n provider: string;\n request: Record<string, unknown>;\n pagination?: Record<string, unknown> | null;\n batch?: Record<string, unknown> | null;\n search: Record<string, unknown>;\n limits: Record<string, unknown>;\n checkpoint: Record<string, unknown>;\n}\n\nexport interface UpdateProviderCorpusJobOptions {\n id: string;\n appId: string;\n ownerEmail: string;\n status?: ProviderCorpusJobStatus;\n checkpoint?: Record<string, unknown>;\n pagesProcessed?: number;\n batchesProcessed?: number;\n itemsProcessed?: number;\n matchedItems?: number;\n totalHits?: number;\n storedHits?: number;\n error?: string | null;\n nextResumeAt?: number | null;\n}\n\nlet initPromise: Promise<void> | undefined;\n\nasync function ensureTables(): Promise<void> {\n if (!initPromise) {\n initPromise = (async () => {\n const db = getDbExec();\n const integerType = intType();\n const createJobsSql = `\n CREATE TABLE IF NOT EXISTS provider_corpus_jobs (\n id TEXT NOT NULL,\n app_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n name TEXT NOT NULL,\n mode TEXT NOT NULL,\n status TEXT NOT NULL,\n provider TEXT NOT NULL,\n request_json TEXT NOT NULL,\n pagination_json TEXT,\n batch_json TEXT,\n search_json TEXT NOT NULL,\n limits_json TEXT NOT NULL,\n checkpoint_json TEXT NOT NULL,\n pages_processed ${integerType} NOT NULL DEFAULT 0,\n batches_processed ${integerType} NOT NULL DEFAULT 0,\n items_processed ${integerType} NOT NULL DEFAULT 0,\n matched_items ${integerType} NOT NULL DEFAULT 0,\n total_hits ${integerType} NOT NULL DEFAULT 0,\n stored_hits ${integerType} NOT NULL DEFAULT 0,\n error TEXT,\n next_resume_at ${integerType},\n created_at ${integerType} NOT NULL,\n updated_at ${integerType} NOT NULL,\n PRIMARY KEY (id)\n )\n `;\n const createHitsSql = `\n CREATE TABLE IF NOT EXISTS provider_corpus_job_hits (\n job_id TEXT NOT NULL,\n hit_index ${integerType} NOT NULL,\n hit_data TEXT NOT NULL,\n PRIMARY KEY (job_id, hit_index)\n )\n `;\n if (isPostgres()) {\n // PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout\n await ensureTableExists(\"provider_corpus_jobs\", createJobsSql);\n await ensureTableExists(\"provider_corpus_job_hits\", createHitsSql);\n await widenPostgresIntegerColumns(db); // best-effort type widening — unchanged\n await ensureIndexExists(\n \"provider_corpus_jobs_scope_idx\",\n `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_scope_idx ON provider_corpus_jobs (app_id, owner_email, updated_at)`,\n );\n await ensureIndexExists(\n \"provider_corpus_jobs_status_idx\",\n `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_status_idx ON provider_corpus_jobs (app_id, owner_email, status)`,\n );\n await ensureIndexExists(\n \"provider_corpus_job_hits_job_idx\",\n `CREATE INDEX IF NOT EXISTS provider_corpus_job_hits_job_idx ON provider_corpus_job_hits (job_id)`,\n );\n return;\n }\n // SQLite (local dev): keep existing behavior\n await db.execute(createJobsSql);\n await db.execute(createHitsSql);\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_scope_idx ON provider_corpus_jobs (app_id, owner_email, updated_at)`,\n `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_status_idx ON provider_corpus_jobs (app_id, owner_email, status)`,\n `CREATE INDEX IF NOT EXISTS provider_corpus_job_hits_job_idx ON provider_corpus_job_hits (job_id)`,\n ]) {\n try {\n await db.execute(ddl);\n } catch {\n // Index already exists or the backend rejected best-effort indexing.\n }\n }\n })().catch((err) => {\n initPromise = undefined;\n throw err;\n });\n }\n return initPromise;\n}\n\nasync function widenPostgresIntegerColumns(db: DbExec): Promise<void> {\n const statements = [\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN pages_processed TYPE BIGINT USING pages_processed::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN batches_processed TYPE BIGINT USING batches_processed::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN items_processed TYPE BIGINT USING items_processed::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN matched_items TYPE BIGINT USING matched_items::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN total_hits TYPE BIGINT USING total_hits::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN stored_hits TYPE BIGINT USING stored_hits::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN next_resume_at TYPE BIGINT USING next_resume_at::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN created_at TYPE BIGINT USING created_at::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN updated_at TYPE BIGINT USING updated_at::bigint`,\n `ALTER TABLE provider_corpus_job_hits ALTER COLUMN hit_index TYPE BIGINT USING hit_index::bigint`,\n ];\n for (const sql of statements) {\n try {\n await db.execute(sql);\n } catch {\n // Best-effort compatibility for older deployments.\n }\n }\n}\n\nexport async function createProviderCorpusJob(\n options: CreateProviderCorpusJobOptions,\n): Promise<ProviderCorpusJobRecord> {\n await ensureTables();\n const db = getDbExec();\n const now = Date.now();\n await db.execute({\n sql: isPostgres()\n ? `\n INSERT INTO provider_corpus_jobs\n (id, app_id, owner_email, name, mode, status, provider, request_json, pagination_json, batch_json, search_json, limits_json, checkpoint_json, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n app_id = EXCLUDED.app_id,\n owner_email = EXCLUDED.owner_email,\n name = EXCLUDED.name,\n mode = EXCLUDED.mode,\n status = EXCLUDED.status,\n provider = EXCLUDED.provider,\n request_json = EXCLUDED.request_json,\n pagination_json = EXCLUDED.pagination_json,\n batch_json = EXCLUDED.batch_json,\n search_json = EXCLUDED.search_json,\n limits_json = EXCLUDED.limits_json,\n checkpoint_json = EXCLUDED.checkpoint_json,\n pages_processed = 0,\n batches_processed = 0,\n items_processed = 0,\n matched_items = 0,\n total_hits = 0,\n stored_hits = 0,\n error = NULL,\n next_resume_at = NULL,\n updated_at = EXCLUDED.updated_at\n WHERE provider_corpus_jobs.app_id = EXCLUDED.app_id\n AND provider_corpus_jobs.owner_email = EXCLUDED.owner_email\n `\n : `\n INSERT INTO provider_corpus_jobs\n (id, app_id, owner_email, name, mode, status, provider, request_json, pagination_json, batch_json, search_json, limits_json, checkpoint_json, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n app_id = excluded.app_id,\n owner_email = excluded.owner_email,\n name = excluded.name,\n mode = excluded.mode,\n status = excluded.status,\n provider = excluded.provider,\n request_json = excluded.request_json,\n pagination_json = excluded.pagination_json,\n batch_json = excluded.batch_json,\n search_json = excluded.search_json,\n limits_json = excluded.limits_json,\n checkpoint_json = excluded.checkpoint_json,\n pages_processed = 0,\n batches_processed = 0,\n items_processed = 0,\n matched_items = 0,\n total_hits = 0,\n stored_hits = 0,\n error = NULL,\n next_resume_at = NULL,\n updated_at = excluded.updated_at\n WHERE provider_corpus_jobs.app_id = excluded.app_id\n AND provider_corpus_jobs.owner_email = excluded.owner_email\n `,\n args: [\n options.id,\n options.appId,\n options.ownerEmail,\n options.name,\n options.mode,\n options.status,\n options.provider,\n JSON.stringify(options.request),\n jsonOrNull(options.pagination),\n jsonOrNull(options.batch),\n JSON.stringify(options.search),\n JSON.stringify(options.limits),\n JSON.stringify(options.checkpoint),\n now,\n now,\n ],\n });\n await db.execute({\n sql: `DELETE FROM provider_corpus_job_hits WHERE job_id = ?`,\n args: [options.id],\n });\n const job = await getProviderCorpusJob({\n id: options.id,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n if (!job) {\n // The scoped upsert above only updates a row that already belongs to this\n // (app_id, owner_email). A null read here means a job with this id exists\n // under a different owner, so the conflicting insert was skipped rather\n // than clobbering the other tenant's job.\n throw new Error(\n `Failed to create provider corpus job ${options.id}: a job with this id ` +\n `already exists for a different owner. Use a different jobId.`,\n );\n }\n return job;\n}\n\nexport async function getProviderCorpusJob(options: {\n id: string;\n appId: string;\n ownerEmail: string;\n}): Promise<ProviderCorpusJobRecord | null> {\n await ensureTables();\n const db = getDbExec();\n const { rows } = await db.execute({\n sql: `SELECT * FROM provider_corpus_jobs WHERE id = ? AND app_id = ? AND owner_email = ?`,\n args: [options.id, options.appId, options.ownerEmail],\n });\n return rows[0] ? rowToJob(rows[0]) : null;\n}\n\nexport async function listProviderCorpusJobs(options: {\n appId: string;\n ownerEmail: string;\n limit?: number;\n}): Promise<ProviderCorpusJobRecord[]> {\n await ensureTables();\n const db = getDbExec();\n const limit = Math.max(1, Math.min(options.limit ?? 20, 100));\n const { rows } = await db.execute({\n sql: `SELECT * FROM provider_corpus_jobs WHERE app_id = ? AND owner_email = ? ORDER BY updated_at DESC LIMIT ?`,\n args: [options.appId, options.ownerEmail, limit],\n });\n return rows.map(rowToJob);\n}\n\nexport async function updateProviderCorpusJob(\n options: UpdateProviderCorpusJobOptions,\n): Promise<ProviderCorpusJobRecord | null> {\n await ensureTables();\n const existing = await getProviderCorpusJob({\n id: options.id,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n if (!existing) return null;\n const next = {\n status: options.status ?? existing.status,\n checkpoint: options.checkpoint ?? existing.checkpoint,\n pagesProcessed: options.pagesProcessed ?? existing.pagesProcessed,\n batchesProcessed: options.batchesProcessed ?? existing.batchesProcessed,\n itemsProcessed: options.itemsProcessed ?? existing.itemsProcessed,\n matchedItems: options.matchedItems ?? existing.matchedItems,\n totalHits: options.totalHits ?? existing.totalHits,\n storedHits: options.storedHits ?? existing.storedHits,\n error: options.error === undefined ? existing.error : options.error,\n nextResumeAt:\n options.nextResumeAt === undefined\n ? existing.nextResumeAt\n : options.nextResumeAt,\n };\n const db = getDbExec();\n await db.execute({\n sql: `\n UPDATE provider_corpus_jobs SET\n status = ?,\n checkpoint_json = ?,\n pages_processed = ?,\n batches_processed = ?,\n items_processed = ?,\n matched_items = ?,\n total_hits = ?,\n stored_hits = ?,\n error = ?,\n next_resume_at = ?,\n updated_at = ?\n WHERE id = ? AND app_id = ? AND owner_email = ?\n `,\n args: [\n next.status,\n JSON.stringify(next.checkpoint),\n next.pagesProcessed,\n next.batchesProcessed,\n next.itemsProcessed,\n next.matchedItems,\n next.totalHits,\n next.storedHits,\n next.error,\n next.nextResumeAt,\n Date.now(),\n options.id,\n options.appId,\n options.ownerEmail,\n ],\n });\n return getProviderCorpusJob({\n id: options.id,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n}\n\nexport async function appendProviderCorpusJobHits(options: {\n jobId: string;\n startIndex: number;\n hits: Record<string, unknown>[];\n}): Promise<void> {\n if (options.hits.length === 0) return;\n await ensureTables();\n const db = getDbExec();\n // Insert in chunked multi-row statements rather than one round-trip per hit,\n // and ignore conflicts on (job_id, hit_index). The runner writes hits before\n // it advances the stored-hits counter and checkpoint, so a crash between the\n // two means resume re-fetches the same page and re-appends the same indices;\n // DO NOTHING makes that retry idempotent instead of a primary-key violation.\n const CHUNK = 100;\n for (let start = 0; start < options.hits.length; start += CHUNK) {\n const chunk = options.hits.slice(start, start + CHUNK);\n const placeholders = chunk.map(() => \"(?, ?, ?)\").join(\", \");\n const args: unknown[] = [];\n for (let j = 0; j < chunk.length; j++) {\n args.push(\n options.jobId,\n options.startIndex + start + j,\n JSON.stringify(chunk[j]),\n );\n }\n await db.execute({\n sql: `INSERT INTO provider_corpus_job_hits (job_id, hit_index, hit_data) VALUES ${placeholders} ON CONFLICT (job_id, hit_index) DO NOTHING`,\n args,\n });\n }\n}\n\nexport async function getProviderCorpusJobHits(options: {\n jobId: string;\n appId: string;\n ownerEmail: string;\n offset?: number;\n limit?: number;\n}): Promise<Record<string, unknown>[]> {\n await ensureTables();\n const job = await getProviderCorpusJob({\n id: options.jobId,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n if (!job) return [];\n const offset = Math.max(0, options.offset ?? 0);\n const limit = Math.max(1, Math.min(options.limit ?? 100, 1000));\n const db = getDbExec();\n const { rows } = await db.execute({\n sql: `SELECT hit_data FROM provider_corpus_job_hits WHERE job_id = ? ORDER BY hit_index ASC LIMIT ? OFFSET ?`,\n args: [options.jobId, limit, offset],\n });\n return rows.map((row) => parseJsonRecord(row.hit_data));\n}\n\nexport async function deleteProviderCorpusJob(options: {\n id: string;\n appId: string;\n ownerEmail: string;\n}): Promise<boolean> {\n await ensureTables();\n const existing = await getProviderCorpusJob(options);\n if (!existing) return false;\n const db = getDbExec();\n await db.execute({\n sql: `DELETE FROM provider_corpus_job_hits WHERE job_id = ?`,\n args: [options.id],\n });\n const result = await db.execute({\n sql: `DELETE FROM provider_corpus_jobs WHERE id = ? AND app_id = ? AND owner_email = ?`,\n args: [options.id, options.appId, options.ownerEmail],\n });\n return result.rowsAffected > 0;\n}\n\nfunction jsonOrNull(\n value: Record<string, unknown> | null | undefined,\n): string | null {\n return value ? JSON.stringify(value) : null;\n}\n\nfunction parseJsonRecord(value: unknown): Record<string, unknown> {\n if (typeof value !== \"string\") return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction parseNullableJsonRecord(\n value: unknown,\n): Record<string, unknown> | null {\n if (typeof value !== \"string\" || !value) return null;\n return parseJsonRecord(value);\n}\n\nfunction nullableNumber(value: unknown): number | null {\n if (value === null || value === undefined) return null;\n const number = Number(value);\n return Number.isFinite(number) ? number : null;\n}\n\nfunction rowToJob(row: Record<string, unknown>): ProviderCorpusJobRecord {\n return {\n id: String(row.id),\n appId: String(row.app_id),\n ownerEmail: String(row.owner_email),\n name: String(row.name),\n mode: String(row.mode),\n status: String(row.status) as ProviderCorpusJobStatus,\n provider: String(row.provider),\n request: parseJsonRecord(row.request_json),\n pagination: parseNullableJsonRecord(row.pagination_json),\n batch: parseNullableJsonRecord(row.batch_json),\n search: parseJsonRecord(row.search_json),\n limits: parseJsonRecord(row.limits_json),\n checkpoint: parseJsonRecord(row.checkpoint_json),\n pagesProcessed: Number(row.pages_processed ?? 0),\n batchesProcessed: Number(row.batches_processed ?? 0),\n itemsProcessed: Number(row.items_processed ?? 0),\n matchedItems: Number(row.matched_items ?? 0),\n totalHits: Number(row.total_hits ?? 0),\n storedHits: Number(row.stored_hits ?? 0),\n error: row.error == null ? null : String(row.error),\n nextResumeAt: nullableNumber(row.next_resume_at),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nexport function _resetProviderCorpusJobsStoreForTests(): void {\n initPromise = undefined;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-registry.d.ts","sourceRoot":"","sources":["../../src/provider-api/custom-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"custom-registry.d.ts","sourceRoot":"","sources":["../../src/provider-api/custom-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAWH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,CAAC;AAEjD,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,mBAAmB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,sBAAsB,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,+FAA+F;IAC/F,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,sBAAsB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAgED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAkBxE;AAmFD;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAkBxE;AAMD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,MAAM,CAAC,CAwDjB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAQjC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAStC"}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* cannot be expressed in the simple header/key model.
|
|
21
21
|
*/
|
|
22
22
|
import { getDbExec, isPostgres } from "../db/client.js";
|
|
23
|
+
import { ensureTableExists } from "../db/ddl-guard.js";
|
|
23
24
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
24
25
|
import { isBlockedExtensionUrlWithDns } from "../extensions/url-safety.js";
|
|
25
26
|
// ---------------------------------------------------------------------------
|
|
@@ -45,10 +46,19 @@ async function ensureTable() {
|
|
|
45
46
|
if (!_initPromise) {
|
|
46
47
|
_initPromise = (async () => {
|
|
47
48
|
const client = getDbExec();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
:
|
|
51
|
-
|
|
49
|
+
if (isPostgres()) {
|
|
50
|
+
const pgSql = CREATE_SQL.replace(/\bINTEGER\b/g, "BIGINT");
|
|
51
|
+
// PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout
|
|
52
|
+
await ensureTableExists("custom_api_providers", pgSql);
|
|
53
|
+
// Widen any pre-existing int4 timestamp columns to BIGINT (no-op once done)
|
|
54
|
+
await widenIntColumnsToBigInt("custom_api_providers", [
|
|
55
|
+
"created_at",
|
|
56
|
+
"updated_at",
|
|
57
|
+
]);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// SQLite (local dev): keep existing behavior
|
|
61
|
+
await client.execute(CREATE_SQL);
|
|
52
62
|
// Fresh Postgres tables get BIGINT via the replace above, but tables
|
|
53
63
|
// created before that compat existed kept int4 timestamp columns; widen
|
|
54
64
|
// them so `Date.now()` writes don't overflow. No-op once done.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-registry.js","sourceRoot":"","sources":["../../src/provider-api/custom-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Custom provider registry — runtime storage and resolution.\n *\n * Lets apps register arbitrary HTTP API providers at runtime without touching\n * the static PROVIDER_CONFIGS list. Provider rows live in the\n * `custom_api_providers` SQL table (created on first use). Credentials\n * referenced in the provider row continue to live in the existing secrets /\n * credentials store — this table stores only key NAMES, never values.\n *\n * Scoping mirrors the `app_secrets` table: each row is scoped to a user\n * (by email) or an organisation (by orgId).\n *\n * Supported auth kinds for custom providers:\n * - bearer → Authorization: Bearer <credential>\n * - basic → Authorization: Basic base64(user:pass)\n * - api-key-header → custom header: <credential>\n *\n * google-service-account and oauth-bearer are intentionally unsupported for\n * custom providers because they require additional out-of-band setup that\n * cannot be expressed in the simple header/key model.\n */\n\nimport { getDbExec, isPostgres } from \"../db/client.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport { isBlockedExtensionUrlWithDns } from \"../extensions/url-safety.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CustomProviderScope = \"user\" | \"org\";\n\nexport type CustomProviderAuthKind =\n | { type: \"bearer\"; credentialKey: string }\n | { type: \"basic\"; usernameKey: string; passwordKey: string }\n | { type: \"api-key-header\"; credentialKey: string; headerName: string }\n | { type: \"none\" };\n\nexport interface CustomProviderConfig {\n id: string;\n scope: CustomProviderScope;\n scopeId: string;\n label: string;\n baseUrl: string;\n auth: CustomProviderAuthKind;\n docsUrls: string[];\n allowedHostSuffixes: string[];\n defaultHeaders: Record<string, string>;\n notes: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface UpsertCustomProviderArgs {\n scope: CustomProviderScope;\n scopeId: string;\n /** Slug used as the provider id (e.g. \"my-api\"). Must be lowercase, letters/digits/hyphens. */\n id: string;\n label: string;\n baseUrl: string;\n auth: CustomProviderAuthKind;\n docsUrls?: string[];\n allowedHostSuffixes?: string[];\n defaultHeaders?: Record<string, string>;\n notes?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Table bootstrap\n// ---------------------------------------------------------------------------\n\nconst CREATE_SQL = `CREATE TABLE IF NOT EXISTS custom_api_providers (\n id TEXT NOT NULL,\n scope TEXT NOT NULL,\n scope_id TEXT NOT NULL,\n label TEXT NOT NULL,\n base_url TEXT NOT NULL,\n auth_json TEXT NOT NULL,\n docs_urls_json TEXT NOT NULL,\n allowed_host_suffixes_json TEXT NOT NULL,\n default_headers_json TEXT NOT NULL,\n notes TEXT NOT NULL DEFAULT '',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (scope, scope_id, id)\n)`;\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const sql = isPostgres()\n ? CREATE_SQL.replace(/\\bINTEGER\\b/g, \"BIGINT\")\n : CREATE_SQL;\n await client.execute(sql);\n // Fresh Postgres tables get BIGINT via the replace above, but tables\n // created before that compat existed kept int4 timestamp columns; widen\n // them so `Date.now()` writes don't overflow. No-op once done.\n await widenIntColumnsToBigInt(\"custom_api_providers\", [\n \"created_at\",\n \"updated_at\",\n ]);\n })().catch((err) => {\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nconst PROVIDER_ID_RE = /^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$|^[a-z0-9]$/;\nconst HEADER_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\n/**\n * Validate and normalise a custom provider base URL. Throws when the URL is\n * invalid, uses a non-http(s) scheme, or resolves to a private/internal host.\n */\nexport async function validateCustomBaseUrl(rawUrl: string): Promise<URL> {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new Error(`Invalid base URL: ${rawUrl}`);\n }\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n throw new Error(\n `Custom provider base URL must use https: or http: (got ${url.protocol})`,\n );\n }\n if (await isBlockedExtensionUrlWithDns(url.href)) {\n throw new Error(\n `Custom provider base URL resolves to a private/internal address — SSRF not allowed.`,\n );\n }\n return url;\n}\n\nfunction validateProviderId(id: string): void {\n if (!PROVIDER_ID_RE.test(id)) {\n throw new Error(\n `Custom provider id must be 1–64 lowercase letters, digits, or hyphens (got \"${id}\").`,\n );\n }\n}\n\nfunction validateAuth(auth: CustomProviderAuthKind): void {\n if (auth.type === \"bearer\") {\n if (!auth.credentialKey)\n throw new Error(\"bearer auth requires credentialKey\");\n } else if (auth.type === \"basic\") {\n if (!auth.usernameKey || !auth.passwordKey)\n throw new Error(\"basic auth requires usernameKey and passwordKey\");\n } else if (auth.type === \"api-key-header\") {\n if (!auth.credentialKey || !auth.headerName)\n throw new Error(\n \"api-key-header auth requires credentialKey and headerName\",\n );\n if (!HEADER_NAME_RE.test(auth.headerName)) {\n throw new Error(\n `Invalid header name for api-key-header auth: ${auth.headerName}`,\n );\n }\n }\n}\n\n/**\n * Public suffixes that must never be used as an allowed host suffix — a\n * suffix like \"com\" would attach the provider's credentials to requests for\n * any host under that TLD, creating a credential-exfiltration vector. Not an\n * exhaustive public-suffix list; combined with the structural checks below.\n */\nconst FORBIDDEN_HOST_SUFFIXES = new Set([\n \"com\",\n \"net\",\n \"org\",\n \"io\",\n \"co\",\n \"dev\",\n \"app\",\n \"ai\",\n \"cloud\",\n \"info\",\n \"biz\",\n \"xyz\",\n \"me\",\n \"us\",\n \"uk\",\n \"eu\",\n \"co.uk\",\n \"org.uk\",\n \"ac.uk\",\n \"gov.uk\",\n \"com.au\",\n \"net.au\",\n \"org.au\",\n \"co.nz\",\n \"co.jp\",\n \"com.br\",\n \"com.cn\",\n \"co.in\",\n \"com.mx\",\n \"co.za\",\n \"github.io\",\n \"herokuapp.com\",\n \"vercel.app\",\n \"netlify.app\",\n \"pages.dev\",\n \"workers.dev\",\n \"azurewebsites.net\",\n \"cloudfront.net\",\n \"amazonaws.com\",\n \"ngrok.io\",\n \"ngrok.app\",\n]);\n\nconst HOST_SUFFIX_RE =\n /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;\n\n/**\n * Validate user-supplied allowed host suffixes. Each suffix must look like a\n * real registrable domain (at least two labels) and must not be a bare TLD or\n * shared-hosting public suffix where unrelated parties control subdomains.\n */\nexport function validateAllowedHostSuffixes(suffixes: string[]): string[] {\n const normalized: string[] = [];\n for (const raw of suffixes) {\n const suffix = String(raw).trim().toLowerCase().replace(/^\\.+/, \"\");\n if (!suffix) continue;\n if (!HOST_SUFFIX_RE.test(suffix)) {\n throw new Error(\n `Invalid allowed host suffix \"${raw}\": must be a domain like \"api.example.com\" with at least two labels.`,\n );\n }\n if (FORBIDDEN_HOST_SUFFIXES.has(suffix)) {\n throw new Error(\n `Allowed host suffix \"${raw}\" is too broad: it would attach this provider's credentials to any host under a public suffix. Use the provider's own registrable domain (e.g. \"example.com\").`,\n );\n }\n normalized.push(suffix);\n }\n return normalized;\n}\n\n// ---------------------------------------------------------------------------\n// CRUD\n// ---------------------------------------------------------------------------\n\n/**\n * Create or update a custom provider. Validates the base URL against SSRF\n * rules at write time. Returns the provider id.\n */\nexport async function upsertCustomProvider(\n args: UpsertCustomProviderArgs,\n): Promise<string> {\n await ensureTable();\n validateProviderId(args.id);\n validateAuth(args.auth);\n const url = await validateCustomBaseUrl(args.baseUrl);\n const baseUrl = url.href.replace(/\\/+$/, \"\");\n const allowedHostSuffixes = validateAllowedHostSuffixes(\n args.allowedHostSuffixes ?? [],\n );\n\n const now = Date.now();\n const client = getDbExec();\n\n const { rows } = await client.execute({\n sql: `SELECT created_at FROM custom_api_providers WHERE scope = ? AND scope_id = ? AND id = ?`,\n args: [args.scope, args.scopeId, args.id],\n });\n\n if (rows.length > 0) {\n await client.execute({\n sql: `UPDATE custom_api_providers SET label=?, base_url=?, auth_json=?, docs_urls_json=?, allowed_host_suffixes_json=?, default_headers_json=?, notes=?, updated_at=? WHERE scope=? AND scope_id=? AND id=?`,\n args: [\n args.label,\n baseUrl,\n JSON.stringify(args.auth),\n JSON.stringify(args.docsUrls ?? []),\n JSON.stringify(allowedHostSuffixes),\n JSON.stringify(args.defaultHeaders ?? {}),\n args.notes ?? \"\",\n now,\n args.scope,\n args.scopeId,\n args.id,\n ],\n });\n } else {\n await client.execute({\n sql: `INSERT INTO custom_api_providers (id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`,\n args: [\n args.id,\n args.scope,\n args.scopeId,\n args.label,\n baseUrl,\n JSON.stringify(args.auth),\n JSON.stringify(args.docsUrls ?? []),\n JSON.stringify(allowedHostSuffixes),\n JSON.stringify(args.defaultHeaders ?? {}),\n args.notes ?? \"\",\n now,\n now,\n ],\n });\n }\n\n return args.id;\n}\n\n/**\n * Delete a custom provider. Returns true if a row was deleted.\n */\nexport async function deleteCustomProvider(\n scope: CustomProviderScope,\n scopeId: string,\n id: string,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const { rowsAffected } = await client.execute({\n sql: `DELETE FROM custom_api_providers WHERE scope=? AND scope_id=? AND id=?`,\n args: [scope, scopeId, id],\n });\n return rowsAffected > 0;\n}\n\n/**\n * List all custom providers visible to a given (scope, scopeId) pair.\n */\nexport async function listCustomProviders(\n scope: CustomProviderScope,\n scopeId: string,\n): Promise<CustomProviderConfig[]> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at FROM custom_api_providers WHERE scope=? AND scope_id=? ORDER BY updated_at DESC`,\n args: [scope, scopeId],\n });\n return rows.map(rowToConfig);\n}\n\n/**\n * Look up one custom provider. Returns null when not found.\n */\nexport async function getCustomProvider(\n scope: CustomProviderScope,\n scopeId: string,\n id: string,\n): Promise<CustomProviderConfig | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at FROM custom_api_providers WHERE scope=? AND scope_id=? AND id=? LIMIT 1`,\n args: [scope, scopeId, id],\n });\n if (rows.length === 0) return null;\n return rowToConfig(rows[0]);\n}\n\nfunction rowToConfig(row: Record<string, unknown>): CustomProviderConfig {\n return {\n id: row.id as string,\n scope: row.scope as CustomProviderScope,\n scopeId: row.scope_id as string,\n label: row.label as string,\n baseUrl: row.base_url as string,\n auth: JSON.parse(row.auth_json as string) as CustomProviderAuthKind,\n docsUrls: JSON.parse(row.docs_urls_json as string) as string[],\n allowedHostSuffixes: JSON.parse(\n row.allowed_host_suffixes_json as string,\n ) as string[],\n defaultHeaders: JSON.parse(row.default_headers_json as string) as Record<\n string,\n string\n >,\n notes: row.notes as string,\n createdAt: Number(row.created_at),\n updatedAt: Number(row.updated_at),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"custom-registry.js","sourceRoot":"","sources":["../../src/provider-api/custom-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Custom provider registry — runtime storage and resolution.\n *\n * Lets apps register arbitrary HTTP API providers at runtime without touching\n * the static PROVIDER_CONFIGS list. Provider rows live in the\n * `custom_api_providers` SQL table (created on first use). Credentials\n * referenced in the provider row continue to live in the existing secrets /\n * credentials store — this table stores only key NAMES, never values.\n *\n * Scoping mirrors the `app_secrets` table: each row is scoped to a user\n * (by email) or an organisation (by orgId).\n *\n * Supported auth kinds for custom providers:\n * - bearer → Authorization: Bearer <credential>\n * - basic → Authorization: Basic base64(user:pass)\n * - api-key-header → custom header: <credential>\n *\n * google-service-account and oauth-bearer are intentionally unsupported for\n * custom providers because they require additional out-of-band setup that\n * cannot be expressed in the simple header/key model.\n */\n\nimport { getDbExec, isPostgres } from \"../db/client.js\";\nimport { ensureTableExists } from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport { isBlockedExtensionUrlWithDns } from \"../extensions/url-safety.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CustomProviderScope = \"user\" | \"org\";\n\nexport type CustomProviderAuthKind =\n | { type: \"bearer\"; credentialKey: string }\n | { type: \"basic\"; usernameKey: string; passwordKey: string }\n | { type: \"api-key-header\"; credentialKey: string; headerName: string }\n | { type: \"none\" };\n\nexport interface CustomProviderConfig {\n id: string;\n scope: CustomProviderScope;\n scopeId: string;\n label: string;\n baseUrl: string;\n auth: CustomProviderAuthKind;\n docsUrls: string[];\n allowedHostSuffixes: string[];\n defaultHeaders: Record<string, string>;\n notes: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface UpsertCustomProviderArgs {\n scope: CustomProviderScope;\n scopeId: string;\n /** Slug used as the provider id (e.g. \"my-api\"). Must be lowercase, letters/digits/hyphens. */\n id: string;\n label: string;\n baseUrl: string;\n auth: CustomProviderAuthKind;\n docsUrls?: string[];\n allowedHostSuffixes?: string[];\n defaultHeaders?: Record<string, string>;\n notes?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Table bootstrap\n// ---------------------------------------------------------------------------\n\nconst CREATE_SQL = `CREATE TABLE IF NOT EXISTS custom_api_providers (\n id TEXT NOT NULL,\n scope TEXT NOT NULL,\n scope_id TEXT NOT NULL,\n label TEXT NOT NULL,\n base_url TEXT NOT NULL,\n auth_json TEXT NOT NULL,\n docs_urls_json TEXT NOT NULL,\n allowed_host_suffixes_json TEXT NOT NULL,\n default_headers_json TEXT NOT NULL,\n notes TEXT NOT NULL DEFAULT '',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (scope, scope_id, id)\n)`;\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n\n if (isPostgres()) {\n const pgSql = CREATE_SQL.replace(/\\bINTEGER\\b/g, \"BIGINT\");\n // PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout\n await ensureTableExists(\"custom_api_providers\", pgSql);\n // Widen any pre-existing int4 timestamp columns to BIGINT (no-op once done)\n await widenIntColumnsToBigInt(\"custom_api_providers\", [\n \"created_at\",\n \"updated_at\",\n ]);\n return;\n }\n // SQLite (local dev): keep existing behavior\n await client.execute(CREATE_SQL);\n // Fresh Postgres tables get BIGINT via the replace above, but tables\n // created before that compat existed kept int4 timestamp columns; widen\n // them so `Date.now()` writes don't overflow. No-op once done.\n await widenIntColumnsToBigInt(\"custom_api_providers\", [\n \"created_at\",\n \"updated_at\",\n ]);\n })().catch((err) => {\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nconst PROVIDER_ID_RE = /^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$|^[a-z0-9]$/;\nconst HEADER_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\n/**\n * Validate and normalise a custom provider base URL. Throws when the URL is\n * invalid, uses a non-http(s) scheme, or resolves to a private/internal host.\n */\nexport async function validateCustomBaseUrl(rawUrl: string): Promise<URL> {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new Error(`Invalid base URL: ${rawUrl}`);\n }\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n throw new Error(\n `Custom provider base URL must use https: or http: (got ${url.protocol})`,\n );\n }\n if (await isBlockedExtensionUrlWithDns(url.href)) {\n throw new Error(\n `Custom provider base URL resolves to a private/internal address — SSRF not allowed.`,\n );\n }\n return url;\n}\n\nfunction validateProviderId(id: string): void {\n if (!PROVIDER_ID_RE.test(id)) {\n throw new Error(\n `Custom provider id must be 1–64 lowercase letters, digits, or hyphens (got \"${id}\").`,\n );\n }\n}\n\nfunction validateAuth(auth: CustomProviderAuthKind): void {\n if (auth.type === \"bearer\") {\n if (!auth.credentialKey)\n throw new Error(\"bearer auth requires credentialKey\");\n } else if (auth.type === \"basic\") {\n if (!auth.usernameKey || !auth.passwordKey)\n throw new Error(\"basic auth requires usernameKey and passwordKey\");\n } else if (auth.type === \"api-key-header\") {\n if (!auth.credentialKey || !auth.headerName)\n throw new Error(\n \"api-key-header auth requires credentialKey and headerName\",\n );\n if (!HEADER_NAME_RE.test(auth.headerName)) {\n throw new Error(\n `Invalid header name for api-key-header auth: ${auth.headerName}`,\n );\n }\n }\n}\n\n/**\n * Public suffixes that must never be used as an allowed host suffix — a\n * suffix like \"com\" would attach the provider's credentials to requests for\n * any host under that TLD, creating a credential-exfiltration vector. Not an\n * exhaustive public-suffix list; combined with the structural checks below.\n */\nconst FORBIDDEN_HOST_SUFFIXES = new Set([\n \"com\",\n \"net\",\n \"org\",\n \"io\",\n \"co\",\n \"dev\",\n \"app\",\n \"ai\",\n \"cloud\",\n \"info\",\n \"biz\",\n \"xyz\",\n \"me\",\n \"us\",\n \"uk\",\n \"eu\",\n \"co.uk\",\n \"org.uk\",\n \"ac.uk\",\n \"gov.uk\",\n \"com.au\",\n \"net.au\",\n \"org.au\",\n \"co.nz\",\n \"co.jp\",\n \"com.br\",\n \"com.cn\",\n \"co.in\",\n \"com.mx\",\n \"co.za\",\n \"github.io\",\n \"herokuapp.com\",\n \"vercel.app\",\n \"netlify.app\",\n \"pages.dev\",\n \"workers.dev\",\n \"azurewebsites.net\",\n \"cloudfront.net\",\n \"amazonaws.com\",\n \"ngrok.io\",\n \"ngrok.app\",\n]);\n\nconst HOST_SUFFIX_RE =\n /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;\n\n/**\n * Validate user-supplied allowed host suffixes. Each suffix must look like a\n * real registrable domain (at least two labels) and must not be a bare TLD or\n * shared-hosting public suffix where unrelated parties control subdomains.\n */\nexport function validateAllowedHostSuffixes(suffixes: string[]): string[] {\n const normalized: string[] = [];\n for (const raw of suffixes) {\n const suffix = String(raw).trim().toLowerCase().replace(/^\\.+/, \"\");\n if (!suffix) continue;\n if (!HOST_SUFFIX_RE.test(suffix)) {\n throw new Error(\n `Invalid allowed host suffix \"${raw}\": must be a domain like \"api.example.com\" with at least two labels.`,\n );\n }\n if (FORBIDDEN_HOST_SUFFIXES.has(suffix)) {\n throw new Error(\n `Allowed host suffix \"${raw}\" is too broad: it would attach this provider's credentials to any host under a public suffix. Use the provider's own registrable domain (e.g. \"example.com\").`,\n );\n }\n normalized.push(suffix);\n }\n return normalized;\n}\n\n// ---------------------------------------------------------------------------\n// CRUD\n// ---------------------------------------------------------------------------\n\n/**\n * Create or update a custom provider. Validates the base URL against SSRF\n * rules at write time. Returns the provider id.\n */\nexport async function upsertCustomProvider(\n args: UpsertCustomProviderArgs,\n): Promise<string> {\n await ensureTable();\n validateProviderId(args.id);\n validateAuth(args.auth);\n const url = await validateCustomBaseUrl(args.baseUrl);\n const baseUrl = url.href.replace(/\\/+$/, \"\");\n const allowedHostSuffixes = validateAllowedHostSuffixes(\n args.allowedHostSuffixes ?? [],\n );\n\n const now = Date.now();\n const client = getDbExec();\n\n const { rows } = await client.execute({\n sql: `SELECT created_at FROM custom_api_providers WHERE scope = ? AND scope_id = ? AND id = ?`,\n args: [args.scope, args.scopeId, args.id],\n });\n\n if (rows.length > 0) {\n await client.execute({\n sql: `UPDATE custom_api_providers SET label=?, base_url=?, auth_json=?, docs_urls_json=?, allowed_host_suffixes_json=?, default_headers_json=?, notes=?, updated_at=? WHERE scope=? AND scope_id=? AND id=?`,\n args: [\n args.label,\n baseUrl,\n JSON.stringify(args.auth),\n JSON.stringify(args.docsUrls ?? []),\n JSON.stringify(allowedHostSuffixes),\n JSON.stringify(args.defaultHeaders ?? {}),\n args.notes ?? \"\",\n now,\n args.scope,\n args.scopeId,\n args.id,\n ],\n });\n } else {\n await client.execute({\n sql: `INSERT INTO custom_api_providers (id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`,\n args: [\n args.id,\n args.scope,\n args.scopeId,\n args.label,\n baseUrl,\n JSON.stringify(args.auth),\n JSON.stringify(args.docsUrls ?? []),\n JSON.stringify(allowedHostSuffixes),\n JSON.stringify(args.defaultHeaders ?? {}),\n args.notes ?? \"\",\n now,\n now,\n ],\n });\n }\n\n return args.id;\n}\n\n/**\n * Delete a custom provider. Returns true if a row was deleted.\n */\nexport async function deleteCustomProvider(\n scope: CustomProviderScope,\n scopeId: string,\n id: string,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const { rowsAffected } = await client.execute({\n sql: `DELETE FROM custom_api_providers WHERE scope=? AND scope_id=? AND id=?`,\n args: [scope, scopeId, id],\n });\n return rowsAffected > 0;\n}\n\n/**\n * List all custom providers visible to a given (scope, scopeId) pair.\n */\nexport async function listCustomProviders(\n scope: CustomProviderScope,\n scopeId: string,\n): Promise<CustomProviderConfig[]> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at FROM custom_api_providers WHERE scope=? AND scope_id=? ORDER BY updated_at DESC`,\n args: [scope, scopeId],\n });\n return rows.map(rowToConfig);\n}\n\n/**\n * Look up one custom provider. Returns null when not found.\n */\nexport async function getCustomProvider(\n scope: CustomProviderScope,\n scopeId: string,\n id: string,\n): Promise<CustomProviderConfig | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at FROM custom_api_providers WHERE scope=? AND scope_id=? AND id=? LIMIT 1`,\n args: [scope, scopeId, id],\n });\n if (rows.length === 0) return null;\n return rowToConfig(rows[0]);\n}\n\nfunction rowToConfig(row: Record<string, unknown>): CustomProviderConfig {\n return {\n id: row.id as string,\n scope: row.scope as CustomProviderScope,\n scopeId: row.scope_id as string,\n label: row.label as string,\n baseUrl: row.base_url as string,\n auth: JSON.parse(row.auth_json as string) as CustomProviderAuthKind,\n docsUrls: JSON.parse(row.docs_urls_json as string) as string[],\n allowedHostSuffixes: JSON.parse(\n row.allowed_host_suffixes_json as string,\n ) as string[],\n defaultHeaders: JSON.parse(row.default_headers_json as string) as Record<\n string,\n string\n >,\n notes: row.notes as string,\n createdAt: Number(row.created_at),\n updatedAt: Number(row.updated_at),\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quota-governor.d.ts","sourceRoot":"","sources":["../../src/provider-api/quota-governor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"quota-governor.d.ts","sourceRoot":"","sources":["../../src/provider-api/quota-governor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAIjE,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,iBAAiB,CAAC;IACvB,iBAAiB,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACtD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,qBAAqB,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,2BAA2B;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,GAAG,aAAa,GAAG,cAAc,CAAC;CACrD;AAED,MAAM,WAAW,+BAA+B,CAAC,CAAC;IAChD,OAAO,EAAE,oBAAoB,CAAC;IAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1B,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,2BAA2B,CAAC;IACpD,yBAAyB,EAAE,CAAC,MAAM,EAAE,4BAA4B,KAAK,CAAC,CAAC;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyCD,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,0BAA0B,GAChC,qBAAqB,CAiCvB;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,GAAG,MAAM,GAAG,SAAS,CAcrB;AAED,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,OAAO,EAAE,+BAA+B,CAAC,CAAC,CAAC,GAC1C,OAAO,CAAC,CAAC,CAAC,CAuBZ;AAED,wBAAgB,+BAA+B,IAAI,IAAI,CAMtD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { getDbExec, intType } from "../db/client.js";
|
|
2
|
+
import { getDbExec, intType, isPostgres } from "../db/client.js";
|
|
3
|
+
import { ensureTableExists, ensureIndexExists } from "../db/ddl-guard.js";
|
|
3
4
|
const GLOBAL_KEY = "__agentNativeProviderApiQuotaGovernor";
|
|
4
5
|
const globalRef = globalThis;
|
|
5
6
|
const initialState = {
|
|
@@ -252,7 +253,7 @@ async function ensureCooldownTable() {
|
|
|
252
253
|
state.initPromise = (async () => {
|
|
253
254
|
const db = getDbExec();
|
|
254
255
|
const integerType = intType();
|
|
255
|
-
|
|
256
|
+
const createSql = `
|
|
256
257
|
CREATE TABLE IF NOT EXISTS provider_api_cooldowns (
|
|
257
258
|
quota_key TEXT NOT NULL,
|
|
258
259
|
provider_id TEXT NOT NULL,
|
|
@@ -263,7 +264,15 @@ async function ensureCooldownTable() {
|
|
|
263
264
|
updated_at ${integerType} NOT NULL,
|
|
264
265
|
PRIMARY KEY (quota_key)
|
|
265
266
|
)
|
|
266
|
-
|
|
267
|
+
`;
|
|
268
|
+
if (isPostgres()) {
|
|
269
|
+
// PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout
|
|
270
|
+
await ensureTableExists("provider_api_cooldowns", createSql);
|
|
271
|
+
await ensureIndexExists("provider_api_cooldowns_provider_idx", `CREATE INDEX IF NOT EXISTS provider_api_cooldowns_provider_idx ON provider_api_cooldowns (provider_id, cooldown_until)`);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// SQLite (local dev): keep existing behavior
|
|
275
|
+
await db.execute(createSql);
|
|
267
276
|
try {
|
|
268
277
|
await db.execute(`CREATE INDEX IF NOT EXISTS provider_api_cooldowns_provider_idx ON provider_api_cooldowns (provider_id, cooldown_until)`);
|
|
269
278
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quota-governor.js","sourceRoot":"","sources":["../../src/provider-api/quota-governor.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport type { CredentialContext } from \"../credentials/index.js\";\nimport { getDbExec, intType } from \"../db/client.js\";\n\nexport interface ProviderQuotaIdentityInput {\n appId: string;\n providerId: string;\n ctx: CredentialContext;\n credentialSources: readonly Record<string, unknown>[];\n connectionId?: string | null;\n accountId?: string | null;\n}\n\nexport interface ProviderQuotaIdentity {\n quotaKey: string;\n providerId: string;\n scopeKey: string;\n credentialFingerprint: string;\n}\n\nexport interface ProviderQuotaRequest {\n identity: ProviderQuotaIdentity;\n method: string;\n target: string;\n requestKey?: string;\n}\n\nexport interface ProviderQuotaObservedResult {\n status?: number;\n headers?: Record<string, string>;\n}\n\nexport interface ProviderQuotaExhaustedDetail {\n providerId: string;\n scopeKey: string;\n quotaKey: string;\n method: string;\n target: string;\n retryAfterMs: number;\n retryAt: string;\n reason: \"cooldown\" | \"retry_after\" | \"max_attempts\";\n}\n\nexport interface ExecuteWithProviderQuotaOptions<T> {\n request: ProviderQuotaRequest;\n execute: () => Promise<T>;\n inspect: (result: T) => ProviderQuotaObservedResult;\n buildQuotaExhaustedResult: (detail: ProviderQuotaExhaustedDetail) => T;\n maxAttempts?: number;\n maxWaitMs?: number;\n}\n\ninterface QueueState {\n tail: Promise<void>;\n}\n\ninterface CooldownEntry {\n until: number;\n providerId: string;\n scopeKey: string;\n status?: number;\n reason?: string;\n}\n\ninterface ProviderQuotaState {\n queues: Map<string, QueueState>;\n inflight: Map<string, Promise<unknown>>;\n cooldowns: Map<string, CooldownEntry>;\n initPromise?: Promise<void>;\n persistenceUnavailableUntil?: number;\n}\n\nconst GLOBAL_KEY = \"__agentNativeProviderApiQuotaGovernor\" as const;\ntype GlobalWithProviderQuota = typeof globalThis & {\n [GLOBAL_KEY]?: ProviderQuotaState;\n};\n\nconst globalRef = globalThis as GlobalWithProviderQuota;\nconst initialState: ProviderQuotaState = {\n queues: new Map(),\n inflight: new Map(),\n cooldowns: new Map(),\n};\nconst state: ProviderQuotaState =\n globalRef[GLOBAL_KEY] ?? (globalRef[GLOBAL_KEY] = initialState);\n\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_MAX_WAIT_MS = 55_000;\nconst MAX_FALLBACK_BACKOFF_MS = 30_000;\nconst PERSISTENCE_RETRY_MS = 60_000;\n\nexport function createProviderQuotaIdentity(\n input: ProviderQuotaIdentityInput,\n): ProviderQuotaIdentity {\n const scopeKey = input.ctx.orgId\n ? `org:${input.ctx.orgId}`\n : input.ctx.userEmail\n ? `user:${input.ctx.userEmail}`\n : \"anonymous\";\n const credentialSources = input.credentialSources\n .map((source) => ({\n provider: String(source.provider ?? input.providerId),\n key: String(source.key ?? \"\"),\n source: String(source.source ?? \"\"),\n connectionId: stringOrNull(source.connectionId) ?? input.connectionId,\n accountId: stringOrNull(source.accountId) ?? input.accountId,\n scope: stringOrNull(source.scope),\n }))\n .sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));\n const credentialFingerprint = hashStable({\n providerId: input.providerId,\n connectionId: input.connectionId ?? null,\n accountId: input.accountId ?? null,\n credentialSources,\n });\n return {\n providerId: input.providerId,\n scopeKey,\n credentialFingerprint,\n quotaKey: hashStable({\n appId: input.appId,\n providerId: input.providerId,\n scopeKey,\n credentialFingerprint,\n }),\n };\n}\n\nexport function createProviderRequestDedupeKey(input: {\n method: string;\n url: string;\n body?: BodyInit;\n headers?: Record<string, string>;\n}): string | undefined {\n const method = input.method.toUpperCase();\n if (method !== \"GET\" && method !== \"HEAD\") return undefined;\n return hashStable({\n method,\n url: input.url,\n headers: dedupeHeaders(input.headers),\n body:\n typeof input.body === \"string\"\n ? input.body\n : input.body == null\n ? null\n : \"[body]\",\n });\n}\n\nexport async function executeWithProviderQuota<T>(\n options: ExecuteWithProviderQuotaOptions<T>,\n): Promise<T> {\n if (isQuotaGovernorDisabled()) return options.execute();\n\n const dedupeKey = options.request.requestKey\n ? `${options.request.identity.quotaKey}:${options.request.requestKey}`\n : null;\n if (dedupeKey) {\n const existing = state.inflight.get(dedupeKey);\n if (existing) return existing as Promise<T>;\n }\n\n const run = runInQueue(options.request.identity.quotaKey, () =>\n executeWithCooldown(options),\n );\n if (dedupeKey) {\n state.inflight.set(dedupeKey, run);\n run\n .finally(() => {\n state.inflight.delete(dedupeKey);\n })\n .catch(() => undefined);\n }\n return run;\n}\n\nexport function resetProviderQuotaStateForTests(): void {\n state.queues.clear();\n state.inflight.clear();\n state.cooldowns.clear();\n state.initPromise = undefined;\n state.persistenceUnavailableUntil = undefined;\n}\n\nasync function executeWithCooldown<T>(\n options: ExecuteWithProviderQuotaOptions<T>,\n): Promise<T> {\n const maxAttempts = positiveIntFromEnv(\n \"AGENT_NATIVE_PROVIDER_API_MAX_ATTEMPTS\",\n options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,\n );\n const maxWaitMs = positiveIntFromEnv(\n \"AGENT_NATIVE_PROVIDER_API_MAX_WAIT_MS\",\n options.maxWaitMs ?? DEFAULT_MAX_WAIT_MS,\n );\n const deadline = Date.now() + maxWaitMs;\n\n for (let attempt = 0; ; attempt++) {\n const cooldownUntil = await getCooldownUntil(options.request.identity);\n if (cooldownUntil > Date.now()) {\n const waitMs = cooldownUntil - Date.now();\n if (waitMs > Math.max(0, deadline - Date.now())) {\n return options.buildQuotaExhaustedResult(\n quotaExhaustedDetail(options.request, waitMs, \"cooldown\"),\n );\n }\n await sleepMs(waitMs);\n }\n\n const result = await options.execute();\n const observed = options.inspect(result);\n if (observed.status !== 429) {\n return result;\n }\n\n const retryAfterMs = retryDelayMs(observed.headers, attempt);\n await setCooldownUntil(\n options.request.identity,\n Date.now() + retryAfterMs,\n {\n status: observed.status,\n reason: \"429\",\n },\n );\n\n if (attempt + 1 >= maxAttempts) {\n return options.buildQuotaExhaustedResult(\n quotaExhaustedDetail(options.request, retryAfterMs, \"max_attempts\"),\n );\n }\n\n if (retryAfterMs > Math.max(0, deadline - Date.now())) {\n return options.buildQuotaExhaustedResult(\n quotaExhaustedDetail(options.request, retryAfterMs, \"retry_after\"),\n );\n }\n\n await sleepMs(retryAfterMs);\n }\n}\n\nasync function runInQueue<T>(\n quotaKey: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const queue = state.queues.get(quotaKey) ?? { tail: Promise.resolve() };\n state.queues.set(quotaKey, queue);\n\n const previous = queue.tail.catch(() => undefined);\n let release: () => void = () => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = previous.then(() => current);\n queue.tail = tail;\n\n await previous;\n try {\n return await fn();\n } finally {\n release();\n if (queue.tail === tail) state.queues.delete(quotaKey);\n }\n}\n\nasync function getCooldownUntil(\n identity: ProviderQuotaIdentity,\n): Promise<number> {\n const memory = state.cooldowns.get(identity.quotaKey);\n const now = Date.now();\n if (memory && memory.until <= now) {\n state.cooldowns.delete(identity.quotaKey);\n }\n let until = memory && memory.until > now ? memory.until : 0;\n\n const persisted = await readPersistedCooldown(identity.quotaKey);\n if (persisted && persisted.until > now) {\n state.cooldowns.set(identity.quotaKey, persisted);\n until = Math.max(until, persisted.until);\n }\n return until;\n}\n\nasync function setCooldownUntil(\n identity: ProviderQuotaIdentity,\n until: number,\n options: { status?: number; reason?: string } = {},\n): Promise<void> {\n const entry: CooldownEntry = {\n until,\n providerId: identity.providerId,\n scopeKey: identity.scopeKey,\n status: options.status,\n reason: options.reason,\n };\n state.cooldowns.set(identity.quotaKey, entry);\n await writePersistedCooldown(identity.quotaKey, entry);\n}\n\nfunction quotaExhaustedDetail(\n request: ProviderQuotaRequest,\n retryAfterMs: number,\n reason: ProviderQuotaExhaustedDetail[\"reason\"],\n): ProviderQuotaExhaustedDetail {\n const retryAtMs = Date.now() + Math.max(0, retryAfterMs);\n return {\n providerId: request.identity.providerId,\n scopeKey: request.identity.scopeKey,\n quotaKey: request.identity.quotaKey,\n method: request.method,\n target: request.target,\n retryAfterMs: Math.max(0, retryAfterMs),\n retryAt: new Date(retryAtMs).toISOString(),\n reason,\n };\n}\n\nfunction retryDelayMs(\n headers: Record<string, string> | undefined,\n attempt: number,\n): number {\n const retryAfter = retryAfterMs(headers);\n if (retryAfter !== null) return retryAfter;\n const base = Math.min(1000 * 2 ** attempt, MAX_FALLBACK_BACKOFF_MS);\n return Math.min(\n base + Math.floor(Math.random() * 250),\n MAX_FALLBACK_BACKOFF_MS,\n );\n}\n\nfunction retryAfterMs(\n headers: Record<string, string> | undefined,\n): number | null {\n const raw = headerValue(headers, \"retry-after\");\n if (!raw) return null;\n const seconds = Number(raw);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const dateMs = Date.parse(raw);\n if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());\n return null;\n}\n\nfunction headerValue(\n headers: Record<string, string> | undefined,\n name: string,\n): string | undefined {\n if (!headers) return undefined;\n const lowerName = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) return value;\n }\n return undefined;\n}\n\nfunction sleepMs(ms: number): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction positiveIntFromEnv(name: string, fallback: number): number {\n const raw = process.env[name];\n const parsed = raw ? Number(raw) : Number.NaN;\n if (Number.isFinite(parsed) && parsed > 0) return Math.floor(parsed);\n return fallback;\n}\n\nfunction isQuotaGovernorDisabled(): boolean {\n const raw = process.env.AGENT_NATIVE_PROVIDER_API_QUOTA_GOVERNOR;\n return raw === \"0\" || raw?.toLowerCase() === \"false\";\n}\n\nfunction shouldPersistCooldowns(): boolean {\n const raw = process.env.AGENT_NATIVE_PROVIDER_API_PERSIST_COOLDOWNS;\n if (raw === \"0\" || raw?.toLowerCase() === \"false\") return false;\n if (raw === \"1\" || raw?.toLowerCase() === \"true\") return true;\n if (process.env.NODE_ENV === \"test\" || process.env.VITEST) return false;\n return Boolean(\n process.env.DATABASE_URL ||\n process.env.NETLIFY ||\n process.env.VERCEL ||\n process.env.AWS_LAMBDA_FUNCTION_NAME ||\n process.env.APP_NAME,\n );\n}\n\nfunction persistenceTemporarilyUnavailable(): boolean {\n return Boolean(\n state.persistenceUnavailableUntil &&\n state.persistenceUnavailableUntil > Date.now(),\n );\n}\n\nasync function ensureCooldownTable(): Promise<void> {\n if (!shouldPersistCooldowns()) return;\n if (persistenceTemporarilyUnavailable()) return;\n if (!state.initPromise) {\n state.initPromise = (async () => {\n const db = getDbExec();\n const integerType = intType();\n await db.execute(`\n CREATE TABLE IF NOT EXISTS provider_api_cooldowns (\n quota_key TEXT NOT NULL,\n provider_id TEXT NOT NULL,\n scope_key TEXT NOT NULL,\n cooldown_until ${integerType} NOT NULL,\n status ${integerType},\n reason TEXT,\n updated_at ${integerType} NOT NULL,\n PRIMARY KEY (quota_key)\n )\n `);\n try {\n await db.execute(\n `CREATE INDEX IF NOT EXISTS provider_api_cooldowns_provider_idx ON provider_api_cooldowns (provider_id, cooldown_until)`,\n );\n } catch {\n // Index already exists or the backend rejected best-effort indexing.\n }\n })().catch((err) => {\n state.initPromise = undefined;\n state.persistenceUnavailableUntil = Date.now() + PERSISTENCE_RETRY_MS;\n throw err;\n });\n }\n await state.initPromise;\n}\n\nasync function readPersistedCooldown(\n quotaKey: string,\n): Promise<CooldownEntry | null> {\n try {\n if (!shouldPersistCooldowns() || persistenceTemporarilyUnavailable()) {\n return null;\n }\n await ensureCooldownTable();\n if (!shouldPersistCooldowns() || persistenceTemporarilyUnavailable()) {\n return null;\n }\n const db = getDbExec();\n const { rows } = await db.execute({\n sql: `SELECT provider_id, scope_key, cooldown_until, status, reason FROM provider_api_cooldowns WHERE quota_key = ?`,\n args: [quotaKey],\n });\n const row = rows[0];\n if (!row) return null;\n const until = Number(row.cooldown_until);\n if (!Number.isFinite(until) || until <= Date.now()) {\n await db\n .execute({\n sql: `DELETE FROM provider_api_cooldowns WHERE quota_key = ?`,\n args: [quotaKey],\n })\n .catch(() => undefined);\n return null;\n }\n return {\n until,\n providerId: String(row.provider_id ?? \"\"),\n scopeKey: String(row.scope_key ?? \"\"),\n status: row.status == null ? undefined : Number(row.status),\n reason: row.reason == null ? undefined : String(row.reason),\n };\n } catch {\n state.persistenceUnavailableUntil = Date.now() + PERSISTENCE_RETRY_MS;\n return null;\n }\n}\n\nasync function writePersistedCooldown(\n quotaKey: string,\n entry: CooldownEntry,\n): Promise<void> {\n try {\n if (!shouldPersistCooldowns() || persistenceTemporarilyUnavailable()) {\n return;\n }\n await ensureCooldownTable();\n if (!shouldPersistCooldowns() || persistenceTemporarilyUnavailable()) {\n return;\n }\n const db = getDbExec();\n await db.execute({\n sql: `\n INSERT INTO provider_api_cooldowns\n (quota_key, provider_id, scope_key, cooldown_until, status, reason, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (quota_key) DO UPDATE SET\n provider_id = excluded.provider_id,\n scope_key = excluded.scope_key,\n cooldown_until = excluded.cooldown_until,\n status = excluded.status,\n reason = excluded.reason,\n updated_at = excluded.updated_at\n `,\n args: [\n quotaKey,\n entry.providerId,\n entry.scopeKey,\n Math.floor(entry.until),\n entry.status ?? null,\n entry.reason ?? null,\n Date.now(),\n ],\n });\n } catch {\n state.persistenceUnavailableUntil = Date.now() + PERSISTENCE_RETRY_MS;\n }\n}\n\nfunction hashStable(value: unknown): string {\n return createHash(\"sha256\")\n .update(stableStringify(value))\n .digest(\"hex\")\n .slice(0, 24);\n}\n\nfunction stableStringify(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, entry]) => entry !== undefined)\n .sort(([a], [b]) => a.localeCompare(b));\n return `{${entries\n .map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`)\n .join(\",\")}}`;\n}\n\nfunction stringOrNull(value: unknown): string | null {\n return typeof value === \"string\" && value ? value : null;\n}\n\nfunction dedupeHeaders(\n headers: Record<string, string> | undefined,\n): Record<string, string> {\n if (!headers) return {};\n const blocked = new Set([\n \"authorization\",\n \"cookie\",\n \"proxy-authorization\",\n \"set-cookie\",\n \"x-api-key\",\n \"api-key\",\n ]);\n return Object.fromEntries(\n Object.entries(headers)\n .filter(([key]) => !blocked.has(key.toLowerCase()))\n .sort(([a], [b]) => a.localeCompare(b)),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"quota-governor.js","sourceRoot":"","sources":["../../src/provider-api/quota-governor.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport type { CredentialContext } from \"../credentials/index.js\";\nimport { getDbExec, intType, isPostgres } from \"../db/client.js\";\nimport { ensureTableExists, ensureIndexExists } from \"../db/ddl-guard.js\";\n\nexport interface ProviderQuotaIdentityInput {\n appId: string;\n providerId: string;\n ctx: CredentialContext;\n credentialSources: readonly Record<string, unknown>[];\n connectionId?: string | null;\n accountId?: string | null;\n}\n\nexport interface ProviderQuotaIdentity {\n quotaKey: string;\n providerId: string;\n scopeKey: string;\n credentialFingerprint: string;\n}\n\nexport interface ProviderQuotaRequest {\n identity: ProviderQuotaIdentity;\n method: string;\n target: string;\n requestKey?: string;\n}\n\nexport interface ProviderQuotaObservedResult {\n status?: number;\n headers?: Record<string, string>;\n}\n\nexport interface ProviderQuotaExhaustedDetail {\n providerId: string;\n scopeKey: string;\n quotaKey: string;\n method: string;\n target: string;\n retryAfterMs: number;\n retryAt: string;\n reason: \"cooldown\" | \"retry_after\" | \"max_attempts\";\n}\n\nexport interface ExecuteWithProviderQuotaOptions<T> {\n request: ProviderQuotaRequest;\n execute: () => Promise<T>;\n inspect: (result: T) => ProviderQuotaObservedResult;\n buildQuotaExhaustedResult: (detail: ProviderQuotaExhaustedDetail) => T;\n maxAttempts?: number;\n maxWaitMs?: number;\n}\n\ninterface QueueState {\n tail: Promise<void>;\n}\n\ninterface CooldownEntry {\n until: number;\n providerId: string;\n scopeKey: string;\n status?: number;\n reason?: string;\n}\n\ninterface ProviderQuotaState {\n queues: Map<string, QueueState>;\n inflight: Map<string, Promise<unknown>>;\n cooldowns: Map<string, CooldownEntry>;\n initPromise?: Promise<void>;\n persistenceUnavailableUntil?: number;\n}\n\nconst GLOBAL_KEY = \"__agentNativeProviderApiQuotaGovernor\" as const;\ntype GlobalWithProviderQuota = typeof globalThis & {\n [GLOBAL_KEY]?: ProviderQuotaState;\n};\n\nconst globalRef = globalThis as GlobalWithProviderQuota;\nconst initialState: ProviderQuotaState = {\n queues: new Map(),\n inflight: new Map(),\n cooldowns: new Map(),\n};\nconst state: ProviderQuotaState =\n globalRef[GLOBAL_KEY] ?? (globalRef[GLOBAL_KEY] = initialState);\n\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_MAX_WAIT_MS = 55_000;\nconst MAX_FALLBACK_BACKOFF_MS = 30_000;\nconst PERSISTENCE_RETRY_MS = 60_000;\n\nexport function createProviderQuotaIdentity(\n input: ProviderQuotaIdentityInput,\n): ProviderQuotaIdentity {\n const scopeKey = input.ctx.orgId\n ? `org:${input.ctx.orgId}`\n : input.ctx.userEmail\n ? `user:${input.ctx.userEmail}`\n : \"anonymous\";\n const credentialSources = input.credentialSources\n .map((source) => ({\n provider: String(source.provider ?? input.providerId),\n key: String(source.key ?? \"\"),\n source: String(source.source ?? \"\"),\n connectionId: stringOrNull(source.connectionId) ?? input.connectionId,\n accountId: stringOrNull(source.accountId) ?? input.accountId,\n scope: stringOrNull(source.scope),\n }))\n .sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));\n const credentialFingerprint = hashStable({\n providerId: input.providerId,\n connectionId: input.connectionId ?? null,\n accountId: input.accountId ?? null,\n credentialSources,\n });\n return {\n providerId: input.providerId,\n scopeKey,\n credentialFingerprint,\n quotaKey: hashStable({\n appId: input.appId,\n providerId: input.providerId,\n scopeKey,\n credentialFingerprint,\n }),\n };\n}\n\nexport function createProviderRequestDedupeKey(input: {\n method: string;\n url: string;\n body?: BodyInit;\n headers?: Record<string, string>;\n}): string | undefined {\n const method = input.method.toUpperCase();\n if (method !== \"GET\" && method !== \"HEAD\") return undefined;\n return hashStable({\n method,\n url: input.url,\n headers: dedupeHeaders(input.headers),\n body:\n typeof input.body === \"string\"\n ? input.body\n : input.body == null\n ? null\n : \"[body]\",\n });\n}\n\nexport async function executeWithProviderQuota<T>(\n options: ExecuteWithProviderQuotaOptions<T>,\n): Promise<T> {\n if (isQuotaGovernorDisabled()) return options.execute();\n\n const dedupeKey = options.request.requestKey\n ? `${options.request.identity.quotaKey}:${options.request.requestKey}`\n : null;\n if (dedupeKey) {\n const existing = state.inflight.get(dedupeKey);\n if (existing) return existing as Promise<T>;\n }\n\n const run = runInQueue(options.request.identity.quotaKey, () =>\n executeWithCooldown(options),\n );\n if (dedupeKey) {\n state.inflight.set(dedupeKey, run);\n run\n .finally(() => {\n state.inflight.delete(dedupeKey);\n })\n .catch(() => undefined);\n }\n return run;\n}\n\nexport function resetProviderQuotaStateForTests(): void {\n state.queues.clear();\n state.inflight.clear();\n state.cooldowns.clear();\n state.initPromise = undefined;\n state.persistenceUnavailableUntil = undefined;\n}\n\nasync function executeWithCooldown<T>(\n options: ExecuteWithProviderQuotaOptions<T>,\n): Promise<T> {\n const maxAttempts = positiveIntFromEnv(\n \"AGENT_NATIVE_PROVIDER_API_MAX_ATTEMPTS\",\n options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,\n );\n const maxWaitMs = positiveIntFromEnv(\n \"AGENT_NATIVE_PROVIDER_API_MAX_WAIT_MS\",\n options.maxWaitMs ?? DEFAULT_MAX_WAIT_MS,\n );\n const deadline = Date.now() + maxWaitMs;\n\n for (let attempt = 0; ; attempt++) {\n const cooldownUntil = await getCooldownUntil(options.request.identity);\n if (cooldownUntil > Date.now()) {\n const waitMs = cooldownUntil - Date.now();\n if (waitMs > Math.max(0, deadline - Date.now())) {\n return options.buildQuotaExhaustedResult(\n quotaExhaustedDetail(options.request, waitMs, \"cooldown\"),\n );\n }\n await sleepMs(waitMs);\n }\n\n const result = await options.execute();\n const observed = options.inspect(result);\n if (observed.status !== 429) {\n return result;\n }\n\n const retryAfterMs = retryDelayMs(observed.headers, attempt);\n await setCooldownUntil(\n options.request.identity,\n Date.now() + retryAfterMs,\n {\n status: observed.status,\n reason: \"429\",\n },\n );\n\n if (attempt + 1 >= maxAttempts) {\n return options.buildQuotaExhaustedResult(\n quotaExhaustedDetail(options.request, retryAfterMs, \"max_attempts\"),\n );\n }\n\n if (retryAfterMs > Math.max(0, deadline - Date.now())) {\n return options.buildQuotaExhaustedResult(\n quotaExhaustedDetail(options.request, retryAfterMs, \"retry_after\"),\n );\n }\n\n await sleepMs(retryAfterMs);\n }\n}\n\nasync function runInQueue<T>(\n quotaKey: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const queue = state.queues.get(quotaKey) ?? { tail: Promise.resolve() };\n state.queues.set(quotaKey, queue);\n\n const previous = queue.tail.catch(() => undefined);\n let release: () => void = () => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = previous.then(() => current);\n queue.tail = tail;\n\n await previous;\n try {\n return await fn();\n } finally {\n release();\n if (queue.tail === tail) state.queues.delete(quotaKey);\n }\n}\n\nasync function getCooldownUntil(\n identity: ProviderQuotaIdentity,\n): Promise<number> {\n const memory = state.cooldowns.get(identity.quotaKey);\n const now = Date.now();\n if (memory && memory.until <= now) {\n state.cooldowns.delete(identity.quotaKey);\n }\n let until = memory && memory.until > now ? memory.until : 0;\n\n const persisted = await readPersistedCooldown(identity.quotaKey);\n if (persisted && persisted.until > now) {\n state.cooldowns.set(identity.quotaKey, persisted);\n until = Math.max(until, persisted.until);\n }\n return until;\n}\n\nasync function setCooldownUntil(\n identity: ProviderQuotaIdentity,\n until: number,\n options: { status?: number; reason?: string } = {},\n): Promise<void> {\n const entry: CooldownEntry = {\n until,\n providerId: identity.providerId,\n scopeKey: identity.scopeKey,\n status: options.status,\n reason: options.reason,\n };\n state.cooldowns.set(identity.quotaKey, entry);\n await writePersistedCooldown(identity.quotaKey, entry);\n}\n\nfunction quotaExhaustedDetail(\n request: ProviderQuotaRequest,\n retryAfterMs: number,\n reason: ProviderQuotaExhaustedDetail[\"reason\"],\n): ProviderQuotaExhaustedDetail {\n const retryAtMs = Date.now() + Math.max(0, retryAfterMs);\n return {\n providerId: request.identity.providerId,\n scopeKey: request.identity.scopeKey,\n quotaKey: request.identity.quotaKey,\n method: request.method,\n target: request.target,\n retryAfterMs: Math.max(0, retryAfterMs),\n retryAt: new Date(retryAtMs).toISOString(),\n reason,\n };\n}\n\nfunction retryDelayMs(\n headers: Record<string, string> | undefined,\n attempt: number,\n): number {\n const retryAfter = retryAfterMs(headers);\n if (retryAfter !== null) return retryAfter;\n const base = Math.min(1000 * 2 ** attempt, MAX_FALLBACK_BACKOFF_MS);\n return Math.min(\n base + Math.floor(Math.random() * 250),\n MAX_FALLBACK_BACKOFF_MS,\n );\n}\n\nfunction retryAfterMs(\n headers: Record<string, string> | undefined,\n): number | null {\n const raw = headerValue(headers, \"retry-after\");\n if (!raw) return null;\n const seconds = Number(raw);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const dateMs = Date.parse(raw);\n if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());\n return null;\n}\n\nfunction headerValue(\n headers: Record<string, string> | undefined,\n name: string,\n): string | undefined {\n if (!headers) return undefined;\n const lowerName = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) return value;\n }\n return undefined;\n}\n\nfunction sleepMs(ms: number): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction positiveIntFromEnv(name: string, fallback: number): number {\n const raw = process.env[name];\n const parsed = raw ? Number(raw) : Number.NaN;\n if (Number.isFinite(parsed) && parsed > 0) return Math.floor(parsed);\n return fallback;\n}\n\nfunction isQuotaGovernorDisabled(): boolean {\n const raw = process.env.AGENT_NATIVE_PROVIDER_API_QUOTA_GOVERNOR;\n return raw === \"0\" || raw?.toLowerCase() === \"false\";\n}\n\nfunction shouldPersistCooldowns(): boolean {\n const raw = process.env.AGENT_NATIVE_PROVIDER_API_PERSIST_COOLDOWNS;\n if (raw === \"0\" || raw?.toLowerCase() === \"false\") return false;\n if (raw === \"1\" || raw?.toLowerCase() === \"true\") return true;\n if (process.env.NODE_ENV === \"test\" || process.env.VITEST) return false;\n return Boolean(\n process.env.DATABASE_URL ||\n process.env.NETLIFY ||\n process.env.VERCEL ||\n process.env.AWS_LAMBDA_FUNCTION_NAME ||\n process.env.APP_NAME,\n );\n}\n\nfunction persistenceTemporarilyUnavailable(): boolean {\n return Boolean(\n state.persistenceUnavailableUntil &&\n state.persistenceUnavailableUntil > Date.now(),\n );\n}\n\nasync function ensureCooldownTable(): Promise<void> {\n if (!shouldPersistCooldowns()) return;\n if (persistenceTemporarilyUnavailable()) return;\n if (!state.initPromise) {\n state.initPromise = (async () => {\n const db = getDbExec();\n const integerType = intType();\n const createSql = `\n CREATE TABLE IF NOT EXISTS provider_api_cooldowns (\n quota_key TEXT NOT NULL,\n provider_id TEXT NOT NULL,\n scope_key TEXT NOT NULL,\n cooldown_until ${integerType} NOT NULL,\n status ${integerType},\n reason TEXT,\n updated_at ${integerType} NOT NULL,\n PRIMARY KEY (quota_key)\n )\n `;\n if (isPostgres()) {\n // PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout\n await ensureTableExists(\"provider_api_cooldowns\", createSql);\n await ensureIndexExists(\n \"provider_api_cooldowns_provider_idx\",\n `CREATE INDEX IF NOT EXISTS provider_api_cooldowns_provider_idx ON provider_api_cooldowns (provider_id, cooldown_until)`,\n );\n return;\n }\n // SQLite (local dev): keep existing behavior\n await db.execute(createSql);\n try {\n await db.execute(\n `CREATE INDEX IF NOT EXISTS provider_api_cooldowns_provider_idx ON provider_api_cooldowns (provider_id, cooldown_until)`,\n );\n } catch {\n // Index already exists or the backend rejected best-effort indexing.\n }\n })().catch((err) => {\n state.initPromise = undefined;\n state.persistenceUnavailableUntil = Date.now() + PERSISTENCE_RETRY_MS;\n throw err;\n });\n }\n await state.initPromise;\n}\n\nasync function readPersistedCooldown(\n quotaKey: string,\n): Promise<CooldownEntry | null> {\n try {\n if (!shouldPersistCooldowns() || persistenceTemporarilyUnavailable()) {\n return null;\n }\n await ensureCooldownTable();\n if (!shouldPersistCooldowns() || persistenceTemporarilyUnavailable()) {\n return null;\n }\n const db = getDbExec();\n const { rows } = await db.execute({\n sql: `SELECT provider_id, scope_key, cooldown_until, status, reason FROM provider_api_cooldowns WHERE quota_key = ?`,\n args: [quotaKey],\n });\n const row = rows[0];\n if (!row) return null;\n const until = Number(row.cooldown_until);\n if (!Number.isFinite(until) || until <= Date.now()) {\n await db\n .execute({\n sql: `DELETE FROM provider_api_cooldowns WHERE quota_key = ?`,\n args: [quotaKey],\n })\n .catch(() => undefined);\n return null;\n }\n return {\n until,\n providerId: String(row.provider_id ?? \"\"),\n scopeKey: String(row.scope_key ?? \"\"),\n status: row.status == null ? undefined : Number(row.status),\n reason: row.reason == null ? undefined : String(row.reason),\n };\n } catch {\n state.persistenceUnavailableUntil = Date.now() + PERSISTENCE_RETRY_MS;\n return null;\n }\n}\n\nasync function writePersistedCooldown(\n quotaKey: string,\n entry: CooldownEntry,\n): Promise<void> {\n try {\n if (!shouldPersistCooldowns() || persistenceTemporarilyUnavailable()) {\n return;\n }\n await ensureCooldownTable();\n if (!shouldPersistCooldowns() || persistenceTemporarilyUnavailable()) {\n return;\n }\n const db = getDbExec();\n await db.execute({\n sql: `\n INSERT INTO provider_api_cooldowns\n (quota_key, provider_id, scope_key, cooldown_until, status, reason, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (quota_key) DO UPDATE SET\n provider_id = excluded.provider_id,\n scope_key = excluded.scope_key,\n cooldown_until = excluded.cooldown_until,\n status = excluded.status,\n reason = excluded.reason,\n updated_at = excluded.updated_at\n `,\n args: [\n quotaKey,\n entry.providerId,\n entry.scopeKey,\n Math.floor(entry.until),\n entry.status ?? null,\n entry.reason ?? null,\n Date.now(),\n ],\n });\n } catch {\n state.persistenceUnavailableUntil = Date.now() + PERSISTENCE_RETRY_MS;\n }\n}\n\nfunction hashStable(value: unknown): string {\n return createHash(\"sha256\")\n .update(stableStringify(value))\n .digest(\"hex\")\n .slice(0, 24);\n}\n\nfunction stableStringify(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, entry]) => entry !== undefined)\n .sort(([a], [b]) => a.localeCompare(b));\n return `{${entries\n .map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`)\n .join(\",\")}}`;\n}\n\nfunction stringOrNull(value: unknown): string | null {\n return typeof value === \"string\" && value ? value : null;\n}\n\nfunction dedupeHeaders(\n headers: Record<string, string> | undefined,\n): Record<string, string> {\n if (!headers) return {};\n const blocked = new Set([\n \"authorization\",\n \"cookie\",\n \"proxy-authorization\",\n \"set-cookie\",\n \"x-api-key\",\n \"api-key\",\n ]);\n return Object.fromEntries(\n Object.entries(headers)\n .filter(([key]) => !blocked.has(key.toLowerCase()))\n .sort(([a], [b]) => a.localeCompare(b)),\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staged-datasets-store.d.ts","sourceRoot":"","sources":["../../src/provider-api/staged-datasets-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"staged-datasets-store.d.ts","sourceRoot":"","sources":["../../src/provider-api/staged-datasets-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,eAAO,MAAM,gBAAgB,SAAU,CAAC;AACxC,eAAO,MAAM,iBAAiB,QAAmB,CAAC;AA+FlD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,mDAAmD;IACnD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAUD,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,EAAE,CAMvE;AA4BD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAmH5B;AAMD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAiBrC;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CASpC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAQ/B;AAMD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBnB;AAqCD,mDAAmD;AACnD,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* different users never share or cross-read each other's scratch data.
|
|
14
14
|
*/
|
|
15
15
|
import { getDbExec, intType, isPostgres } from "../db/client.js";
|
|
16
|
+
import { ensureTableExists, ensureIndexExists } from "../db/ddl-guard.js";
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
// Caps
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
@@ -27,7 +28,7 @@ async function ensureTables() {
|
|
|
27
28
|
_initPromise = (async () => {
|
|
28
29
|
const db = getDbExec();
|
|
29
30
|
const integerType = intType();
|
|
30
|
-
|
|
31
|
+
const createDatasetsSql = `
|
|
31
32
|
CREATE TABLE IF NOT EXISTS staged_datasets (
|
|
32
33
|
id TEXT NOT NULL,
|
|
33
34
|
app_id TEXT NOT NULL,
|
|
@@ -40,18 +41,28 @@ async function ensureTables() {
|
|
|
40
41
|
updated_at ${integerType} NOT NULL,
|
|
41
42
|
PRIMARY KEY (id)
|
|
42
43
|
)
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
`;
|
|
45
|
+
const createRowsSql = `
|
|
45
46
|
CREATE TABLE IF NOT EXISTS staged_dataset_rows (
|
|
46
47
|
dataset_id TEXT NOT NULL,
|
|
47
48
|
row_index ${integerType} NOT NULL,
|
|
48
49
|
row_data TEXT NOT NULL,
|
|
49
50
|
PRIMARY KEY (dataset_id, row_index)
|
|
50
51
|
)
|
|
51
|
-
|
|
52
|
+
`;
|
|
52
53
|
if (isPostgres()) {
|
|
53
|
-
|
|
54
|
+
// PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout
|
|
55
|
+
await ensureTableExists("staged_datasets", createDatasetsSql);
|
|
56
|
+
await ensureTableExists("staged_dataset_rows", createRowsSql);
|
|
57
|
+
await widenPostgresIntegerColumns(db); // best-effort type widening — unchanged
|
|
58
|
+
await ensureIndexExists("staged_datasets_scope_idx", `CREATE INDEX IF NOT EXISTS staged_datasets_scope_idx ON staged_datasets (app_id, owner_email)`);
|
|
59
|
+
await ensureIndexExists("staged_dataset_rows_dataset_idx", `CREATE INDEX IF NOT EXISTS staged_dataset_rows_dataset_idx ON staged_dataset_rows (dataset_id)`);
|
|
60
|
+
return;
|
|
54
61
|
}
|
|
62
|
+
// SQLite (local dev): keep existing behavior
|
|
63
|
+
await db.execute(createDatasetsSql);
|
|
64
|
+
await db.execute(createRowsSql);
|
|
65
|
+
// Note: widenPostgresIntegerColumns is Postgres-only, skip on SQLite
|
|
55
66
|
for (const ddl of [
|
|
56
67
|
`CREATE INDEX IF NOT EXISTS staged_datasets_scope_idx ON staged_datasets (app_id, owner_email)`,
|
|
57
68
|
`CREATE INDEX IF NOT EXISTS staged_dataset_rows_dataset_idx ON staged_dataset_rows (dataset_id)`,
|