mcp-scraper 0.66.5 → 0.66.7
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/CHANGELOG.md +17 -1
- package/README.md +1 -1
- package/dist/bin/api-server.cjs +910 -601
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +2 -2
- package/dist/bin/mcp-scraper-install.js +2 -2
- package/dist/bin/mcp-stdio-server.cjs +105 -18
- package/dist/bin/mcp-stdio-server.js +6 -6
- package/dist/bin/paa-harvest.cjs +14 -1
- package/dist/bin/paa-harvest.js +3 -3
- package/dist/{chunk-RSFMR7O7.js → chunk-7KB5QTHP.js} +1 -1
- package/dist/{chunk-K7Y27ND3.js → chunk-CGOMGGOF.js} +2 -2
- package/dist/{chunk-DBWWIK7O.js → chunk-EVOBJYBE.js} +1 -1
- package/dist/{chunk-3EYALCYK.js → chunk-F434YJPV.js} +1 -1
- package/dist/{chunk-PMCT2YQM.js → chunk-HBZENASD.js} +1 -1
- package/dist/{chunk-KXRWY7DZ.js → chunk-HWGQVP3Q.js} +1 -1
- package/dist/{chunk-55B3T5L2.js → chunk-HZDJ5UZ4.js} +68 -1
- package/dist/{chunk-ABYBPODM.js → chunk-JZPSG4NR.js} +1 -1
- package/dist/{chunk-CETDPNMZ.js → chunk-LVXRWJJJ.js} +1 -1
- package/dist/{chunk-TCOP5BT7.js → chunk-O7ZC5Y3Z.js} +1 -1
- package/dist/{chunk-3TKKJQB4.js → chunk-U2UHJWFW.js} +1 -1
- package/dist/{chunk-OJFWURGD.js → chunk-Z5QQEWXA.js} +53 -19
- package/dist/{db-4RVZ3NYZ.js → db-MNNNIWVS.js} +1 -1
- package/dist/{extract-bundle-EA5URLXN.js → extract-bundle-RW7CVIRF.js} +3 -3
- package/dist/index.cjs +14 -1
- package/dist/index.js +3 -3
- package/dist/{lead-list-enrichment-repository-IBXNWM6H.js → lead-list-enrichment-repository-PZYE6TDP.js} +2 -2
- package/dist/{location-data-repository-WHHQUCUI.js → location-data-repository-EPCNGMOM.js} +2 -2
- package/dist/{server-LESUYFDI.js → server-KSVJ4X4V.js} +650 -445
- package/dist/{site-extract-repository-NTM4MDQN.js → site-extract-repository-CZ7SVT3Z.js} +2 -2
- package/dist/{worker-UTBUWEXQ.js → worker-IBLGV272.js} +5 -5
- package/package.json +1 -1
|
@@ -10,13 +10,13 @@ import {
|
|
|
10
10
|
queuedCommonsDocumentCount,
|
|
11
11
|
semanticCommonsEntityScores,
|
|
12
12
|
typeFromMime
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-EVOBJYBE.js";
|
|
14
14
|
import {
|
|
15
15
|
REQUIRED_LOCATION_STATE_CODES,
|
|
16
16
|
importHostedCensusPlacesCsv,
|
|
17
17
|
importHostedZipGroupsCsv,
|
|
18
18
|
listActiveHostedLocationDatasets
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-HBZENASD.js";
|
|
20
20
|
import {
|
|
21
21
|
cancelPendingLeadListEnrichmentRows,
|
|
22
22
|
claimLeadListEnrichmentOutbox,
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
saveLeadListEnrichmentRowResult,
|
|
44
44
|
setInitializingLeadListEnrichmentConcurrencyLock,
|
|
45
45
|
stableCanonicalJson
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-JZPSG4NR.js";
|
|
47
47
|
import {
|
|
48
48
|
ANALYTICS_CONTENT_SORTS,
|
|
49
49
|
AnalyticsRepositoryError,
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
harvestProblemResponse,
|
|
109
109
|
publicErrorBoundary,
|
|
110
110
|
serializeHarvestProblem
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-LVXRWJJJ.js";
|
|
112
112
|
import "./chunk-OZJMVCDK.js";
|
|
113
113
|
import {
|
|
114
114
|
BACKUP_LAST_RESORT_ATTEMPTS,
|
|
@@ -127,7 +127,7 @@ import {
|
|
|
127
127
|
resolveKernelProxyId,
|
|
128
128
|
serpArrivalUrl,
|
|
129
129
|
withRuntimeUserAgent
|
|
130
|
-
} from "./chunk-
|
|
130
|
+
} from "./chunk-CGOMGGOF.js";
|
|
131
131
|
import {
|
|
132
132
|
ApiErrorResponseSchema,
|
|
133
133
|
ArtifactTemplatePresetKeySchema,
|
|
@@ -199,7 +199,7 @@ import {
|
|
|
199
199
|
resolveDeploymentProfile,
|
|
200
200
|
resolveLocalSourcebookSchemaType,
|
|
201
201
|
transcribeMediaUrl
|
|
202
|
-
} from "./chunk-
|
|
202
|
+
} from "./chunk-Z5QQEWXA.js";
|
|
203
203
|
import {
|
|
204
204
|
auditImageUrls,
|
|
205
205
|
auditImages,
|
|
@@ -210,7 +210,7 @@ import {
|
|
|
210
210
|
sanitizeAttempts,
|
|
211
211
|
sanitizeHarvestResult,
|
|
212
212
|
sanitizeOutboundDiagnostics
|
|
213
|
-
} from "./chunk-
|
|
213
|
+
} from "./chunk-F434YJPV.js";
|
|
214
214
|
import {
|
|
215
215
|
browserServiceApiKey,
|
|
216
216
|
browserServiceProfileName,
|
|
@@ -220,7 +220,7 @@ import {
|
|
|
220
220
|
recordKernelSession,
|
|
221
221
|
recordVendorUsage,
|
|
222
222
|
runWithCostContext
|
|
223
|
-
} from "./chunk-
|
|
223
|
+
} from "./chunk-U2UHJWFW.js";
|
|
224
224
|
import {
|
|
225
225
|
listWorkflowDefinitions,
|
|
226
226
|
runWorkflow,
|
|
@@ -256,7 +256,7 @@ import {
|
|
|
256
256
|
} from "./chunk-GGZEC22A.js";
|
|
257
257
|
import {
|
|
258
258
|
PACKAGE_VERSION
|
|
259
|
-
} from "./chunk-
|
|
259
|
+
} from "./chunk-7KB5QTHP.js";
|
|
260
260
|
import {
|
|
261
261
|
SITE_EXTRACT_ARTIFACT_PREFIX,
|
|
262
262
|
abandonExtractSettlement,
|
|
@@ -287,7 +287,7 @@ import {
|
|
|
287
287
|
setExtractJobTotal,
|
|
288
288
|
settleExtractJob,
|
|
289
289
|
terminalExtractJobStatus
|
|
290
|
-
} from "./chunk-
|
|
290
|
+
} from "./chunk-HWGQVP3Q.js";
|
|
291
291
|
import {
|
|
292
292
|
createPrivateArtifact,
|
|
293
293
|
privateArtifactOwnerId,
|
|
@@ -451,7 +451,7 @@ import {
|
|
|
451
451
|
softDeleteInboxMessage,
|
|
452
452
|
upsertPageSnapshot,
|
|
453
453
|
verifyPassword
|
|
454
|
-
} from "./chunk-
|
|
454
|
+
} from "./chunk-HZDJ5UZ4.js";
|
|
455
455
|
|
|
456
456
|
// src/blog/registry.ts
|
|
457
457
|
var posts = [
|
|
@@ -9545,7 +9545,7 @@ var siteExtractFn = inngest.createFunction(
|
|
|
9545
9545
|
});
|
|
9546
9546
|
}) : null;
|
|
9547
9547
|
const finalized = await step.run("finalize", async () => {
|
|
9548
|
-
const { assembleExtractArtifacts } = await import("./extract-bundle-
|
|
9548
|
+
const { assembleExtractArtifacts } = await import("./extract-bundle-RW7CVIRF.js");
|
|
9549
9549
|
const current = await getExtractJob(jobId2);
|
|
9550
9550
|
if (!current) throw new Error("extract job disappeared before finalization");
|
|
9551
9551
|
const stored = await assembleExtractArtifacts(current, { branding, imageAudit });
|
|
@@ -10505,7 +10505,7 @@ async function resolveUsZipsPath(requestedPath) {
|
|
|
10505
10505
|
async function loadZipGroups(stateAbbr, requestedPath, warnings, sourceMode) {
|
|
10506
10506
|
const mode = resolvedLocationDataMode({ zipSource: sourceMode });
|
|
10507
10507
|
if (mode === "hosted") {
|
|
10508
|
-
const { getHostedZipGroups } = await import("./location-data-repository-
|
|
10508
|
+
const { getHostedZipGroups } = await import("./location-data-repository-EPCNGMOM.js");
|
|
10509
10509
|
const hosted = await getHostedZipGroups(stateAbbr);
|
|
10510
10510
|
if (!hosted.dataset) {
|
|
10511
10511
|
throw new DirectoryLocationError(
|
|
@@ -10568,7 +10568,7 @@ async function resolveDirectoryMarkets(options, config = {}) {
|
|
|
10568
10568
|
const sourceUrl = censusStateUrl(state.fips);
|
|
10569
10569
|
const warnings = [];
|
|
10570
10570
|
if (resolvedLocationDataMode(config) === "hosted") {
|
|
10571
|
-
const { queryHostedLocationMarkets } = await import("./location-data-repository-
|
|
10571
|
+
const { queryHostedLocationMarkets } = await import("./location-data-repository-EPCNGMOM.js");
|
|
10572
10572
|
const hosted = await queryHostedLocationMarkets({
|
|
10573
10573
|
state: state.abbr,
|
|
10574
10574
|
minPopulation: options.minPopulation,
|
|
@@ -12055,7 +12055,7 @@ function summarizePersistedLeadRows(rows, warnings = []) {
|
|
|
12055
12055
|
async function settlePersistedLeadListEnrichment(jobId2) {
|
|
12056
12056
|
const jobs = await listLeadListEnrichmentJobsNeedingSettlement(100);
|
|
12057
12057
|
const job = jobs.find((candidate) => candidate.id === jobId2);
|
|
12058
|
-
const { getLeadListEnrichmentJob: getLeadListEnrichmentJob2 } = await import("./lead-list-enrichment-repository-
|
|
12058
|
+
const { getLeadListEnrichmentJob: getLeadListEnrichmentJob2 } = await import("./lead-list-enrichment-repository-PZYE6TDP.js");
|
|
12059
12059
|
const current = job ?? await getLeadListEnrichmentJob2(jobId2);
|
|
12060
12060
|
if (!current) return null;
|
|
12061
12061
|
const rows = await listLeadListEnrichmentRows(jobId2);
|
|
@@ -12111,7 +12111,7 @@ async function reconcileLeadListEnrichmentSettlements(limit = 25) {
|
|
|
12111
12111
|
const persisted = job.result ?? summarizePersistedLeadRows(rows, job.warnings);
|
|
12112
12112
|
const status = job.cancelRequestedAt ? "cancelled" : persisted.failedRows > 0 && persisted.completeRows > 0 ? "partial" : persisted.failedRows > 0 ? "cancelled" : persisted.rowsWithContacts > 0 ? "complete" : "empty";
|
|
12113
12113
|
if (persisted.failedRows > 0 && persisted.completeRows === 0 && !job.cancelRequestedAt) {
|
|
12114
|
-
const { failLeadListEnrichmentJob: failLeadListEnrichmentJob2 } = await import("./lead-list-enrichment-repository-
|
|
12114
|
+
const { failLeadListEnrichmentJob: failLeadListEnrichmentJob2 } = await import("./lead-list-enrichment-repository-PZYE6TDP.js");
|
|
12115
12115
|
await failLeadListEnrichmentJob2({
|
|
12116
12116
|
jobId: job.id,
|
|
12117
12117
|
errorCode: "lead_list_all_rows_failed",
|
|
@@ -16505,6 +16505,350 @@ function findPreferredEntitledSubscription(subscriptions, predicate) {
|
|
|
16505
16505
|
return void 0;
|
|
16506
16506
|
}
|
|
16507
16507
|
|
|
16508
|
+
// src/api/service-connections.ts
|
|
16509
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
16510
|
+
var schemaReady = null;
|
|
16511
|
+
var schemaDb3 = null;
|
|
16512
|
+
function ensureServiceConnectionsSchema() {
|
|
16513
|
+
const currentDb = getDb();
|
|
16514
|
+
if (schemaReady && schemaDb3 === currentDb) return schemaReady;
|
|
16515
|
+
schemaDb3 = currentDb;
|
|
16516
|
+
schemaReady = (async () => {
|
|
16517
|
+
const db = currentDb;
|
|
16518
|
+
await db.execute(`
|
|
16519
|
+
CREATE TABLE IF NOT EXISTS service_connections (
|
|
16520
|
+
id TEXT PRIMARY KEY,
|
|
16521
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
16522
|
+
provider_config_key TEXT NOT NULL,
|
|
16523
|
+
provider TEXT,
|
|
16524
|
+
transport TEXT NOT NULL,
|
|
16525
|
+
upstream_connection_id TEXT,
|
|
16526
|
+
label TEXT,
|
|
16527
|
+
user_label TEXT,
|
|
16528
|
+
provider_account_id TEXT,
|
|
16529
|
+
provider_account_email TEXT,
|
|
16530
|
+
provider_account_name TEXT,
|
|
16531
|
+
provider_identity_checked_at TEXT,
|
|
16532
|
+
provider_identity_source_updated_at TEXT,
|
|
16533
|
+
lifecycle_status TEXT NOT NULL DEFAULT 'pending',
|
|
16534
|
+
operational_status TEXT NOT NULL DEFAULT 'unknown',
|
|
16535
|
+
reconnect_required INTEGER NOT NULL DEFAULT 0,
|
|
16536
|
+
actions_enabled INTEGER NOT NULL DEFAULT 0,
|
|
16537
|
+
read_tools_json TEXT NOT NULL DEFAULT '[]',
|
|
16538
|
+
action_tools_json TEXT NOT NULL DEFAULT '[]',
|
|
16539
|
+
tool_revision TEXT,
|
|
16540
|
+
last_checked_at TEXT,
|
|
16541
|
+
last_successful_call_at TEXT,
|
|
16542
|
+
last_failure_at TEXT,
|
|
16543
|
+
last_failure_code TEXT,
|
|
16544
|
+
last_failure_retryable INTEGER,
|
|
16545
|
+
consecutive_failures INTEGER NOT NULL DEFAULT 0,
|
|
16546
|
+
vault_name TEXT,
|
|
16547
|
+
table_name TEXT,
|
|
16548
|
+
legacy_owner_identity TEXT,
|
|
16549
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
16550
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
16551
|
+
UNIQUE(user_id, provider_config_key, upstream_connection_id)
|
|
16552
|
+
)
|
|
16553
|
+
`);
|
|
16554
|
+
try {
|
|
16555
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN user_label TEXT`);
|
|
16556
|
+
} catch {
|
|
16557
|
+
}
|
|
16558
|
+
try {
|
|
16559
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_id TEXT`);
|
|
16560
|
+
} catch {
|
|
16561
|
+
}
|
|
16562
|
+
try {
|
|
16563
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_email TEXT`);
|
|
16564
|
+
} catch {
|
|
16565
|
+
}
|
|
16566
|
+
try {
|
|
16567
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_name TEXT`);
|
|
16568
|
+
} catch {
|
|
16569
|
+
}
|
|
16570
|
+
try {
|
|
16571
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_checked_at TEXT`);
|
|
16572
|
+
} catch {
|
|
16573
|
+
}
|
|
16574
|
+
try {
|
|
16575
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_source_updated_at TEXT`);
|
|
16576
|
+
} catch {
|
|
16577
|
+
}
|
|
16578
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_user_updated ON service_connections(user_id, updated_at DESC)`);
|
|
16579
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_upstream ON service_connections(provider_config_key, upstream_connection_id)`);
|
|
16580
|
+
await db.execute(`
|
|
16581
|
+
CREATE TABLE IF NOT EXISTS service_connection_health_events (
|
|
16582
|
+
id TEXT PRIMARY KEY,
|
|
16583
|
+
connection_id TEXT NOT NULL REFERENCES service_connections(id) ON DELETE CASCADE,
|
|
16584
|
+
operational_status TEXT NOT NULL,
|
|
16585
|
+
failure_code TEXT,
|
|
16586
|
+
retryable INTEGER,
|
|
16587
|
+
evidence_source TEXT NOT NULL,
|
|
16588
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
16589
|
+
)
|
|
16590
|
+
`);
|
|
16591
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS service_connection_health_events_connection ON service_connection_health_events(connection_id, created_at DESC)`);
|
|
16592
|
+
await db.execute(`
|
|
16593
|
+
CREATE TABLE IF NOT EXISTS service_connection_action_audit (
|
|
16594
|
+
id TEXT PRIMARY KEY,
|
|
16595
|
+
connection_id TEXT NOT NULL REFERENCES service_connections(id) ON DELETE CASCADE,
|
|
16596
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
16597
|
+
tool TEXT NOT NULL,
|
|
16598
|
+
request_id TEXT NOT NULL,
|
|
16599
|
+
status TEXT NOT NULL,
|
|
16600
|
+
error_code TEXT,
|
|
16601
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
16602
|
+
UNIQUE(user_id, request_id)
|
|
16603
|
+
)
|
|
16604
|
+
`);
|
|
16605
|
+
try {
|
|
16606
|
+
await db.execute(`ALTER TABLE service_connection_action_audit ADD COLUMN request_digest TEXT`);
|
|
16607
|
+
} catch {
|
|
16608
|
+
}
|
|
16609
|
+
try {
|
|
16610
|
+
await db.execute(`ALTER TABLE service_connection_action_audit ADD COLUMN result_json TEXT`);
|
|
16611
|
+
} catch {
|
|
16612
|
+
}
|
|
16613
|
+
})().catch((error) => {
|
|
16614
|
+
schemaReady = null;
|
|
16615
|
+
schemaDb3 = null;
|
|
16616
|
+
throw error;
|
|
16617
|
+
});
|
|
16618
|
+
return schemaReady;
|
|
16619
|
+
}
|
|
16620
|
+
function parseTools(value) {
|
|
16621
|
+
if (typeof value !== "string") return [];
|
|
16622
|
+
try {
|
|
16623
|
+
const parsed = JSON.parse(value);
|
|
16624
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
16625
|
+
} catch {
|
|
16626
|
+
return [];
|
|
16627
|
+
}
|
|
16628
|
+
}
|
|
16629
|
+
function rowToRecord(row) {
|
|
16630
|
+
return {
|
|
16631
|
+
id: String(row.id),
|
|
16632
|
+
userId: Number(row.user_id),
|
|
16633
|
+
providerConfigKey: String(row.provider_config_key),
|
|
16634
|
+
provider: typeof row.provider === "string" ? row.provider : null,
|
|
16635
|
+
transport: row.transport === "remote_mcp" ? "remote_mcp" : "nango",
|
|
16636
|
+
upstreamConnectionId: typeof row.upstream_connection_id === "string" ? row.upstream_connection_id : null,
|
|
16637
|
+
label: typeof row.label === "string" ? row.label : null,
|
|
16638
|
+
userLabel: typeof row.user_label === "string" ? row.user_label : null,
|
|
16639
|
+
providerAccountId: typeof row.provider_account_id === "string" ? row.provider_account_id : null,
|
|
16640
|
+
providerAccountEmail: typeof row.provider_account_email === "string" ? row.provider_account_email : null,
|
|
16641
|
+
providerAccountName: typeof row.provider_account_name === "string" ? row.provider_account_name : null,
|
|
16642
|
+
providerIdentityCheckedAt: typeof row.provider_identity_checked_at === "string" ? row.provider_identity_checked_at : null,
|
|
16643
|
+
providerIdentitySourceUpdatedAt: typeof row.provider_identity_source_updated_at === "string" ? row.provider_identity_source_updated_at : null,
|
|
16644
|
+
lifecycleStatus: String(row.lifecycle_status),
|
|
16645
|
+
operationalStatus: String(row.operational_status),
|
|
16646
|
+
reconnectRequired: Number(row.reconnect_required) === 1,
|
|
16647
|
+
actionsEnabled: Number(row.actions_enabled) === 1,
|
|
16648
|
+
readTools: parseTools(row.read_tools_json),
|
|
16649
|
+
actionTools: parseTools(row.action_tools_json),
|
|
16650
|
+
toolRevision: typeof row.tool_revision === "string" ? row.tool_revision : null,
|
|
16651
|
+
lastCheckedAt: typeof row.last_checked_at === "string" ? row.last_checked_at : null,
|
|
16652
|
+
lastSuccessfulCallAt: typeof row.last_successful_call_at === "string" ? row.last_successful_call_at : null,
|
|
16653
|
+
lastFailureAt: typeof row.last_failure_at === "string" ? row.last_failure_at : null,
|
|
16654
|
+
lastFailureCode: typeof row.last_failure_code === "string" ? row.last_failure_code : null,
|
|
16655
|
+
lastFailureRetryable: row.last_failure_retryable === null || row.last_failure_retryable === void 0 ? null : Number(row.last_failure_retryable) === 1,
|
|
16656
|
+
consecutiveFailures: Number(row.consecutive_failures ?? 0),
|
|
16657
|
+
vaultName: typeof row.vault_name === "string" ? row.vault_name : null,
|
|
16658
|
+
tableName: typeof row.table_name === "string" ? row.table_name : null,
|
|
16659
|
+
createdAt: String(row.created_at),
|
|
16660
|
+
updatedAt: String(row.updated_at)
|
|
16661
|
+
};
|
|
16662
|
+
}
|
|
16663
|
+
async function userIdForIdentity(identity) {
|
|
16664
|
+
const user = await getUserByEmail(identity.normalize("NFKC").trim().toLowerCase());
|
|
16665
|
+
if (!user) throw new Error("service_connection_user_not_found");
|
|
16666
|
+
return Number(user.id);
|
|
16667
|
+
}
|
|
16668
|
+
async function reconcileDiscoveredNangoConnections(identity, discovered) {
|
|
16669
|
+
await ensureServiceConnectionsSchema();
|
|
16670
|
+
const db = getDb();
|
|
16671
|
+
const userId = await userIdForIdentity(identity);
|
|
16672
|
+
for (const connection of discovered) {
|
|
16673
|
+
await db.execute({
|
|
16674
|
+
sql: `
|
|
16675
|
+
INSERT INTO service_connections (
|
|
16676
|
+
id, user_id, provider_config_key, provider, transport, upstream_connection_id,
|
|
16677
|
+
label, lifecycle_status, reconnect_required, actions_enabled, legacy_owner_identity, created_at, updated_at
|
|
16678
|
+
) VALUES (?, ?, ?, ?, 'nango', ?, ?, ?, ?, 1, ?, COALESCE(?, datetime('now')), COALESCE(?, datetime('now')))
|
|
16679
|
+
ON CONFLICT(user_id, provider_config_key, upstream_connection_id) DO UPDATE SET
|
|
16680
|
+
provider = excluded.provider,
|
|
16681
|
+
label = COALESCE(excluded.label, service_connections.label),
|
|
16682
|
+
lifecycle_status = excluded.lifecycle_status,
|
|
16683
|
+
reconnect_required = excluded.reconnect_required,
|
|
16684
|
+
updated_at = excluded.updated_at
|
|
16685
|
+
`,
|
|
16686
|
+
args: [
|
|
16687
|
+
randomUUID6(),
|
|
16688
|
+
userId,
|
|
16689
|
+
connection.providerConfigKey,
|
|
16690
|
+
connection.provider,
|
|
16691
|
+
connection.upstreamConnectionId,
|
|
16692
|
+
connection.label,
|
|
16693
|
+
connection.lifecycleStatus,
|
|
16694
|
+
connection.reconnectRequired ? 1 : 0,
|
|
16695
|
+
identity,
|
|
16696
|
+
connection.createdAt,
|
|
16697
|
+
connection.updatedAt
|
|
16698
|
+
]
|
|
16699
|
+
});
|
|
16700
|
+
}
|
|
16701
|
+
return listServiceConnections(identity, "nango");
|
|
16702
|
+
}
|
|
16703
|
+
async function listServiceConnections(identity, transport) {
|
|
16704
|
+
await ensureServiceConnectionsSchema();
|
|
16705
|
+
const userId = await userIdForIdentity(identity);
|
|
16706
|
+
const result = await getDb().execute({
|
|
16707
|
+
sql: `SELECT * FROM service_connections WHERE user_id = ?${transport ? " AND transport = ?" : ""} AND lifecycle_status <> 'disconnected' ORDER BY updated_at DESC`,
|
|
16708
|
+
args: transport ? [userId, transport] : [userId]
|
|
16709
|
+
});
|
|
16710
|
+
return result.rows.map((row) => rowToRecord(row));
|
|
16711
|
+
}
|
|
16712
|
+
async function getOwnedServiceConnection(identity, connectionId) {
|
|
16713
|
+
await ensureServiceConnectionsSchema();
|
|
16714
|
+
const userId = await userIdForIdentity(identity);
|
|
16715
|
+
const result = await getDb().execute({
|
|
16716
|
+
sql: "SELECT * FROM service_connections WHERE id = ? AND user_id = ? LIMIT 1",
|
|
16717
|
+
args: [connectionId, userId]
|
|
16718
|
+
});
|
|
16719
|
+
const row = result.rows[0];
|
|
16720
|
+
return row ? rowToRecord(row) : null;
|
|
16721
|
+
}
|
|
16722
|
+
async function updateServiceConnectionTools(connectionId, readTools, actionTools, toolRevision) {
|
|
16723
|
+
await ensureServiceConnectionsSchema();
|
|
16724
|
+
await getDb().execute({
|
|
16725
|
+
sql: `UPDATE service_connections SET read_tools_json = ?, action_tools_json = ?, tool_revision = ?, updated_at = datetime('now') WHERE id = ?`,
|
|
16726
|
+
args: [JSON.stringify(readTools), JSON.stringify(actionTools), toolRevision, connectionId]
|
|
16727
|
+
});
|
|
16728
|
+
}
|
|
16729
|
+
async function setServiceConnectionProviderIdentity(args) {
|
|
16730
|
+
await ensureServiceConnectionsSchema();
|
|
16731
|
+
await getDb().execute({
|
|
16732
|
+
sql: `
|
|
16733
|
+
UPDATE service_connections SET
|
|
16734
|
+
provider_account_id = ?, provider_account_email = ?, provider_account_name = ?,
|
|
16735
|
+
provider_identity_checked_at = datetime('now'), provider_identity_source_updated_at = ?,
|
|
16736
|
+
updated_at = datetime('now')
|
|
16737
|
+
WHERE id = ?
|
|
16738
|
+
`,
|
|
16739
|
+
args: [
|
|
16740
|
+
args.providerAccountId ?? null,
|
|
16741
|
+
args.providerAccountEmail?.toLowerCase() ?? null,
|
|
16742
|
+
args.providerAccountName ?? null,
|
|
16743
|
+
args.sourceUpdatedAt,
|
|
16744
|
+
args.connectionId
|
|
16745
|
+
]
|
|
16746
|
+
});
|
|
16747
|
+
}
|
|
16748
|
+
async function setServiceConnectionUserLabel(identity, connectionId, userLabel) {
|
|
16749
|
+
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
16750
|
+
if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
|
|
16751
|
+
await getDb().execute({
|
|
16752
|
+
sql: `UPDATE service_connections SET user_label = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
16753
|
+
args: [userLabel, connectionId, connection.userId]
|
|
16754
|
+
});
|
|
16755
|
+
return userLabel;
|
|
16756
|
+
}
|
|
16757
|
+
async function recordServiceConnectionHealth(args) {
|
|
16758
|
+
await ensureServiceConnectionsSchema();
|
|
16759
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16760
|
+
await getDb().execute({
|
|
16761
|
+
sql: `
|
|
16762
|
+
UPDATE service_connections SET
|
|
16763
|
+
operational_status = ?, last_checked_at = ?,
|
|
16764
|
+
last_successful_call_at = CASE WHEN ? = 'available' THEN ? ELSE last_successful_call_at END,
|
|
16765
|
+
last_failure_at = CASE WHEN ? IN ('degraded', 'unavailable') THEN ? ELSE NULL END,
|
|
16766
|
+
last_failure_code = ?, last_failure_retryable = ?,
|
|
16767
|
+
consecutive_failures = CASE WHEN ? = 'available' THEN 0 ELSE consecutive_failures + 1 END,
|
|
16768
|
+
updated_at = datetime('now')
|
|
16769
|
+
WHERE id = ?
|
|
16770
|
+
`,
|
|
16771
|
+
args: [
|
|
16772
|
+
args.operationalStatus,
|
|
16773
|
+
now,
|
|
16774
|
+
args.operationalStatus,
|
|
16775
|
+
now,
|
|
16776
|
+
args.operationalStatus,
|
|
16777
|
+
now,
|
|
16778
|
+
args.failureCode ?? null,
|
|
16779
|
+
args.retryable == null ? null : args.retryable ? 1 : 0,
|
|
16780
|
+
args.operationalStatus,
|
|
16781
|
+
args.connectionId
|
|
16782
|
+
]
|
|
16783
|
+
});
|
|
16784
|
+
await getDb().execute({
|
|
16785
|
+
sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
16786
|
+
args: [randomUUID6(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
|
|
16787
|
+
});
|
|
16788
|
+
}
|
|
16789
|
+
async function setServiceConnectionActions(identity, connectionId, enabled) {
|
|
16790
|
+
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
16791
|
+
if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
|
|
16792
|
+
await getDb().execute({
|
|
16793
|
+
sql: `UPDATE service_connections SET actions_enabled = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
16794
|
+
args: [enabled ? 1 : 0, connectionId, connection.userId]
|
|
16795
|
+
});
|
|
16796
|
+
return enabled;
|
|
16797
|
+
}
|
|
16798
|
+
async function markServiceConnectionDisconnected(identity, connectionId) {
|
|
16799
|
+
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
16800
|
+
if (!connection) throw new Error("service_connection_not_found");
|
|
16801
|
+
await getDb().execute({
|
|
16802
|
+
sql: `UPDATE service_connections SET lifecycle_status = 'disconnected', operational_status = 'unknown', reconnect_required = 0, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
16803
|
+
args: [connectionId, connection.userId]
|
|
16804
|
+
});
|
|
16805
|
+
const remaining = await getDb().execute({
|
|
16806
|
+
sql: `SELECT COUNT(*) AS count FROM service_connections WHERE user_id = ? AND transport = 'nango' AND lifecycle_status = 'connected'`,
|
|
16807
|
+
args: [connection.userId]
|
|
16808
|
+
});
|
|
16809
|
+
return Number(remaining.rows[0]?.count ?? 0);
|
|
16810
|
+
}
|
|
16811
|
+
async function recordServiceConnectionAction(args) {
|
|
16812
|
+
await ensureServiceConnectionsSchema();
|
|
16813
|
+
const connection = await getOwnedServiceConnection(args.identity, args.connectionId);
|
|
16814
|
+
if (!connection) throw new Error("service_connection_not_found");
|
|
16815
|
+
await getDb().execute({
|
|
16816
|
+
sql: `UPDATE service_connection_action_audit SET status = ?, error_code = ?, result_json = ? WHERE user_id = ? AND request_id = ? AND connection_id = ?`,
|
|
16817
|
+
args: [args.status, args.errorCode ?? null, args.result === void 0 ? null : JSON.stringify(args.result), connection.userId, args.requestId, connection.id]
|
|
16818
|
+
});
|
|
16819
|
+
}
|
|
16820
|
+
async function claimServiceConnectionAction(args) {
|
|
16821
|
+
await ensureServiceConnectionsSchema();
|
|
16822
|
+
const connection = await getOwnedServiceConnection(args.identity, args.connectionId);
|
|
16823
|
+
if (!connection) throw new Error("service_connection_not_found");
|
|
16824
|
+
const inserted = await getDb().execute({
|
|
16825
|
+
sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
|
|
16826
|
+
args: [randomUUID6(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
|
|
16827
|
+
});
|
|
16828
|
+
if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
|
|
16829
|
+
const existing = await getDb().execute({
|
|
16830
|
+
sql: `SELECT connection_id, tool, status, error_code, request_digest, result_json FROM service_connection_action_audit WHERE user_id = ? AND request_id = ? LIMIT 1`,
|
|
16831
|
+
args: [connection.userId, args.requestId]
|
|
16832
|
+
});
|
|
16833
|
+
const row = existing.rows[0];
|
|
16834
|
+
const conflict = String(row?.connection_id ?? "") !== connection.id || String(row?.tool ?? "") !== args.tool || String(row?.request_digest ?? "") !== args.requestDigest;
|
|
16835
|
+
let result = void 0;
|
|
16836
|
+
if (!conflict && typeof row?.result_json === "string") {
|
|
16837
|
+
try {
|
|
16838
|
+
result = JSON.parse(row.result_json);
|
|
16839
|
+
} catch {
|
|
16840
|
+
result = void 0;
|
|
16841
|
+
}
|
|
16842
|
+
}
|
|
16843
|
+
return {
|
|
16844
|
+
claimed: false,
|
|
16845
|
+
status: String(row?.status ?? "unknown"),
|
|
16846
|
+
errorCode: typeof row?.error_code === "string" ? row.error_code : null,
|
|
16847
|
+
conflict,
|
|
16848
|
+
...result !== void 0 ? { result } : {}
|
|
16849
|
+
};
|
|
16850
|
+
}
|
|
16851
|
+
|
|
16508
16852
|
// src/api/connected-account-billing.ts
|
|
16509
16853
|
var DEFAULT_CONNECTED_ACCOUNT_PRICE_ID = "price_1TtYPAS8aAcsk3TGHOgrDZiQ";
|
|
16510
16854
|
function connectedBillingPeriodStart(now = /* @__PURE__ */ new Date()) {
|
|
@@ -16521,6 +16865,87 @@ async function chargedConnectionIds(userId, periodStart) {
|
|
|
16521
16865
|
});
|
|
16522
16866
|
return new Set(result.rows.map((row) => String(row.connection_id)));
|
|
16523
16867
|
}
|
|
16868
|
+
async function inheritDisconnectedConnectionCharge(userId, connectionId, periodStart) {
|
|
16869
|
+
await ensureServiceConnectionsSchema();
|
|
16870
|
+
const normalizedUserId = Number(userId);
|
|
16871
|
+
const db = getDb();
|
|
16872
|
+
await db.batch([
|
|
16873
|
+
{
|
|
16874
|
+
sql: `
|
|
16875
|
+
INSERT OR IGNORE INTO connected_account_charge_transfers (
|
|
16876
|
+
user_id, period_start, from_connection_id, to_connection_id, provider_config_key
|
|
16877
|
+
)
|
|
16878
|
+
SELECT ?, ?, previous.id, replacement.id, replacement.provider_config_key
|
|
16879
|
+
FROM service_connections AS replacement
|
|
16880
|
+
JOIN service_connections AS previous
|
|
16881
|
+
ON previous.user_id = replacement.user_id
|
|
16882
|
+
AND previous.transport = 'nango'
|
|
16883
|
+
AND previous.provider_config_key = replacement.provider_config_key
|
|
16884
|
+
AND previous.lifecycle_status = 'disconnected'
|
|
16885
|
+
JOIN connected_account_charges AS charge
|
|
16886
|
+
ON charge.user_id = previous.user_id
|
|
16887
|
+
AND charge.connection_id = previous.id
|
|
16888
|
+
AND charge.period_start = ?
|
|
16889
|
+
WHERE replacement.id = ?
|
|
16890
|
+
AND replacement.user_id = ?
|
|
16891
|
+
AND replacement.transport = 'nango'
|
|
16892
|
+
AND replacement.lifecycle_status IN ('connected', 'needs_reauth')
|
|
16893
|
+
AND NOT EXISTS (
|
|
16894
|
+
SELECT 1 FROM connected_account_charges AS existing
|
|
16895
|
+
WHERE existing.user_id = replacement.user_id
|
|
16896
|
+
AND existing.connection_id = replacement.id
|
|
16897
|
+
AND existing.period_start = ?
|
|
16898
|
+
)
|
|
16899
|
+
ORDER BY previous.updated_at DESC, previous.id ASC
|
|
16900
|
+
LIMIT 1
|
|
16901
|
+
`,
|
|
16902
|
+
args: [normalizedUserId, periodStart, periodStart, connectionId, normalizedUserId, periodStart]
|
|
16903
|
+
},
|
|
16904
|
+
{
|
|
16905
|
+
sql: `
|
|
16906
|
+
UPDATE connected_account_charges
|
|
16907
|
+
SET connection_id = ?
|
|
16908
|
+
WHERE user_id = ?
|
|
16909
|
+
AND period_start = ?
|
|
16910
|
+
AND connection_id = (
|
|
16911
|
+
SELECT transfer.from_connection_id
|
|
16912
|
+
FROM connected_account_charge_transfers AS transfer
|
|
16913
|
+
JOIN connected_account_charges AS current_charge
|
|
16914
|
+
ON current_charge.user_id = transfer.user_id
|
|
16915
|
+
AND current_charge.period_start = transfer.period_start
|
|
16916
|
+
AND current_charge.connection_id = transfer.from_connection_id
|
|
16917
|
+
WHERE transfer.user_id = ?
|
|
16918
|
+
AND transfer.period_start = ?
|
|
16919
|
+
AND transfer.to_connection_id = ?
|
|
16920
|
+
ORDER BY transfer.transferred_at DESC, transfer.id DESC
|
|
16921
|
+
LIMIT 1
|
|
16922
|
+
)
|
|
16923
|
+
AND NOT EXISTS (
|
|
16924
|
+
SELECT 1 FROM connected_account_charges AS existing
|
|
16925
|
+
WHERE existing.user_id = ?
|
|
16926
|
+
AND existing.connection_id = ?
|
|
16927
|
+
AND existing.period_start = ?
|
|
16928
|
+
)
|
|
16929
|
+
`,
|
|
16930
|
+
args: [
|
|
16931
|
+
connectionId,
|
|
16932
|
+
normalizedUserId,
|
|
16933
|
+
periodStart,
|
|
16934
|
+
normalizedUserId,
|
|
16935
|
+
periodStart,
|
|
16936
|
+
connectionId,
|
|
16937
|
+
normalizedUserId,
|
|
16938
|
+
connectionId,
|
|
16939
|
+
periodStart
|
|
16940
|
+
]
|
|
16941
|
+
}
|
|
16942
|
+
], "write");
|
|
16943
|
+
const inherited = await db.execute({
|
|
16944
|
+
sql: "SELECT 1 FROM connected_account_charges WHERE user_id = ? AND connection_id = ? AND period_start = ? LIMIT 1",
|
|
16945
|
+
args: [normalizedUserId, connectionId, periodStart]
|
|
16946
|
+
});
|
|
16947
|
+
return inherited.rows.length > 0;
|
|
16948
|
+
}
|
|
16524
16949
|
async function getConnectedAccountBillingState(userId) {
|
|
16525
16950
|
const result = await getDb().execute({
|
|
16526
16951
|
sql: "SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",
|
|
@@ -16661,6 +17086,7 @@ async function reconcileConnectedAccountBilling(user, connections) {
|
|
|
16661
17086
|
const alreadyCharged = await chargedConnectionIds(user.id, periodStart);
|
|
16662
17087
|
const unpaid = billableConnectionIds.filter((id) => !alreadyCharged.has(id));
|
|
16663
17088
|
for (const connectionId of unpaid) {
|
|
17089
|
+
if (await inheritDisconnectedConnectionCharge(user.id, connectionId, periodStart)) continue;
|
|
16664
17090
|
const feeMc = CONNECTED_ACTIVE_CONNECTION_MONTHLY_MC;
|
|
16665
17091
|
const debit = await debitMcIdempotent(
|
|
16666
17092
|
user.id,
|
|
@@ -19752,7 +20178,7 @@ var adminAuth = createMiddleware3(async (c, next) => {
|
|
|
19752
20178
|
import { Hono as Hono9 } from "hono";
|
|
19753
20179
|
|
|
19754
20180
|
// src/api/admin-credits.ts
|
|
19755
|
-
import { randomUUID as
|
|
20181
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
19756
20182
|
var MIN_ADJUSTMENT_CREDITS = 1;
|
|
19757
20183
|
var LARGE_ADJUSTMENT_CREDITS = 1e5;
|
|
19758
20184
|
var MAX_ADJUSTMENT_CREDITS = 1e7;
|
|
@@ -19902,7 +20328,7 @@ async function applyAdjustment(input) {
|
|
|
19902
20328
|
const credits = validateCredits(input.credits, input.confirmLarge === true);
|
|
19903
20329
|
const reason = validateReason(input.reason);
|
|
19904
20330
|
const actor = validateActor(input.actor);
|
|
19905
|
-
const reference = input.reference?.trim() || `adj_${
|
|
20331
|
+
const reference = input.reference?.trim() || `adj_${randomUUID7()}`;
|
|
19906
20332
|
if (reference.length > 120) throw new AdminCreditError(400, "reference must be at most 120 characters");
|
|
19907
20333
|
const db = getDb();
|
|
19908
20334
|
const existing = await db.execute({
|
|
@@ -22509,17 +22935,17 @@ g2App.post("/reviews", createApiKeyAuth(), async (c) => {
|
|
|
22509
22935
|
});
|
|
22510
22936
|
|
|
22511
22937
|
// src/api/directory-routes.ts
|
|
22512
|
-
import { createHash as createHash5, randomUUID as
|
|
22938
|
+
import { createHash as createHash5, randomUUID as randomUUID9 } from "crypto";
|
|
22513
22939
|
import { Hono as Hono16 } from "hono";
|
|
22514
22940
|
|
|
22515
22941
|
// src/api/directory-workflow-dispatch.ts
|
|
22516
|
-
import { randomUUID as
|
|
22942
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
22517
22943
|
function retryDelaySeconds(attempts) {
|
|
22518
22944
|
return Math.min(60 * 60, Math.max(15, 15 * 2 ** Math.min(8, Math.max(0, attempts - 1))));
|
|
22519
22945
|
}
|
|
22520
22946
|
async function dispatchPendingDirectoryWorkflows(limit = 25) {
|
|
22521
22947
|
const rows = await claimDirectoryWorkflowOutbox({
|
|
22522
|
-
workerId: `directory-dispatch-${process.pid}-${
|
|
22948
|
+
workerId: `directory-dispatch-${process.pid}-${randomUUID8().slice(0, 8)}`,
|
|
22523
22949
|
limit
|
|
22524
22950
|
});
|
|
22525
22951
|
const result = { claimed: rows.length, dispatched: 0, failed: 0 };
|
|
@@ -22558,14 +22984,14 @@ function idempotencyKey(raw) {
|
|
|
22558
22984
|
if (trimmed.length > 500) return { ok: false, message: "Idempotency-Key must be 500 characters or fewer." };
|
|
22559
22985
|
return { ok: true, key: trimmed };
|
|
22560
22986
|
}
|
|
22561
|
-
return { ok: true, key: `directory-${
|
|
22987
|
+
return { ok: true, key: `directory-${randomUUID9()}` };
|
|
22562
22988
|
}
|
|
22563
22989
|
function debitKeyFor(userId, responseKey) {
|
|
22564
22990
|
const digest2 = createHash5("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
|
|
22565
22991
|
return `directory-workflow:${userId}:${digest2}`;
|
|
22566
22992
|
}
|
|
22567
22993
|
function jobId() {
|
|
22568
|
-
return `dir_${
|
|
22994
|
+
return `dir_${randomUUID9().replace(/-/g, "")}`;
|
|
22569
22995
|
}
|
|
22570
22996
|
function publicStatus(job, result) {
|
|
22571
22997
|
if (job.status === "failed") return "failed";
|
|
@@ -22879,8 +23305,8 @@ import { homedir as homedir3 } from "os";
|
|
|
22879
23305
|
import { join as join7 } from "path";
|
|
22880
23306
|
|
|
22881
23307
|
// src/api/lead-list-import-repository.ts
|
|
22882
|
-
import { randomUUID as
|
|
22883
|
-
var
|
|
23308
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
23309
|
+
var schemaDb4 = null;
|
|
22884
23310
|
var schemaPromise3 = null;
|
|
22885
23311
|
function parseJson3(value, fallback) {
|
|
22886
23312
|
if (value == null) return fallback;
|
|
@@ -22945,15 +23371,15 @@ function bounded(value, field, min, max) {
|
|
|
22945
23371
|
return normalized;
|
|
22946
23372
|
}
|
|
22947
23373
|
function newLeadListUploadId() {
|
|
22948
|
-
return `upl_${
|
|
23374
|
+
return `upl_${randomUUID10().replace(/-/g, "")}`;
|
|
22949
23375
|
}
|
|
22950
23376
|
function newImportedLeadListId() {
|
|
22951
|
-
return `lst_${
|
|
23377
|
+
return `lst_${randomUUID10().replace(/-/g, "")}`;
|
|
22952
23378
|
}
|
|
22953
23379
|
async function ensureLeadListImportRepositorySchema() {
|
|
22954
23380
|
const db = getDb();
|
|
22955
|
-
if (
|
|
22956
|
-
|
|
23381
|
+
if (schemaDb4 !== db) {
|
|
23382
|
+
schemaDb4 = db;
|
|
22957
23383
|
schemaPromise3 = null;
|
|
22958
23384
|
}
|
|
22959
23385
|
if (!schemaPromise3) {
|
|
@@ -24090,17 +24516,17 @@ leadListInputApp.post("/imports", async (c) => {
|
|
|
24090
24516
|
});
|
|
24091
24517
|
|
|
24092
24518
|
// src/api/lead-list-enrichment-routes.ts
|
|
24093
|
-
import { createHash as createHash8, randomUUID as
|
|
24519
|
+
import { createHash as createHash8, randomUUID as randomUUID12 } from "crypto";
|
|
24094
24520
|
import { Hono as Hono18 } from "hono";
|
|
24095
24521
|
import { z as z25 } from "zod";
|
|
24096
24522
|
|
|
24097
24523
|
// src/api/lead-list-enrichment-dispatch.ts
|
|
24098
|
-
import { randomUUID as
|
|
24524
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
24099
24525
|
function retryDelaySeconds2(attempts) {
|
|
24100
24526
|
return Math.min(3600, Math.max(15, 15 * 2 ** Math.min(8, Math.max(0, attempts - 1))));
|
|
24101
24527
|
}
|
|
24102
24528
|
async function dispatchPendingLeadListEnrichments(limit = 25) {
|
|
24103
|
-
const rows = await claimLeadListEnrichmentOutbox({ workerId: `lead-list-dispatch-${process.pid}-${
|
|
24529
|
+
const rows = await claimLeadListEnrichmentOutbox({ workerId: `lead-list-dispatch-${process.pid}-${randomUUID11().slice(0, 8)}`, limit });
|
|
24104
24530
|
const result = { claimed: rows.length, dispatched: 0, failed: 0 };
|
|
24105
24531
|
for (const row of rows) {
|
|
24106
24532
|
if (!row.claimToken) continue;
|
|
@@ -24148,7 +24574,7 @@ function idempotencyKey2(raw) {
|
|
|
24148
24574
|
return { ok: true, value };
|
|
24149
24575
|
}
|
|
24150
24576
|
function newJobId() {
|
|
24151
|
-
return `lle_${
|
|
24577
|
+
return `lle_${randomUUID12().replace(/-/g, "")}`;
|
|
24152
24578
|
}
|
|
24153
24579
|
function debitKeyFor2(userId, key) {
|
|
24154
24580
|
const digest2 = createHash8("sha256").update(String(userId)).update("\0").update(key).digest("hex");
|
|
@@ -26158,7 +26584,7 @@ workflowApp.post("/cron/dispatch", async (c) => {
|
|
|
26158
26584
|
|
|
26159
26585
|
// src/api/serp-intelligence-routes.ts
|
|
26160
26586
|
import { Hono as Hono22 } from "hono";
|
|
26161
|
-
import { createHash as createHash10, randomUUID as
|
|
26587
|
+
import { createHash as createHash10, randomUUID as randomUUID13 } from "crypto";
|
|
26162
26588
|
|
|
26163
26589
|
// src/serp-intelligence/page-snapshot-extractor.ts
|
|
26164
26590
|
import { createHash as createHash9 } from "crypto";
|
|
@@ -26944,7 +27370,7 @@ async function logRequestEventBestEffort(input) {
|
|
|
26944
27370
|
}
|
|
26945
27371
|
}
|
|
26946
27372
|
function captureBillingKeys(userId, suppliedKey, body) {
|
|
26947
|
-
const responseKey = suppliedKey?.trim() ||
|
|
27373
|
+
const responseKey = suppliedKey?.trim() || randomUUID13();
|
|
26948
27374
|
const requestFingerprint3 = createHash10("sha256").update(JSON.stringify(body)).digest("hex");
|
|
26949
27375
|
const keyDigest = createHash10("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
|
|
26950
27376
|
return {
|
|
@@ -27274,7 +27700,7 @@ var MemoryMcpToolExecutor = class {
|
|
|
27274
27700
|
};
|
|
27275
27701
|
|
|
27276
27702
|
// src/api/image-source-artifacts.ts
|
|
27277
|
-
import { randomUUID as
|
|
27703
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
27278
27704
|
var IMAGE_SOURCE_ARTIFACT_PREFIX = "image-sources/";
|
|
27279
27705
|
var IMAGE_SOURCE_ARTIFACT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
27280
27706
|
var IMAGE_SOURCE_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
|
|
@@ -27305,7 +27731,7 @@ async function createImageSourceArtifact(args) {
|
|
|
27305
27731
|
if (args.content.length === 0 || args.content.length > IMAGE_SOURCE_MAX_BYTES) {
|
|
27306
27732
|
throw new Error("image_source_size_invalid");
|
|
27307
27733
|
}
|
|
27308
|
-
const id =
|
|
27734
|
+
const id = randomUUID14().replaceAll("-", "");
|
|
27309
27735
|
return createPrivateArtifact({
|
|
27310
27736
|
policy: policy3(),
|
|
27311
27737
|
ownerId: args.ownerId,
|
|
@@ -28050,11 +28476,11 @@ mcpApp.all("/", async (c) => {
|
|
|
28050
28476
|
});
|
|
28051
28477
|
|
|
28052
28478
|
// src/api/browser-agent-routes.ts
|
|
28053
|
-
import { randomUUID as
|
|
28479
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
28054
28480
|
import { Hono as Hono24 } from "hono";
|
|
28055
28481
|
|
|
28056
28482
|
// src/api/browser-agent-db.ts
|
|
28057
|
-
import { randomUUID as
|
|
28483
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
28058
28484
|
var _ready = false;
|
|
28059
28485
|
var _migrationPromise = null;
|
|
28060
28486
|
async function migrateBrowserAgent() {
|
|
@@ -28185,7 +28611,7 @@ async function runBrowserAgentMigration() {
|
|
|
28185
28611
|
}
|
|
28186
28612
|
async function createExtensionRow(input) {
|
|
28187
28613
|
const db = getDb();
|
|
28188
|
-
const id = `bext_${
|
|
28614
|
+
const id = `bext_${randomUUID15().replace(/-/g, "").slice(0, 20)}`;
|
|
28189
28615
|
await db.execute({
|
|
28190
28616
|
sql: `INSERT INTO browser_agent_extensions (id, user_id, name, backend_id, backend_name, source, source_url, size_bytes)
|
|
28191
28617
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -28220,7 +28646,7 @@ async function deleteExtensionRow(userId, name) {
|
|
|
28220
28646
|
}
|
|
28221
28647
|
async function createAuthConnectionRow(input) {
|
|
28222
28648
|
const db = getDb();
|
|
28223
|
-
const connectionId = `authc_${
|
|
28649
|
+
const connectionId = `authc_${randomUUID15().replace(/-/g, "").slice(0, 20)}`;
|
|
28224
28650
|
await db.execute({
|
|
28225
28651
|
sql: `INSERT INTO browser_auth_connections (connection_id, domain, profile, account_email, note, status, browser_agent_session_id)
|
|
28226
28652
|
VALUES (?, ?, ?, ?, ?, 'NEEDS_AUTH', ?)`,
|
|
@@ -28305,7 +28731,7 @@ async function deleteProfileLabel(userId, profile) {
|
|
|
28305
28731
|
}
|
|
28306
28732
|
async function createSessionRow(input) {
|
|
28307
28733
|
const db = getDb();
|
|
28308
|
-
const id = `bas_${
|
|
28734
|
+
const id = `bas_${randomUUID15().replace(/-/g, "").slice(0, 20)}`;
|
|
28309
28735
|
await db.execute({
|
|
28310
28736
|
sql: `INSERT INTO browser_agent_sessions (id, runtime_session_id, live_view_url, cdp_ws_url, status, label, user_id, concurrency_lock_id, last_action_at)
|
|
28311
28737
|
VALUES (?, ?, ?, ?, 'open', ?, ?, ?, datetime('now'))`,
|
|
@@ -28383,7 +28809,7 @@ async function recordAction(input) {
|
|
|
28383
28809
|
sql: `INSERT INTO browser_agent_actions (id, session_id, type, params_json, ok, error)
|
|
28384
28810
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
28385
28811
|
args: [
|
|
28386
|
-
`baa_${
|
|
28812
|
+
`baa_${randomUUID15().replace(/-/g, "").slice(0, 20)}`,
|
|
28387
28813
|
input.sessionId,
|
|
28388
28814
|
input.type,
|
|
28389
28815
|
input.params == null ? null : JSON.stringify(input.params),
|
|
@@ -29494,9 +29920,9 @@ async function replayList(runtimeSessionId) {
|
|
|
29494
29920
|
}
|
|
29495
29921
|
|
|
29496
29922
|
// src/api/serp-identity-db.ts
|
|
29497
|
-
import { randomUUID as
|
|
29923
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
29498
29924
|
async function createSerpIdentityRow(input) {
|
|
29499
|
-
const id = `serpi_${
|
|
29925
|
+
const id = `serpi_${randomUUID16().replace(/-/g, "").slice(0, 20)}`;
|
|
29500
29926
|
await getDb().execute({
|
|
29501
29927
|
sql: `INSERT INTO serp_identities
|
|
29502
29928
|
(id, user_id, name, kernel_profile_name, kernel_proxy_id, proxy_type, country, status)
|
|
@@ -29543,7 +29969,7 @@ async function deleteSerpIdentityRow(userId, name) {
|
|
|
29543
29969
|
}
|
|
29544
29970
|
|
|
29545
29971
|
// src/api/serp-identity-service.ts
|
|
29546
|
-
import { createHash as createHash11, randomUUID as
|
|
29972
|
+
import { createHash as createHash11, randomUUID as randomUUID17 } from "crypto";
|
|
29547
29973
|
import Kernel5 from "@onkernel/sdk";
|
|
29548
29974
|
var MAX_SERP_IDENTITIES_PER_USER = 3;
|
|
29549
29975
|
function kernelClient() {
|
|
@@ -29553,7 +29979,7 @@ function kernelClient() {
|
|
|
29553
29979
|
}
|
|
29554
29980
|
function backendName(userId, name, resource) {
|
|
29555
29981
|
const digest2 = createHash11("sha256").update(`${userId}:${name}`).digest("hex").slice(0, 16);
|
|
29556
|
-
const nonce =
|
|
29982
|
+
const nonce = randomUUID17().replace(/-/g, "").slice(0, 8);
|
|
29557
29983
|
return `mcp-serp-${resource}-${digest2}-${nonce}`;
|
|
29558
29984
|
}
|
|
29559
29985
|
function isNotFound2(error) {
|
|
@@ -30339,7 +30765,7 @@ function buildBrowserAgentRoutes() {
|
|
|
30339
30765
|
}
|
|
30340
30766
|
const existing = await getExtensionRow(user.id, name);
|
|
30341
30767
|
if (existing) return c.json({ error: `an extension named "${name}" already exists \u2014 delete it first or pick another name` }, 409);
|
|
30342
|
-
const backendName2 = `u${user.id}_${
|
|
30768
|
+
const backendName2 = `u${user.id}_${randomUUID18().replace(/-/g, "")}`;
|
|
30343
30769
|
try {
|
|
30344
30770
|
const imported = await importExtensionFromStore(storeUrl, backendName2);
|
|
30345
30771
|
const row = await createExtensionRow({
|
|
@@ -30892,7 +31318,7 @@ async function resolveUser3(customerId, emailFallback) {
|
|
|
30892
31318
|
// src/api/oauth-routes.ts
|
|
30893
31319
|
import { Hono as Hono26 } from "hono";
|
|
30894
31320
|
import { getCookie, setCookie } from "hono/cookie";
|
|
30895
|
-
import { createHash as createHash12, randomBytes as randomBytes3, randomUUID as
|
|
31321
|
+
import { createHash as createHash12, randomBytes as randomBytes3, randomUUID as randomUUID19 } from "crypto";
|
|
30896
31322
|
import { importPKCS8, exportJWK, SignJWT } from "jose";
|
|
30897
31323
|
var ISSUER = "https://mcpscraper.dev";
|
|
30898
31324
|
var RESOURCE = () => process.env.MCP_MEMORY_RESOURCE_URL ?? "https://memory.mcpscraper.dev/mcp";
|
|
@@ -31277,7 +31703,7 @@ function pkceMatches(verifier, challenge) {
|
|
|
31277
31703
|
}
|
|
31278
31704
|
async function mintAccessToken(identity, scope, plan, audience) {
|
|
31279
31705
|
const { privateKey, kid } = await getKeys();
|
|
31280
|
-
return new SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti(
|
|
31706
|
+
return new SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti(randomUUID19()).setExpirationTime(`${ACCESS_TTL_SECONDS}s`).sign(privateKey);
|
|
31281
31707
|
}
|
|
31282
31708
|
function tokenErrorResponse(c, error, description, status) {
|
|
31283
31709
|
return c.json({ error, error_description: description }, status);
|
|
@@ -33727,7 +34153,7 @@ function renderWorkshopFieldNotesBody() {
|
|
|
33727
34153
|
}
|
|
33728
34154
|
|
|
33729
34155
|
// src/api/workshop-registration-repository.ts
|
|
33730
|
-
import { randomUUID as
|
|
34156
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
33731
34157
|
function rowToRegistration(row) {
|
|
33732
34158
|
return {
|
|
33733
34159
|
id: String(row.id),
|
|
@@ -33796,7 +34222,7 @@ async function claimWorkshopRegistration(input) {
|
|
|
33796
34222
|
) < ?
|
|
33797
34223
|
`,
|
|
33798
34224
|
args: [
|
|
33799
|
-
|
|
34225
|
+
randomUUID20(),
|
|
33800
34226
|
input.eventSlug,
|
|
33801
34227
|
input.email,
|
|
33802
34228
|
input.firstName,
|
|
@@ -33813,7 +34239,7 @@ async function claimWorkshopRegistration(input) {
|
|
|
33813
34239
|
id, event_slug, email, first_name, last_name, status
|
|
33814
34240
|
) VALUES (?, ?, ?, ?, ?, 'waitlisted')
|
|
33815
34241
|
`,
|
|
33816
|
-
args: [
|
|
34242
|
+
args: [randomUUID20(), input.eventSlug, input.email, input.firstName, input.lastName || null]
|
|
33817
34243
|
});
|
|
33818
34244
|
const waitlisted = await getWorkshopRegistration(input.eventSlug, input.email);
|
|
33819
34245
|
if (!waitlisted) throw new Error("workshop registration could not be claimed");
|
|
@@ -33828,7 +34254,7 @@ async function registerWorkshopInterest(input) {
|
|
|
33828
34254
|
id, event_slug, email, first_name, last_name, status
|
|
33829
34255
|
) VALUES (?, ?, ?, ?, ?, 'interested')
|
|
33830
34256
|
`,
|
|
33831
|
-
args: [
|
|
34257
|
+
args: [randomUUID20(), input.eventSlug, input.email, input.firstName, input.lastName || null]
|
|
33832
34258
|
});
|
|
33833
34259
|
const registration = await getWorkshopRegistration(input.eventSlug, input.email);
|
|
33834
34260
|
if (!registration) throw new Error("workshop interest could not be recorded");
|
|
@@ -34631,7 +35057,7 @@ import { Hono as Hono33 } from "hono";
|
|
|
34631
35057
|
import { z as z33 } from "zod";
|
|
34632
35058
|
|
|
34633
35059
|
// src/api/commons-repository.ts
|
|
34634
|
-
import { createHash as createHash17, randomUUID as
|
|
35060
|
+
import { createHash as createHash17, randomUUID as randomUUID21 } from "crypto";
|
|
34635
35061
|
|
|
34636
35062
|
// src/api/commons-image-store.ts
|
|
34637
35063
|
import { createHash as createHash15 } from "crypto";
|
|
@@ -34640,20 +35066,27 @@ import { isIP } from "net";
|
|
|
34640
35066
|
var COMMONS_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
34641
35067
|
var COMMONS_IMAGE_MAX_REDIRECTS = 3;
|
|
34642
35068
|
var COMMONS_IMAGE_FETCH_TIMEOUT_MS = 2e4;
|
|
35069
|
+
var COMMONS_IMAGE_RETRY_GUIDANCE = "Call commons_host_image with a different stable public HTTPS image URL or imageBase64, replace featuredImage.url with the returned URL, validate again, then retry commons_submit_entity with the same idempotency key.";
|
|
34643
35070
|
var CommonsImageError = class extends Error {
|
|
34644
|
-
constructor(code, message, httpStatus = 400) {
|
|
35071
|
+
constructor(code, message, httpStatus = 400, retryable = false, retryGuidance = COMMONS_IMAGE_RETRY_GUIDANCE) {
|
|
34645
35072
|
super(message);
|
|
34646
35073
|
this.code = code;
|
|
34647
35074
|
this.httpStatus = httpStatus;
|
|
35075
|
+
this.retryable = retryable;
|
|
35076
|
+
this.retryGuidance = retryGuidance;
|
|
34648
35077
|
this.name = "CommonsImageError";
|
|
34649
35078
|
}
|
|
34650
35079
|
code;
|
|
34651
35080
|
httpStatus;
|
|
35081
|
+
retryable;
|
|
35082
|
+
retryGuidance;
|
|
34652
35083
|
};
|
|
34653
35084
|
var imageSchemaReady = false;
|
|
35085
|
+
var imageSchemaDb = null;
|
|
34654
35086
|
async function ensureCommonsImageSchema() {
|
|
34655
|
-
|
|
34656
|
-
|
|
35087
|
+
const db = getDb();
|
|
35088
|
+
if (imageSchemaReady && imageSchemaDb === db) return;
|
|
35089
|
+
await db.execute(`
|
|
34657
35090
|
CREATE TABLE IF NOT EXISTS commons_images (
|
|
34658
35091
|
id TEXT PRIMARY KEY,
|
|
34659
35092
|
user_id INTEGER NOT NULL,
|
|
@@ -34669,8 +35102,9 @@ async function ensureCommonsImageSchema() {
|
|
|
34669
35102
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
34670
35103
|
)
|
|
34671
35104
|
`);
|
|
34672
|
-
await
|
|
35105
|
+
await db.execute(`CREATE UNIQUE INDEX IF NOT EXISTS commons_images_digest ON commons_images(digest)`);
|
|
34673
35106
|
imageSchemaReady = true;
|
|
35107
|
+
imageSchemaDb = db;
|
|
34674
35108
|
}
|
|
34675
35109
|
function sniffImageFormat(bytes) {
|
|
34676
35110
|
if (bytes.length < 12) return null;
|
|
@@ -34700,7 +35134,7 @@ async function assertPublicHttpsUrl(rawUrl) {
|
|
|
34700
35134
|
try {
|
|
34701
35135
|
url = new URL(rawUrl);
|
|
34702
35136
|
} catch {
|
|
34703
|
-
throw new CommonsImageError("image_url_invalid",
|
|
35137
|
+
throw new CommonsImageError("image_url_invalid", "The image source is not a valid URL.");
|
|
34704
35138
|
}
|
|
34705
35139
|
if (url.protocol !== "https:") {
|
|
34706
35140
|
throw new CommonsImageError("image_url_not_https", "Commons image sources must be https URLs.");
|
|
@@ -34708,7 +35142,7 @@ async function assertPublicHttpsUrl(rawUrl) {
|
|
|
34708
35142
|
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
34709
35143
|
const addresses = isIP(hostname) ? [{ address: hostname }] : await dnsLookup(hostname, { all: true }).catch(() => []);
|
|
34710
35144
|
if (!addresses.length) {
|
|
34711
|
-
throw new CommonsImageError("image_host_unresolvable",
|
|
35145
|
+
throw new CommonsImageError("image_host_unresolvable", "The image source host could not be resolved.");
|
|
34712
35146
|
}
|
|
34713
35147
|
if (addresses.some((entry) => isBlockedAddress(entry.address))) {
|
|
34714
35148
|
throw new CommonsImageError("image_host_blocked", "Commons image sources cannot resolve to private or loopback addresses.");
|
|
@@ -34719,15 +35153,36 @@ async function downloadImage(sourceUrl) {
|
|
|
34719
35153
|
let current = sourceUrl;
|
|
34720
35154
|
for (let redirect = 0; redirect <= COMMONS_IMAGE_MAX_REDIRECTS; redirect += 1) {
|
|
34721
35155
|
const url = await assertPublicHttpsUrl(current);
|
|
34722
|
-
|
|
35156
|
+
let response;
|
|
35157
|
+
try {
|
|
35158
|
+
response = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(COMMONS_IMAGE_FETCH_TIMEOUT_MS) });
|
|
35159
|
+
} catch (error) {
|
|
35160
|
+
if (error instanceof CommonsImageError) throw error;
|
|
35161
|
+
throw new CommonsImageError(
|
|
35162
|
+
"image_source_unavailable",
|
|
35163
|
+
"The image source could not be reached by Commons.",
|
|
35164
|
+
422,
|
|
35165
|
+
true
|
|
35166
|
+
);
|
|
35167
|
+
}
|
|
34723
35168
|
if (response.status >= 300 && response.status < 400) {
|
|
34724
35169
|
const location2 = response.headers.get("location");
|
|
34725
35170
|
if (!location2) throw new CommonsImageError("image_redirect_invalid", "Image source redirected without a destination.");
|
|
34726
|
-
|
|
35171
|
+
try {
|
|
35172
|
+
current = new URL(location2, url).toString();
|
|
35173
|
+
} catch {
|
|
35174
|
+
throw new CommonsImageError("image_redirect_invalid", "Image source redirected to an invalid destination.");
|
|
35175
|
+
}
|
|
34727
35176
|
continue;
|
|
34728
35177
|
}
|
|
34729
35178
|
if (!response.ok) {
|
|
34730
|
-
|
|
35179
|
+
const retryable = response.status === 408 || response.status === 425 || response.status === 429 || response.status >= 500;
|
|
35180
|
+
throw new CommonsImageError(
|
|
35181
|
+
"image_source_unavailable",
|
|
35182
|
+
`The image source returned HTTP ${response.status} and could not be hosted.`,
|
|
35183
|
+
422,
|
|
35184
|
+
retryable
|
|
35185
|
+
);
|
|
34731
35186
|
}
|
|
34732
35187
|
const declaredLength = Number(response.headers.get("content-length") ?? "0");
|
|
34733
35188
|
if (declaredLength > COMMONS_IMAGE_MAX_BYTES) {
|
|
@@ -34754,8 +35209,35 @@ function commonsImageBlobStore() {
|
|
|
34754
35209
|
const token6 = process.env.COMMONS_IMAGE_READ_WRITE_TOKEN?.trim() || process.env.COMMONS_IMAGE_BLOB_READ_WRITE_TOKEN?.trim() || process.env.BLOB_READ_WRITE_TOKEN?.trim();
|
|
34755
35210
|
return token6 ? new VercelBlobStore(token6) : getBlobStore();
|
|
34756
35211
|
}
|
|
34757
|
-
function
|
|
34758
|
-
|
|
35212
|
+
function normalizeCommonsImageUrl(rawUrl) {
|
|
35213
|
+
try {
|
|
35214
|
+
const url = new URL(rawUrl.trim());
|
|
35215
|
+
if (url.protocol !== "https:") return null;
|
|
35216
|
+
url.hash = "";
|
|
35217
|
+
return url.toString();
|
|
35218
|
+
} catch {
|
|
35219
|
+
return null;
|
|
35220
|
+
}
|
|
35221
|
+
}
|
|
35222
|
+
async function findCommonsHostedImageByUrl(rawUrl) {
|
|
35223
|
+
const url = normalizeCommonsImageUrl(rawUrl);
|
|
35224
|
+
if (!url) return null;
|
|
35225
|
+
await ensureCommonsImageSchema();
|
|
35226
|
+
const result = await getDb().execute({
|
|
35227
|
+
sql: "SELECT id, url, content_type, bytes, digest, source_url FROM commons_images WHERE url = ? LIMIT 1",
|
|
35228
|
+
args: [url]
|
|
35229
|
+
});
|
|
35230
|
+
const row = result.rows[0];
|
|
35231
|
+
if (!row) return null;
|
|
35232
|
+
return {
|
|
35233
|
+
id: String(row.id),
|
|
35234
|
+
url: String(row.url),
|
|
35235
|
+
contentType: String(row.content_type),
|
|
35236
|
+
bytes: Number(row.bytes),
|
|
35237
|
+
digest: String(row.digest),
|
|
35238
|
+
sourceUrl: row.source_url ? String(row.source_url) : null,
|
|
35239
|
+
reused: true
|
|
35240
|
+
};
|
|
34759
35241
|
}
|
|
34760
35242
|
var hostOverride = null;
|
|
34761
35243
|
async function hostCommonsImage(input) {
|
|
@@ -34827,7 +35309,8 @@ async function hostCommonsImage(input) {
|
|
|
34827
35309
|
async function hostEntityImages(input) {
|
|
34828
35310
|
const rewrite = { mediaUrls: /* @__PURE__ */ new Map(), dropped: [] };
|
|
34829
35311
|
const featuredUrl = input.featuredImage?.url;
|
|
34830
|
-
|
|
35312
|
+
const registeredFeatured = featuredUrl ? await findCommonsHostedImageByUrl(featuredUrl) : null;
|
|
35313
|
+
if (featuredUrl && !registeredFeatured) {
|
|
34831
35314
|
const hosted = await hostCommonsImage({
|
|
34832
35315
|
userId: input.userId,
|
|
34833
35316
|
sourceUrl: featuredUrl,
|
|
@@ -34835,17 +35318,19 @@ async function hostEntityImages(input) {
|
|
|
34835
35318
|
license: input.featuredImage?.license,
|
|
34836
35319
|
attribution: input.featuredImage?.sourceUrl
|
|
34837
35320
|
}).catch((error) => {
|
|
34838
|
-
|
|
35321
|
+
if (error instanceof CommonsImageError) throw error;
|
|
34839
35322
|
throw new CommonsImageError(
|
|
34840
|
-
|
|
34841
|
-
|
|
35323
|
+
"featured_image_unhostable",
|
|
35324
|
+
"The featured image could not be hosted, so the entity was not persisted with a link that could break.",
|
|
34842
35325
|
422
|
|
34843
35326
|
);
|
|
34844
35327
|
});
|
|
34845
35328
|
rewrite.featuredImageUrl = hosted.url;
|
|
35329
|
+
rewrite.mediaUrls.set(featuredUrl, hosted.url);
|
|
34846
35330
|
}
|
|
34847
35331
|
for (const item of input.media ?? []) {
|
|
34848
|
-
if (
|
|
35332
|
+
if (item.url && rewrite.mediaUrls.has(item.url)) continue;
|
|
35333
|
+
if (!item.url || await findCommonsHostedImageByUrl(item.url)) continue;
|
|
34849
35334
|
try {
|
|
34850
35335
|
const hosted = await hostCommonsImage({
|
|
34851
35336
|
userId: input.userId,
|
|
@@ -35213,7 +35698,7 @@ var COMMONS_ENTITY_PROFILES = {
|
|
|
35213
35698
|
]
|
|
35214
35699
|
}
|
|
35215
35700
|
};
|
|
35216
|
-
var
|
|
35701
|
+
var schemaReady2 = false;
|
|
35217
35702
|
var COMMONS_SCHEMA_OBJECTS = [
|
|
35218
35703
|
"schema_migrations",
|
|
35219
35704
|
"commons_entities",
|
|
@@ -35240,9 +35725,9 @@ var COMMONS_SCHEMA_OBJECTS = [
|
|
|
35240
35725
|
"commons_publication_editions_publication_published"
|
|
35241
35726
|
];
|
|
35242
35727
|
async function ensureCommonsSchema() {
|
|
35243
|
-
if (
|
|
35728
|
+
if (schemaReady2) return;
|
|
35244
35729
|
if (await schemaObjectsPresent(COMMONS_SCHEMA_OBJECTS)) {
|
|
35245
|
-
|
|
35730
|
+
schemaReady2 = true;
|
|
35246
35731
|
return;
|
|
35247
35732
|
}
|
|
35248
35733
|
const db = getDb();
|
|
@@ -35465,7 +35950,7 @@ async function ensureCommonsSchema() {
|
|
|
35465
35950
|
args: [COMMONS_SCHEMA_VERSION]
|
|
35466
35951
|
}
|
|
35467
35952
|
], "write");
|
|
35468
|
-
|
|
35953
|
+
schemaReady2 = true;
|
|
35469
35954
|
}
|
|
35470
35955
|
async function searchCommonsEntities(filters = {}, userId) {
|
|
35471
35956
|
await ensureCommonsSchema();
|
|
@@ -35674,7 +36159,7 @@ async function prepareCommonsEntity(input, user) {
|
|
|
35674
36159
|
normalizedEntityType: entityType,
|
|
35675
36160
|
proposedSlug: slug4 || null,
|
|
35676
36161
|
publicUrl: slug4 ? publicUrlForSlug(slug4) : null,
|
|
35677
|
-
writePath: "commons_prepare_entity -> commons_validate_entity -> commons_submit_entity"
|
|
36162
|
+
writePath: "commons_prepare_entity -> compose the entity -> commons_host_image -> commons_validate_entity -> commons_submit_entity"
|
|
35678
36163
|
},
|
|
35679
36164
|
contract,
|
|
35680
36165
|
billing: {
|
|
@@ -35696,8 +36181,8 @@ async function prepareCommonsEntity(input, user) {
|
|
|
35696
36181
|
"Search likely duplicates before submitting a new entity. If one matches, edit it with entityId and baseRevision instead of creating another page.",
|
|
35697
36182
|
"Use the returned profile as a contract, not a rigid empty template: do not include History, Reception, Pricing, Timeline, or similar sections unless source evidence supports them.",
|
|
35698
36183
|
"Write neutral encyclopedia prose. Do not paste raw scrape Markdown, marketing layout fragments, or empty/filler headings.",
|
|
35699
|
-
"
|
|
35700
|
-
"Call commons_validate_entity with the composed payload before commons_submit_entity.",
|
|
36184
|
+
"For a featured image not already hosted by Commons, call commons_host_image and replace featuredImage.url with its returned permanent URL. Preserve original URL/source byline/relCanonical when republishing, and put media assets in the media array.",
|
|
36185
|
+
"Call commons_validate_entity with the composed, image-ready payload before commons_submit_entity.",
|
|
35701
36186
|
"Validation is free. Submitting a new entity costs 10 Credits; submitting an edit to an existing entity costs 2 Credits."
|
|
35702
36187
|
]
|
|
35703
36188
|
};
|
|
@@ -35712,6 +36197,8 @@ async function validateCommonsEntity(input, user) {
|
|
|
35712
36197
|
const contract = commonsEntityProfileFor(input.entityType, input.seo?.schemaOrgType);
|
|
35713
36198
|
const source = normalizeSource(input.source);
|
|
35714
36199
|
const featuredImage = normalizeFeaturedImage(input.featuredImage);
|
|
36200
|
+
const normalizedFeaturedImageUrl = featuredImage?.url ? normalizeCommonsImageUrl(featuredImage.url) : null;
|
|
36201
|
+
const registeredFeaturedImage = normalizedFeaturedImageUrl ? await findCommonsHostedImageByUrl(normalizedFeaturedImageUrl) : null;
|
|
35715
36202
|
const bodyMarkdown = safeCandidateBody(input.bodyMarkdown, input.contentSections);
|
|
35716
36203
|
const headings = extractMarkdownHeadings(bodyMarkdown);
|
|
35717
36204
|
const placeholderSections = unsupportedPlaceholderSections(bodyMarkdown);
|
|
@@ -35749,15 +36236,44 @@ async function validateCommonsEntity(input, user) {
|
|
|
35749
36236
|
if (input.reviewPolicy === "always_review") {
|
|
35750
36237
|
warnings.push("reviewPolicy is always_review, so the proposal will not auto-publish even if the payload is otherwise valid.");
|
|
35751
36238
|
}
|
|
36239
|
+
if (featuredImage?.url && !registeredFeaturedImage) {
|
|
36240
|
+
warnings.push("The featured image is not registered in permanent Commons image storage. Call commons_host_image, replace featuredImage.url with its returned URL, and validate again before submitting.");
|
|
36241
|
+
}
|
|
35752
36242
|
if (user?.id == null) warnings.push("No authenticated user was supplied to validation; submit still requires API-key authenticated user context.");
|
|
35753
36243
|
const valid = errors.length === 0;
|
|
35754
|
-
const
|
|
36244
|
+
const imageReadyForPublish = Boolean(registeredFeaturedImage);
|
|
36245
|
+
const publishable = valid && imageReadyForPublish;
|
|
36246
|
+
const autoPublishEligible = publishable && input.reviewPolicy !== "always_review";
|
|
35755
36247
|
const tagVocabulary = await commonsTagVocabulary();
|
|
35756
36248
|
return {
|
|
35757
36249
|
ok: true,
|
|
35758
36250
|
valid,
|
|
35759
|
-
publishable
|
|
36251
|
+
publishable,
|
|
35760
36252
|
autoPublishEligible,
|
|
36253
|
+
imageDiagnostics: {
|
|
36254
|
+
featuredImage: registeredFeaturedImage ? {
|
|
36255
|
+
state: "commons_hosted",
|
|
36256
|
+
url: registeredFeaturedImage.url,
|
|
36257
|
+
commonsImageId: registeredFeaturedImage.id,
|
|
36258
|
+
readyForPublish: true,
|
|
36259
|
+
submitWillAttemptAutoHost: false,
|
|
36260
|
+
requiredAction: null
|
|
36261
|
+
} : featuredImage?.url ? {
|
|
36262
|
+
state: "external_unhosted",
|
|
36263
|
+
url: normalizedFeaturedImageUrl,
|
|
36264
|
+
commonsImageId: null,
|
|
36265
|
+
readyForPublish: false,
|
|
36266
|
+
submitWillAttemptAutoHost: true,
|
|
36267
|
+
requiredAction: "Call commons_host_image, replace featuredImage.url with its returned permanent URL, and validate again."
|
|
36268
|
+
} : {
|
|
36269
|
+
state: "missing",
|
|
36270
|
+
url: null,
|
|
36271
|
+
commonsImageId: null,
|
|
36272
|
+
readyForPublish: false,
|
|
36273
|
+
submitWillAttemptAutoHost: false,
|
|
36274
|
+
requiredAction: "Add a featured image by calling commons_host_image, then set featuredImage.url to its returned permanent URL."
|
|
36275
|
+
}
|
|
36276
|
+
},
|
|
35761
36277
|
errors,
|
|
35762
36278
|
warnings,
|
|
35763
36279
|
contract,
|
|
@@ -35838,7 +36354,7 @@ async function submitCommonsEntity(input, user) {
|
|
|
35838
36354
|
const existingClaims = existing && normalized.claims !== void 0 ? await getCommonsClaimsByEntityId(existing.id) : [];
|
|
35839
36355
|
const safety = evaluatePublishSafety(normalized, existing);
|
|
35840
36356
|
const shouldApply = safety.safe && normalized.reviewPolicy !== "always_review";
|
|
35841
|
-
const proposalId = `commons-proposal-${
|
|
36357
|
+
const proposalId = `commons-proposal-${randomUUID21()}`;
|
|
35842
36358
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
35843
36359
|
const entityId = existing?.id ?? normalized.entityId ?? allocateEntityId();
|
|
35844
36360
|
const proposalStatus = shouldApply ? "accepted" : "pending_review";
|
|
@@ -35893,7 +36409,7 @@ async function submitCommonsEntity(input, user) {
|
|
|
35893
36409
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
35894
36410
|
`,
|
|
35895
36411
|
args: [
|
|
35896
|
-
`commons-ledger-${
|
|
36412
|
+
`commons-ledger-${randomUUID21()}`,
|
|
35897
36413
|
nextEntity.id,
|
|
35898
36414
|
proposalId,
|
|
35899
36415
|
user.id,
|
|
@@ -35999,7 +36515,7 @@ async function saveCommonsFilter(input, user) {
|
|
|
35999
36515
|
sql: "SELECT id FROM commons_saved_filters WHERE user_id = ? AND name = ? LIMIT 1",
|
|
36000
36516
|
args: [user.id, name]
|
|
36001
36517
|
});
|
|
36002
|
-
const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${
|
|
36518
|
+
const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${randomUUID21()}`;
|
|
36003
36519
|
await getDb().execute({
|
|
36004
36520
|
sql: `
|
|
36005
36521
|
INSERT INTO commons_saved_filters (id, user_id, name, description, filter_json, created_at, updated_at)
|
|
@@ -37355,7 +37871,7 @@ var CommonsRepositoryError = class extends Error {
|
|
|
37355
37871
|
};
|
|
37356
37872
|
|
|
37357
37873
|
// src/api/commons-publication-repository.ts
|
|
37358
|
-
import { createHash as createHash18, randomUUID as
|
|
37874
|
+
import { createHash as createHash18, randomUUID as randomUUID22 } from "crypto";
|
|
37359
37875
|
|
|
37360
37876
|
// src/editorial-reading-room/article-page.ts
|
|
37361
37877
|
function escapeHtml6(value) {
|
|
@@ -37568,7 +38084,7 @@ async function claimCommonsPublication(input, user) {
|
|
|
37568
38084
|
}
|
|
37569
38085
|
const existingName = await getCommonsPublicationBySubdomain(subdomain);
|
|
37570
38086
|
if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
|
|
37571
|
-
const id = `tcpub_${
|
|
38087
|
+
const id = `tcpub_${randomUUID22()}`;
|
|
37572
38088
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
37573
38089
|
try {
|
|
37574
38090
|
await getDb().execute({
|
|
@@ -37620,7 +38136,7 @@ async function publishCommonsEditorial(input, user) {
|
|
|
37620
38136
|
const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
|
|
37621
38137
|
const rendered = renderEditorialReadingRoom(editionInput);
|
|
37622
38138
|
const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
|
|
37623
|
-
const editionId = `tced_${
|
|
38139
|
+
const editionId = `tced_${randomUUID22()}`;
|
|
37624
38140
|
const revision = (latest?.revision ?? 0) + 1;
|
|
37625
38141
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
37626
38142
|
await getDb().batch([
|
|
@@ -37702,7 +38218,7 @@ async function updateCommonsEditorialArticle(input, user) {
|
|
|
37702
38218
|
const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
|
|
37703
38219
|
const rendered = renderEditorialReadingRoom(editionInput);
|
|
37704
38220
|
const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
|
|
37705
|
-
const editionId = `tced_${
|
|
38221
|
+
const editionId = `tced_${randomUUID22()}`;
|
|
37706
38222
|
const revision = latest.revision + 1;
|
|
37707
38223
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
37708
38224
|
await getDb().batch([
|
|
@@ -38236,7 +38752,7 @@ commonsApp.post("/images", auth2, async (c) => {
|
|
|
38236
38752
|
return c.json({ ok: true, data: hosted }, hosted.reused ? 200 : 201);
|
|
38237
38753
|
} catch (error) {
|
|
38238
38754
|
if (error instanceof CommonsImageError) {
|
|
38239
|
-
return c
|
|
38755
|
+
return imageErrorResponse(c, error);
|
|
38240
38756
|
}
|
|
38241
38757
|
throw error;
|
|
38242
38758
|
}
|
|
@@ -38300,7 +38816,14 @@ commonsApp.post("/entities/propose", auth2, async (c) => {
|
|
|
38300
38816
|
}
|
|
38301
38817
|
return c.json({ ok: true, data: result, billing: publicListingWriteBilling(billing) }, 201);
|
|
38302
38818
|
} catch (error) {
|
|
38303
|
-
|
|
38819
|
+
let chargeStatus = billing ? billing.duplicate ? "not_charged" : "refund_pending" : "not_charged";
|
|
38820
|
+
if (billing && !billing.duplicate) {
|
|
38821
|
+
const settlement = await refundListingWrite(billing, "Transparent Commons write failed before persistence.").catch(() => null);
|
|
38822
|
+
if (settlement) chargeStatus = "refunded";
|
|
38823
|
+
}
|
|
38824
|
+
if (error instanceof CommonsImageError) {
|
|
38825
|
+
return imageErrorResponse(c, error, chargeStatus);
|
|
38826
|
+
}
|
|
38304
38827
|
if (error instanceof CommonsRepositoryError) {
|
|
38305
38828
|
return c.json({ ok: false, error: error.code, message: error.message }, repositoryStatus(error));
|
|
38306
38829
|
}
|
|
@@ -38433,6 +38956,18 @@ function repositoryStatus(error) {
|
|
|
38433
38956
|
if (error.httpStatus === 409) return 409;
|
|
38434
38957
|
return 400;
|
|
38435
38958
|
}
|
|
38959
|
+
function imageErrorResponse(c, error, chargeStatus) {
|
|
38960
|
+
return c.json({
|
|
38961
|
+
ok: false,
|
|
38962
|
+
error: error.code,
|
|
38963
|
+
error_code: error.code,
|
|
38964
|
+
error_type: "invalid_request",
|
|
38965
|
+
retryable: error.retryable,
|
|
38966
|
+
...chargeStatus ? { charge_status: chargeStatus } : {},
|
|
38967
|
+
message: `${error.message} ${error.retryGuidance}`,
|
|
38968
|
+
details: { retry_guidance: error.retryGuidance }
|
|
38969
|
+
}, error.httpStatus);
|
|
38970
|
+
}
|
|
38436
38971
|
async function publicationOperation(c, operation, successStatus = 200) {
|
|
38437
38972
|
try {
|
|
38438
38973
|
return c.json({ ok: true, data: await operation() }, successStatus);
|
|
@@ -40172,10 +40707,10 @@ function scheduledArtifactOwnerIdForApiKey(apiKey) {
|
|
|
40172
40707
|
}
|
|
40173
40708
|
|
|
40174
40709
|
// src/api/scheduled-run-view-links.ts
|
|
40175
|
-
import { createHash as createHash22, randomBytes as randomBytes5, randomUUID as
|
|
40176
|
-
var
|
|
40710
|
+
import { createHash as createHash22, randomBytes as randomBytes5, randomUUID as randomUUID23 } from "crypto";
|
|
40711
|
+
var schemaReady3 = false;
|
|
40177
40712
|
async function ensureScheduledRunViewLinksSchema() {
|
|
40178
|
-
if (
|
|
40713
|
+
if (schemaReady3) return;
|
|
40179
40714
|
const db = getDb();
|
|
40180
40715
|
await db.execute(`
|
|
40181
40716
|
CREATE TABLE IF NOT EXISTS scheduled_run_view_links (
|
|
@@ -40223,7 +40758,7 @@ async function ensureScheduledRunViewLinksSchema() {
|
|
|
40223
40758
|
CREATE INDEX IF NOT EXISTS scheduled_run_view_links_expiry
|
|
40224
40759
|
ON scheduled_run_view_links(expires_at, revoked_at)
|
|
40225
40760
|
`);
|
|
40226
|
-
|
|
40761
|
+
schemaReady3 = true;
|
|
40227
40762
|
}
|
|
40228
40763
|
function tokenHash2(token6) {
|
|
40229
40764
|
return createHash22("sha256").update(token6).digest("hex");
|
|
@@ -40248,7 +40783,7 @@ async function createScheduledRunViewLink(input) {
|
|
|
40248
40783
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
40249
40784
|
const token6 = randomBytes5(32).toString("base64url");
|
|
40250
40785
|
const record = {
|
|
40251
|
-
shareId:
|
|
40786
|
+
shareId: randomUUID23(),
|
|
40252
40787
|
ownerId: input.ownerId,
|
|
40253
40788
|
runId: input.runId,
|
|
40254
40789
|
artifactId: input.artifactId,
|
|
@@ -41116,352 +41651,6 @@ import { Hono as Hono37 } from "hono";
|
|
|
41116
41651
|
|
|
41117
41652
|
// src/api/scheduler-integration-auth.ts
|
|
41118
41653
|
import { createHash as createHash28, createHmac as createHmac4, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
41119
|
-
|
|
41120
|
-
// src/api/service-connections.ts
|
|
41121
|
-
import { randomUUID as randomUUID23 } from "crypto";
|
|
41122
|
-
var schemaReady3 = null;
|
|
41123
|
-
var schemaDb4 = null;
|
|
41124
|
-
function ensureServiceConnectionsSchema() {
|
|
41125
|
-
const currentDb = getDb();
|
|
41126
|
-
if (schemaReady3 && schemaDb4 === currentDb) return schemaReady3;
|
|
41127
|
-
schemaDb4 = currentDb;
|
|
41128
|
-
schemaReady3 = (async () => {
|
|
41129
|
-
const db = currentDb;
|
|
41130
|
-
await db.execute(`
|
|
41131
|
-
CREATE TABLE IF NOT EXISTS service_connections (
|
|
41132
|
-
id TEXT PRIMARY KEY,
|
|
41133
|
-
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
41134
|
-
provider_config_key TEXT NOT NULL,
|
|
41135
|
-
provider TEXT,
|
|
41136
|
-
transport TEXT NOT NULL,
|
|
41137
|
-
upstream_connection_id TEXT,
|
|
41138
|
-
label TEXT,
|
|
41139
|
-
user_label TEXT,
|
|
41140
|
-
provider_account_id TEXT,
|
|
41141
|
-
provider_account_email TEXT,
|
|
41142
|
-
provider_account_name TEXT,
|
|
41143
|
-
provider_identity_checked_at TEXT,
|
|
41144
|
-
provider_identity_source_updated_at TEXT,
|
|
41145
|
-
lifecycle_status TEXT NOT NULL DEFAULT 'pending',
|
|
41146
|
-
operational_status TEXT NOT NULL DEFAULT 'unknown',
|
|
41147
|
-
reconnect_required INTEGER NOT NULL DEFAULT 0,
|
|
41148
|
-
actions_enabled INTEGER NOT NULL DEFAULT 0,
|
|
41149
|
-
read_tools_json TEXT NOT NULL DEFAULT '[]',
|
|
41150
|
-
action_tools_json TEXT NOT NULL DEFAULT '[]',
|
|
41151
|
-
tool_revision TEXT,
|
|
41152
|
-
last_checked_at TEXT,
|
|
41153
|
-
last_successful_call_at TEXT,
|
|
41154
|
-
last_failure_at TEXT,
|
|
41155
|
-
last_failure_code TEXT,
|
|
41156
|
-
last_failure_retryable INTEGER,
|
|
41157
|
-
consecutive_failures INTEGER NOT NULL DEFAULT 0,
|
|
41158
|
-
vault_name TEXT,
|
|
41159
|
-
table_name TEXT,
|
|
41160
|
-
legacy_owner_identity TEXT,
|
|
41161
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
41162
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
41163
|
-
UNIQUE(user_id, provider_config_key, upstream_connection_id)
|
|
41164
|
-
)
|
|
41165
|
-
`);
|
|
41166
|
-
try {
|
|
41167
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN user_label TEXT`);
|
|
41168
|
-
} catch {
|
|
41169
|
-
}
|
|
41170
|
-
try {
|
|
41171
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_id TEXT`);
|
|
41172
|
-
} catch {
|
|
41173
|
-
}
|
|
41174
|
-
try {
|
|
41175
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_email TEXT`);
|
|
41176
|
-
} catch {
|
|
41177
|
-
}
|
|
41178
|
-
try {
|
|
41179
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_name TEXT`);
|
|
41180
|
-
} catch {
|
|
41181
|
-
}
|
|
41182
|
-
try {
|
|
41183
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_checked_at TEXT`);
|
|
41184
|
-
} catch {
|
|
41185
|
-
}
|
|
41186
|
-
try {
|
|
41187
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_source_updated_at TEXT`);
|
|
41188
|
-
} catch {
|
|
41189
|
-
}
|
|
41190
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_user_updated ON service_connections(user_id, updated_at DESC)`);
|
|
41191
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_upstream ON service_connections(provider_config_key, upstream_connection_id)`);
|
|
41192
|
-
await db.execute(`
|
|
41193
|
-
CREATE TABLE IF NOT EXISTS service_connection_health_events (
|
|
41194
|
-
id TEXT PRIMARY KEY,
|
|
41195
|
-
connection_id TEXT NOT NULL REFERENCES service_connections(id) ON DELETE CASCADE,
|
|
41196
|
-
operational_status TEXT NOT NULL,
|
|
41197
|
-
failure_code TEXT,
|
|
41198
|
-
retryable INTEGER,
|
|
41199
|
-
evidence_source TEXT NOT NULL,
|
|
41200
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
41201
|
-
)
|
|
41202
|
-
`);
|
|
41203
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS service_connection_health_events_connection ON service_connection_health_events(connection_id, created_at DESC)`);
|
|
41204
|
-
await db.execute(`
|
|
41205
|
-
CREATE TABLE IF NOT EXISTS service_connection_action_audit (
|
|
41206
|
-
id TEXT PRIMARY KEY,
|
|
41207
|
-
connection_id TEXT NOT NULL REFERENCES service_connections(id) ON DELETE CASCADE,
|
|
41208
|
-
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
41209
|
-
tool TEXT NOT NULL,
|
|
41210
|
-
request_id TEXT NOT NULL,
|
|
41211
|
-
status TEXT NOT NULL,
|
|
41212
|
-
error_code TEXT,
|
|
41213
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
41214
|
-
UNIQUE(user_id, request_id)
|
|
41215
|
-
)
|
|
41216
|
-
`);
|
|
41217
|
-
try {
|
|
41218
|
-
await db.execute(`ALTER TABLE service_connection_action_audit ADD COLUMN request_digest TEXT`);
|
|
41219
|
-
} catch {
|
|
41220
|
-
}
|
|
41221
|
-
try {
|
|
41222
|
-
await db.execute(`ALTER TABLE service_connection_action_audit ADD COLUMN result_json TEXT`);
|
|
41223
|
-
} catch {
|
|
41224
|
-
}
|
|
41225
|
-
})().catch((error) => {
|
|
41226
|
-
schemaReady3 = null;
|
|
41227
|
-
schemaDb4 = null;
|
|
41228
|
-
throw error;
|
|
41229
|
-
});
|
|
41230
|
-
return schemaReady3;
|
|
41231
|
-
}
|
|
41232
|
-
function parseTools(value) {
|
|
41233
|
-
if (typeof value !== "string") return [];
|
|
41234
|
-
try {
|
|
41235
|
-
const parsed = JSON.parse(value);
|
|
41236
|
-
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
41237
|
-
} catch {
|
|
41238
|
-
return [];
|
|
41239
|
-
}
|
|
41240
|
-
}
|
|
41241
|
-
function rowToRecord(row) {
|
|
41242
|
-
return {
|
|
41243
|
-
id: String(row.id),
|
|
41244
|
-
userId: Number(row.user_id),
|
|
41245
|
-
providerConfigKey: String(row.provider_config_key),
|
|
41246
|
-
provider: typeof row.provider === "string" ? row.provider : null,
|
|
41247
|
-
transport: row.transport === "remote_mcp" ? "remote_mcp" : "nango",
|
|
41248
|
-
upstreamConnectionId: typeof row.upstream_connection_id === "string" ? row.upstream_connection_id : null,
|
|
41249
|
-
label: typeof row.label === "string" ? row.label : null,
|
|
41250
|
-
userLabel: typeof row.user_label === "string" ? row.user_label : null,
|
|
41251
|
-
providerAccountId: typeof row.provider_account_id === "string" ? row.provider_account_id : null,
|
|
41252
|
-
providerAccountEmail: typeof row.provider_account_email === "string" ? row.provider_account_email : null,
|
|
41253
|
-
providerAccountName: typeof row.provider_account_name === "string" ? row.provider_account_name : null,
|
|
41254
|
-
providerIdentityCheckedAt: typeof row.provider_identity_checked_at === "string" ? row.provider_identity_checked_at : null,
|
|
41255
|
-
providerIdentitySourceUpdatedAt: typeof row.provider_identity_source_updated_at === "string" ? row.provider_identity_source_updated_at : null,
|
|
41256
|
-
lifecycleStatus: String(row.lifecycle_status),
|
|
41257
|
-
operationalStatus: String(row.operational_status),
|
|
41258
|
-
reconnectRequired: Number(row.reconnect_required) === 1,
|
|
41259
|
-
actionsEnabled: Number(row.actions_enabled) === 1,
|
|
41260
|
-
readTools: parseTools(row.read_tools_json),
|
|
41261
|
-
actionTools: parseTools(row.action_tools_json),
|
|
41262
|
-
toolRevision: typeof row.tool_revision === "string" ? row.tool_revision : null,
|
|
41263
|
-
lastCheckedAt: typeof row.last_checked_at === "string" ? row.last_checked_at : null,
|
|
41264
|
-
lastSuccessfulCallAt: typeof row.last_successful_call_at === "string" ? row.last_successful_call_at : null,
|
|
41265
|
-
lastFailureAt: typeof row.last_failure_at === "string" ? row.last_failure_at : null,
|
|
41266
|
-
lastFailureCode: typeof row.last_failure_code === "string" ? row.last_failure_code : null,
|
|
41267
|
-
lastFailureRetryable: row.last_failure_retryable === null || row.last_failure_retryable === void 0 ? null : Number(row.last_failure_retryable) === 1,
|
|
41268
|
-
consecutiveFailures: Number(row.consecutive_failures ?? 0),
|
|
41269
|
-
vaultName: typeof row.vault_name === "string" ? row.vault_name : null,
|
|
41270
|
-
tableName: typeof row.table_name === "string" ? row.table_name : null,
|
|
41271
|
-
createdAt: String(row.created_at),
|
|
41272
|
-
updatedAt: String(row.updated_at)
|
|
41273
|
-
};
|
|
41274
|
-
}
|
|
41275
|
-
async function userIdForIdentity(identity) {
|
|
41276
|
-
const user = await getUserByEmail(identity.normalize("NFKC").trim().toLowerCase());
|
|
41277
|
-
if (!user) throw new Error("service_connection_user_not_found");
|
|
41278
|
-
return Number(user.id);
|
|
41279
|
-
}
|
|
41280
|
-
async function reconcileDiscoveredNangoConnections(identity, discovered) {
|
|
41281
|
-
await ensureServiceConnectionsSchema();
|
|
41282
|
-
const db = getDb();
|
|
41283
|
-
const userId = await userIdForIdentity(identity);
|
|
41284
|
-
for (const connection of discovered) {
|
|
41285
|
-
await db.execute({
|
|
41286
|
-
sql: `
|
|
41287
|
-
INSERT INTO service_connections (
|
|
41288
|
-
id, user_id, provider_config_key, provider, transport, upstream_connection_id,
|
|
41289
|
-
label, lifecycle_status, reconnect_required, actions_enabled, legacy_owner_identity, created_at, updated_at
|
|
41290
|
-
) VALUES (?, ?, ?, ?, 'nango', ?, ?, ?, ?, 1, ?, COALESCE(?, datetime('now')), COALESCE(?, datetime('now')))
|
|
41291
|
-
ON CONFLICT(user_id, provider_config_key, upstream_connection_id) DO UPDATE SET
|
|
41292
|
-
provider = excluded.provider,
|
|
41293
|
-
label = COALESCE(excluded.label, service_connections.label),
|
|
41294
|
-
lifecycle_status = excluded.lifecycle_status,
|
|
41295
|
-
reconnect_required = excluded.reconnect_required,
|
|
41296
|
-
updated_at = excluded.updated_at
|
|
41297
|
-
`,
|
|
41298
|
-
args: [
|
|
41299
|
-
randomUUID23(),
|
|
41300
|
-
userId,
|
|
41301
|
-
connection.providerConfigKey,
|
|
41302
|
-
connection.provider,
|
|
41303
|
-
connection.upstreamConnectionId,
|
|
41304
|
-
connection.label,
|
|
41305
|
-
connection.lifecycleStatus,
|
|
41306
|
-
connection.reconnectRequired ? 1 : 0,
|
|
41307
|
-
identity,
|
|
41308
|
-
connection.createdAt,
|
|
41309
|
-
connection.updatedAt
|
|
41310
|
-
]
|
|
41311
|
-
});
|
|
41312
|
-
}
|
|
41313
|
-
return listServiceConnections(identity, "nango");
|
|
41314
|
-
}
|
|
41315
|
-
async function listServiceConnections(identity, transport) {
|
|
41316
|
-
await ensureServiceConnectionsSchema();
|
|
41317
|
-
const userId = await userIdForIdentity(identity);
|
|
41318
|
-
const result = await getDb().execute({
|
|
41319
|
-
sql: `SELECT * FROM service_connections WHERE user_id = ?${transport ? " AND transport = ?" : ""} AND lifecycle_status <> 'disconnected' ORDER BY updated_at DESC`,
|
|
41320
|
-
args: transport ? [userId, transport] : [userId]
|
|
41321
|
-
});
|
|
41322
|
-
return result.rows.map((row) => rowToRecord(row));
|
|
41323
|
-
}
|
|
41324
|
-
async function getOwnedServiceConnection(identity, connectionId) {
|
|
41325
|
-
await ensureServiceConnectionsSchema();
|
|
41326
|
-
const userId = await userIdForIdentity(identity);
|
|
41327
|
-
const result = await getDb().execute({
|
|
41328
|
-
sql: "SELECT * FROM service_connections WHERE id = ? AND user_id = ? LIMIT 1",
|
|
41329
|
-
args: [connectionId, userId]
|
|
41330
|
-
});
|
|
41331
|
-
const row = result.rows[0];
|
|
41332
|
-
return row ? rowToRecord(row) : null;
|
|
41333
|
-
}
|
|
41334
|
-
async function updateServiceConnectionTools(connectionId, readTools, actionTools, toolRevision) {
|
|
41335
|
-
await ensureServiceConnectionsSchema();
|
|
41336
|
-
await getDb().execute({
|
|
41337
|
-
sql: `UPDATE service_connections SET read_tools_json = ?, action_tools_json = ?, tool_revision = ?, updated_at = datetime('now') WHERE id = ?`,
|
|
41338
|
-
args: [JSON.stringify(readTools), JSON.stringify(actionTools), toolRevision, connectionId]
|
|
41339
|
-
});
|
|
41340
|
-
}
|
|
41341
|
-
async function setServiceConnectionProviderIdentity(args) {
|
|
41342
|
-
await ensureServiceConnectionsSchema();
|
|
41343
|
-
await getDb().execute({
|
|
41344
|
-
sql: `
|
|
41345
|
-
UPDATE service_connections SET
|
|
41346
|
-
provider_account_id = ?, provider_account_email = ?, provider_account_name = ?,
|
|
41347
|
-
provider_identity_checked_at = datetime('now'), provider_identity_source_updated_at = ?,
|
|
41348
|
-
updated_at = datetime('now')
|
|
41349
|
-
WHERE id = ?
|
|
41350
|
-
`,
|
|
41351
|
-
args: [
|
|
41352
|
-
args.providerAccountId ?? null,
|
|
41353
|
-
args.providerAccountEmail?.toLowerCase() ?? null,
|
|
41354
|
-
args.providerAccountName ?? null,
|
|
41355
|
-
args.sourceUpdatedAt,
|
|
41356
|
-
args.connectionId
|
|
41357
|
-
]
|
|
41358
|
-
});
|
|
41359
|
-
}
|
|
41360
|
-
async function setServiceConnectionUserLabel(identity, connectionId, userLabel) {
|
|
41361
|
-
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
41362
|
-
if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
|
|
41363
|
-
await getDb().execute({
|
|
41364
|
-
sql: `UPDATE service_connections SET user_label = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
41365
|
-
args: [userLabel, connectionId, connection.userId]
|
|
41366
|
-
});
|
|
41367
|
-
return userLabel;
|
|
41368
|
-
}
|
|
41369
|
-
async function recordServiceConnectionHealth(args) {
|
|
41370
|
-
await ensureServiceConnectionsSchema();
|
|
41371
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
41372
|
-
await getDb().execute({
|
|
41373
|
-
sql: `
|
|
41374
|
-
UPDATE service_connections SET
|
|
41375
|
-
operational_status = ?, last_checked_at = ?,
|
|
41376
|
-
last_successful_call_at = CASE WHEN ? = 'available' THEN ? ELSE last_successful_call_at END,
|
|
41377
|
-
last_failure_at = CASE WHEN ? IN ('degraded', 'unavailable') THEN ? ELSE NULL END,
|
|
41378
|
-
last_failure_code = ?, last_failure_retryable = ?,
|
|
41379
|
-
consecutive_failures = CASE WHEN ? = 'available' THEN 0 ELSE consecutive_failures + 1 END,
|
|
41380
|
-
updated_at = datetime('now')
|
|
41381
|
-
WHERE id = ?
|
|
41382
|
-
`,
|
|
41383
|
-
args: [
|
|
41384
|
-
args.operationalStatus,
|
|
41385
|
-
now,
|
|
41386
|
-
args.operationalStatus,
|
|
41387
|
-
now,
|
|
41388
|
-
args.operationalStatus,
|
|
41389
|
-
now,
|
|
41390
|
-
args.failureCode ?? null,
|
|
41391
|
-
args.retryable == null ? null : args.retryable ? 1 : 0,
|
|
41392
|
-
args.operationalStatus,
|
|
41393
|
-
args.connectionId
|
|
41394
|
-
]
|
|
41395
|
-
});
|
|
41396
|
-
await getDb().execute({
|
|
41397
|
-
sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
41398
|
-
args: [randomUUID23(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
|
|
41399
|
-
});
|
|
41400
|
-
}
|
|
41401
|
-
async function setServiceConnectionActions(identity, connectionId, enabled) {
|
|
41402
|
-
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
41403
|
-
if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
|
|
41404
|
-
await getDb().execute({
|
|
41405
|
-
sql: `UPDATE service_connections SET actions_enabled = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
41406
|
-
args: [enabled ? 1 : 0, connectionId, connection.userId]
|
|
41407
|
-
});
|
|
41408
|
-
return enabled;
|
|
41409
|
-
}
|
|
41410
|
-
async function markServiceConnectionDisconnected(identity, connectionId) {
|
|
41411
|
-
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
41412
|
-
if (!connection) throw new Error("service_connection_not_found");
|
|
41413
|
-
await getDb().execute({
|
|
41414
|
-
sql: `UPDATE service_connections SET lifecycle_status = 'disconnected', operational_status = 'unknown', reconnect_required = 0, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
41415
|
-
args: [connectionId, connection.userId]
|
|
41416
|
-
});
|
|
41417
|
-
const remaining = await getDb().execute({
|
|
41418
|
-
sql: `SELECT COUNT(*) AS count FROM service_connections WHERE user_id = ? AND transport = 'nango' AND lifecycle_status = 'connected'`,
|
|
41419
|
-
args: [connection.userId]
|
|
41420
|
-
});
|
|
41421
|
-
return Number(remaining.rows[0]?.count ?? 0);
|
|
41422
|
-
}
|
|
41423
|
-
async function recordServiceConnectionAction(args) {
|
|
41424
|
-
await ensureServiceConnectionsSchema();
|
|
41425
|
-
const connection = await getOwnedServiceConnection(args.identity, args.connectionId);
|
|
41426
|
-
if (!connection) throw new Error("service_connection_not_found");
|
|
41427
|
-
await getDb().execute({
|
|
41428
|
-
sql: `UPDATE service_connection_action_audit SET status = ?, error_code = ?, result_json = ? WHERE user_id = ? AND request_id = ? AND connection_id = ?`,
|
|
41429
|
-
args: [args.status, args.errorCode ?? null, args.result === void 0 ? null : JSON.stringify(args.result), connection.userId, args.requestId, connection.id]
|
|
41430
|
-
});
|
|
41431
|
-
}
|
|
41432
|
-
async function claimServiceConnectionAction(args) {
|
|
41433
|
-
await ensureServiceConnectionsSchema();
|
|
41434
|
-
const connection = await getOwnedServiceConnection(args.identity, args.connectionId);
|
|
41435
|
-
if (!connection) throw new Error("service_connection_not_found");
|
|
41436
|
-
const inserted = await getDb().execute({
|
|
41437
|
-
sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
|
|
41438
|
-
args: [randomUUID23(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
|
|
41439
|
-
});
|
|
41440
|
-
if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
|
|
41441
|
-
const existing = await getDb().execute({
|
|
41442
|
-
sql: `SELECT connection_id, tool, status, error_code, request_digest, result_json FROM service_connection_action_audit WHERE user_id = ? AND request_id = ? LIMIT 1`,
|
|
41443
|
-
args: [connection.userId, args.requestId]
|
|
41444
|
-
});
|
|
41445
|
-
const row = existing.rows[0];
|
|
41446
|
-
const conflict = String(row?.connection_id ?? "") !== connection.id || String(row?.tool ?? "") !== args.tool || String(row?.request_digest ?? "") !== args.requestDigest;
|
|
41447
|
-
let result = void 0;
|
|
41448
|
-
if (!conflict && typeof row?.result_json === "string") {
|
|
41449
|
-
try {
|
|
41450
|
-
result = JSON.parse(row.result_json);
|
|
41451
|
-
} catch {
|
|
41452
|
-
result = void 0;
|
|
41453
|
-
}
|
|
41454
|
-
}
|
|
41455
|
-
return {
|
|
41456
|
-
claimed: false,
|
|
41457
|
-
status: String(row?.status ?? "unknown"),
|
|
41458
|
-
errorCode: typeof row?.error_code === "string" ? row.error_code : null,
|
|
41459
|
-
conflict,
|
|
41460
|
-
...result !== void 0 ? { result } : {}
|
|
41461
|
-
};
|
|
41462
|
-
}
|
|
41463
|
-
|
|
41464
|
-
// src/api/scheduler-integration-auth.ts
|
|
41465
41654
|
var MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
|
|
41466
41655
|
var SchedulerIntegrationAuthError = class extends Error {
|
|
41467
41656
|
code;
|
|
@@ -44203,6 +44392,7 @@ function connectedToolPolicyOrThrow(registry, toolName) {
|
|
|
44203
44392
|
// src/api/main-nango-transport.ts
|
|
44204
44393
|
var DEFAULT_NANGO_MCP_URL = "https://api.nango.dev/mcp";
|
|
44205
44394
|
var NANGO_TIMEOUT_MS = 2e4;
|
|
44395
|
+
var NANGO_TOOL_CALL_TIMEOUT_MS = 24e4;
|
|
44206
44396
|
var NANGO_CONNECTION_PAGE_SIZE = 100;
|
|
44207
44397
|
var NANGO_CONNECTION_MAX_PAGES = 20;
|
|
44208
44398
|
var PROVIDER_IDENTITY_RETRY_MS = 6 * 60 * 60 * 1e3;
|
|
@@ -44241,6 +44431,15 @@ function retryAfterMsFromText(value) {
|
|
|
44241
44431
|
function normalizeNangoClientError(error) {
|
|
44242
44432
|
if (error instanceof MainNangoTransportError) return error;
|
|
44243
44433
|
const detail = nangoClientErrorText(error);
|
|
44434
|
+
const structured = error && typeof error === "object" ? error : null;
|
|
44435
|
+
if (structured?.code === -32001 || structured?.name === "RequestTimeout" || /Request timed out|RequestTimeout|Maximum total timeout exceeded/i.test(detail)) {
|
|
44436
|
+
return new MainNangoTransportError(
|
|
44437
|
+
"The connected service operation timed out before returning a result. Retry failed items or use a smaller batch.",
|
|
44438
|
+
503,
|
|
44439
|
+
"mcp_request_timeout",
|
|
44440
|
+
true
|
|
44441
|
+
);
|
|
44442
|
+
}
|
|
44244
44443
|
if (/SERVICE_DISABLED|accessNotConfigured/i.test(detail)) {
|
|
44245
44444
|
return new MainNangoTransportError(
|
|
44246
44445
|
"The provider API required by this connection is disabled. Enable it in the provider project, wait for propagation, then retry.",
|
|
@@ -44271,6 +44470,12 @@ function normalizeNangoClientError(error) {
|
|
|
44271
44470
|
}
|
|
44272
44471
|
return new MainNangoTransportError("The service connection is temporarily unavailable.", 503, "connection_transport_unavailable");
|
|
44273
44472
|
}
|
|
44473
|
+
async function callNangoToolWithBudget(client2, tool, input) {
|
|
44474
|
+
return client2.callTool(
|
|
44475
|
+
{ name: tool, arguments: input },
|
|
44476
|
+
{ timeout: NANGO_TOOL_CALL_TIMEOUT_MS }
|
|
44477
|
+
);
|
|
44478
|
+
}
|
|
44274
44479
|
function asRecord(value) {
|
|
44275
44480
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
44276
44481
|
}
|
|
@@ -44730,7 +44935,7 @@ async function callNangoToolDirect(args) {
|
|
|
44730
44935
|
let result;
|
|
44731
44936
|
let providerError;
|
|
44732
44937
|
try {
|
|
44733
|
-
result = await withNangoClient(connection, (client2) => client2
|
|
44938
|
+
result = await withNangoClient(connection, (client2) => callNangoToolWithBudget(client2, args.tool, args.input));
|
|
44734
44939
|
} catch (error) {
|
|
44735
44940
|
providerError = error;
|
|
44736
44941
|
}
|
|
@@ -49691,7 +49896,7 @@ app.get("/cron/tick", async (c) => {
|
|
|
49691
49896
|
return c.json({ error: "Unauthorized" }, 401);
|
|
49692
49897
|
}
|
|
49693
49898
|
const startedAt = Date.now();
|
|
49694
|
-
const { drainQueue } = await import("./worker-
|
|
49899
|
+
const { drainQueue } = await import("./worker-IBLGV272.js");
|
|
49695
49900
|
const budget = { maxJobs: 10, deadlineMs: startedAt + CRON_TICK_DRAIN_BUDGET_MS };
|
|
49696
49901
|
const origin = `${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`;
|
|
49697
49902
|
const unfinished = [];
|
|
@@ -49769,8 +49974,8 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
|
|
|
49769
49974
|
finishExtractJob: finishExtractJob2,
|
|
49770
49975
|
terminalExtractJobStatus: terminalExtractJobStatus2,
|
|
49771
49976
|
extractJobLimitInfo: extractJobLimitInfo2
|
|
49772
|
-
} = await import("./site-extract-repository-
|
|
49773
|
-
const { assembleExtractArtifacts } = await import("./extract-bundle-
|
|
49977
|
+
} = await import("./site-extract-repository-CZ7SVT3Z.js");
|
|
49978
|
+
const { assembleExtractArtifacts } = await import("./extract-bundle-RW7CVIRF.js");
|
|
49774
49979
|
const existing = await getExtractJob2(jobId2);
|
|
49775
49980
|
if (!existing) return c.json({ error: "job not found" }, 404);
|
|
49776
49981
|
if (existing.status !== "failed") {
|
|
@@ -49797,7 +50002,7 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
|
|
|
49797
50002
|
if (!updated) return c.json({ error: "job state changed during re-finalization" }, 409);
|
|
49798
50003
|
let settlement = "already_settled_or_refunded";
|
|
49799
50004
|
if (job.billedMc == null && job.userId != null) {
|
|
49800
|
-
const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-
|
|
50005
|
+
const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-CZ7SVT3Z.js");
|
|
49801
50006
|
const heldMc = Number(job.options.heldMc ?? 0);
|
|
49802
50007
|
const successful = await countSuccessfulPages2(jobId2);
|
|
49803
50008
|
const usedMc = Math.min(successful * MC_COSTS.page_scrape, heldMc);
|