mcp-scraper 0.66.6 → 0.67.0
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 +16 -1
- package/README.md +1 -1
- package/dist/bin/api-server.cjs +1122 -625
- 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 +147 -17
- package/dist/bin/mcp-stdio-server.js +6 -6
- package/dist/bin/paa-harvest.cjs +20 -1
- package/dist/bin/paa-harvest.js +3 -3
- package/dist/{chunk-3EYALCYK.js → chunk-2L4C4DAZ.js} +1 -1
- package/dist/{chunk-CETDPNMZ.js → chunk-2LXTOAKS.js} +1 -1
- package/dist/{chunk-K7Y27ND3.js → chunk-62LEXS5O.js} +2 -2
- package/dist/{chunk-55B3T5L2.js → chunk-65FTMB7G.js} +92 -1
- package/dist/{chunk-3TKKJQB4.js → chunk-ES25GP6C.js} +1 -1
- package/dist/{chunk-NLEAYKOC.js → chunk-GT63KEUB.js} +1 -1
- package/dist/{chunk-KPE52NPT.js → chunk-IHDTONZV.js} +1 -1
- package/dist/{chunk-KXRWY7DZ.js → chunk-LCGTNWOT.js} +1 -1
- package/dist/{chunk-DBWWIK7O.js → chunk-LN6N3YLK.js} +1 -1
- package/dist/{chunk-ABYBPODM.js → chunk-UL4ZKAWZ.js} +1 -1
- package/dist/{chunk-VMWVEKZT.js → chunk-XOOKZYZG.js} +71 -18
- package/dist/{chunk-PMCT2YQM.js → chunk-Y46YNQMM.js} +1 -1
- package/dist/{db-4RVZ3NYZ.js → db-ZGZAKYBW.js} +1 -1
- package/dist/{extract-bundle-EA5URLXN.js → extract-bundle-QQIYJSBW.js} +3 -3
- package/dist/index.cjs +20 -1
- package/dist/index.js +3 -3
- package/dist/{lead-list-enrichment-repository-IBXNWM6H.js → lead-list-enrichment-repository-CXLVIV3R.js} +2 -2
- package/dist/{location-data-repository-WHHQUCUI.js → location-data-repository-G72M7DTN.js} +2 -2
- package/dist/{server-5WPS3LZD.js → server-MFVYRB6I.js} +818 -471
- package/dist/{site-extract-repository-NTM4MDQN.js → site-extract-repository-7EP6UX6I.js} +2 -2
- package/dist/{worker-UTBUWEXQ.js → worker-JGWV2LID.js} +5 -5
- package/package.json +1 -1
package/dist/bin/api-server.cjs
CHANGED
|
@@ -164,6 +164,26 @@ function publicErrorType(code) {
|
|
|
164
164
|
case "site_export_read_failed":
|
|
165
165
|
case "site_export_image_not_found":
|
|
166
166
|
return "extraction";
|
|
167
|
+
case "image_url_invalid":
|
|
168
|
+
case "image_url_not_https":
|
|
169
|
+
case "image_host_unresolvable":
|
|
170
|
+
case "image_host_blocked":
|
|
171
|
+
case "image_redirect_invalid":
|
|
172
|
+
case "image_source_unavailable":
|
|
173
|
+
case "image_too_large":
|
|
174
|
+
case "image_redirect_limit":
|
|
175
|
+
case "image_base64_invalid":
|
|
176
|
+
case "image_source_missing":
|
|
177
|
+
case "image_source_ambiguous":
|
|
178
|
+
case "image_source_type_invalid":
|
|
179
|
+
case "image_artifact_not_found":
|
|
180
|
+
case "image_page_too_large":
|
|
181
|
+
case "page_image_not_found":
|
|
182
|
+
case "page_image_candidates_unusable":
|
|
183
|
+
case "image_format_unsupported":
|
|
184
|
+
case "image_storage_unconfigured":
|
|
185
|
+
case "featured_image_unhostable":
|
|
186
|
+
return "invalid_request";
|
|
167
187
|
case "insufficient_balance":
|
|
168
188
|
return "billing";
|
|
169
189
|
case "vendor_unavailable":
|
|
@@ -237,6 +257,44 @@ function publicErrorMessage(code) {
|
|
|
237
257
|
return "The stored site export could not be read or did not pass its integrity check.";
|
|
238
258
|
case "site_export_image_not_found":
|
|
239
259
|
return "The requested site-export image was not found, expired, or does not belong to this account.";
|
|
260
|
+
case "image_url_invalid":
|
|
261
|
+
return "The image source is not a valid URL. Use a stable public HTTPS URL or inline image bytes.";
|
|
262
|
+
case "image_url_not_https":
|
|
263
|
+
return "Commons image sources must use a public HTTPS URL or inline image bytes.";
|
|
264
|
+
case "image_host_unresolvable":
|
|
265
|
+
return "The image host could not be resolved. Use a different stable public URL or inline image bytes.";
|
|
266
|
+
case "image_host_blocked":
|
|
267
|
+
return "The image source resolves to a network address Commons cannot fetch safely.";
|
|
268
|
+
case "image_redirect_invalid":
|
|
269
|
+
return "The image source redirected to an invalid or unsafe destination.";
|
|
270
|
+
case "image_source_unavailable":
|
|
271
|
+
return "The image source could not be fetched. Host a different stable public URL or provide inline image bytes.";
|
|
272
|
+
case "image_too_large":
|
|
273
|
+
return "The image exceeds the Commons image size limit.";
|
|
274
|
+
case "image_redirect_limit":
|
|
275
|
+
return "The image source exceeded the Commons redirect limit.";
|
|
276
|
+
case "image_base64_invalid":
|
|
277
|
+
return "The supplied inline image bytes are invalid.";
|
|
278
|
+
case "image_source_missing":
|
|
279
|
+
return "Provide exactly one of a public image or webpage URL, inline image bytes, or an owner-scoped image artifact.";
|
|
280
|
+
case "image_source_ambiguous":
|
|
281
|
+
return "Provide only one image source: sourceUrl, imageBase64, or artifactId.";
|
|
282
|
+
case "image_source_type_invalid":
|
|
283
|
+
return "Use sourceType only with inline image bytes or an owner-scoped image artifact.";
|
|
284
|
+
case "image_artifact_not_found":
|
|
285
|
+
return "The image artifact was not found, expired, or does not belong to this account.";
|
|
286
|
+
case "image_page_too_large":
|
|
287
|
+
return "The webpage is too large for bounded Commons image discovery.";
|
|
288
|
+
case "page_image_not_found":
|
|
289
|
+
return "The webpage did not expose a usable featured or content image.";
|
|
290
|
+
case "page_image_candidates_unusable":
|
|
291
|
+
return "The webpage exposed image candidates, but none returned supported public image bytes.";
|
|
292
|
+
case "image_format_unsupported":
|
|
293
|
+
return "Commons accepts JPEG, PNG, GIF, or WebP image bytes.";
|
|
294
|
+
case "image_storage_unconfigured":
|
|
295
|
+
return "Commons image storage is temporarily unavailable.";
|
|
296
|
+
case "featured_image_unhostable":
|
|
297
|
+
return "The required featured image could not be hosted for Commons publication.";
|
|
240
298
|
default:
|
|
241
299
|
return PUBLIC_SERVICE_UNAVAILABLE_MESSAGE;
|
|
242
300
|
}
|
|
@@ -299,7 +357,26 @@ var init_errors = __esm({
|
|
|
299
357
|
"site_export_not_found",
|
|
300
358
|
"site_export_format_unavailable",
|
|
301
359
|
"site_export_read_failed",
|
|
302
|
-
"site_export_image_not_found"
|
|
360
|
+
"site_export_image_not_found",
|
|
361
|
+
"image_url_invalid",
|
|
362
|
+
"image_url_not_https",
|
|
363
|
+
"image_host_unresolvable",
|
|
364
|
+
"image_host_blocked",
|
|
365
|
+
"image_redirect_invalid",
|
|
366
|
+
"image_source_unavailable",
|
|
367
|
+
"image_too_large",
|
|
368
|
+
"image_redirect_limit",
|
|
369
|
+
"image_base64_invalid",
|
|
370
|
+
"image_source_missing",
|
|
371
|
+
"image_source_ambiguous",
|
|
372
|
+
"image_source_type_invalid",
|
|
373
|
+
"image_artifact_not_found",
|
|
374
|
+
"image_page_too_large",
|
|
375
|
+
"page_image_not_found",
|
|
376
|
+
"page_image_candidates_unusable",
|
|
377
|
+
"image_format_unsupported",
|
|
378
|
+
"image_storage_unconfigured",
|
|
379
|
+
"featured_image_unhostable"
|
|
303
380
|
];
|
|
304
381
|
PUBLIC_ERROR_TYPES = [
|
|
305
382
|
"request_aborted",
|
|
@@ -4495,6 +4572,20 @@ async function migrate(options = {}) {
|
|
|
4495
4572
|
PRIMARY KEY (user_id, connection_id, period_start)
|
|
4496
4573
|
)
|
|
4497
4574
|
`);
|
|
4575
|
+
await db.execute(`
|
|
4576
|
+
CREATE TABLE IF NOT EXISTS connected_account_charge_transfers (
|
|
4577
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
4578
|
+
user_id INTEGER NOT NULL,
|
|
4579
|
+
period_start TEXT NOT NULL,
|
|
4580
|
+
from_connection_id TEXT NOT NULL,
|
|
4581
|
+
to_connection_id TEXT NOT NULL,
|
|
4582
|
+
provider_config_key TEXT NOT NULL,
|
|
4583
|
+
transferred_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4584
|
+
UNIQUE (user_id, period_start, from_connection_id),
|
|
4585
|
+
CHECK (from_connection_id <> to_connection_id)
|
|
4586
|
+
)
|
|
4587
|
+
`);
|
|
4588
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS connected_account_charge_transfers_destination ON connected_account_charge_transfers(user_id, period_start, to_connection_id, transferred_at DESC)`);
|
|
4498
4589
|
await db.execute(`
|
|
4499
4590
|
CREATE TABLE IF NOT EXISTS billing_credits (
|
|
4500
4591
|
idempotency_key TEXT PRIMARY KEY,
|
|
@@ -11327,7 +11418,8 @@ function firstJsonLdImage(value) {
|
|
|
11327
11418
|
}
|
|
11328
11419
|
return null;
|
|
11329
11420
|
}
|
|
11330
|
-
function
|
|
11421
|
+
function contentImages(html) {
|
|
11422
|
+
const images = [];
|
|
11331
11423
|
for (const match of html.matchAll(/<img\b[^>]*>/gi)) {
|
|
11332
11424
|
const tag = match[0];
|
|
11333
11425
|
const attr = (name) => tag.match(new RegExp(`\\b${name}\\s*=\\s*(?:"([^"]+)"|'([^']+)'|([^\\s>]+))`, "i"))?.slice(1).find(Boolean) ?? null;
|
|
@@ -11338,30 +11430,36 @@ function firstContentImage(html) {
|
|
|
11338
11430
|
const width = Number(attr("width") ?? 0);
|
|
11339
11431
|
const height = Number(attr("height") ?? 0);
|
|
11340
11432
|
if (width > 0 && width < 200 || height > 0 && height < 120) continue;
|
|
11341
|
-
|
|
11433
|
+
images.push(candidate);
|
|
11342
11434
|
}
|
|
11343
|
-
return
|
|
11435
|
+
return images;
|
|
11344
11436
|
}
|
|
11345
|
-
function
|
|
11437
|
+
function resolveFeaturedImageCandidates(input) {
|
|
11346
11438
|
const replay = parseWaybackReplayUrl(input.pageUrl);
|
|
11347
11439
|
const baseUrl = replay?.originalUrl ?? input.pageUrl;
|
|
11348
11440
|
const candidates = [
|
|
11349
11441
|
{ value: input.meta["og:image"] ?? input.meta["og:image:url"] ?? input.meta["og:image:secure_url"], source: "og:image" },
|
|
11350
11442
|
{ value: input.meta["twitter:image"] ?? input.meta["twitter:image:src"], source: "twitter:image" },
|
|
11351
11443
|
{ value: firstJsonLdImage(input.schema), source: "json-ld" },
|
|
11352
|
-
|
|
11444
|
+
...contentImages(input.html).map((value) => ({ value, source: "content-image" }))
|
|
11353
11445
|
];
|
|
11446
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11447
|
+
const resolved = [];
|
|
11354
11448
|
for (const candidate of candidates) {
|
|
11355
11449
|
if (!candidate.value) continue;
|
|
11356
11450
|
const url = safeHttpUrl2(candidate.value, baseUrl);
|
|
11357
|
-
if (!url) continue;
|
|
11358
|
-
|
|
11451
|
+
if (!url || seen.has(url)) continue;
|
|
11452
|
+
seen.add(url);
|
|
11453
|
+
resolved.push({
|
|
11359
11454
|
url,
|
|
11360
11455
|
archivedUrl: replay ? buildWaybackReplayUrl(replay.timestamp, url, "im_") : null,
|
|
11361
11456
|
source: candidate.source
|
|
11362
|
-
};
|
|
11457
|
+
});
|
|
11363
11458
|
}
|
|
11364
|
-
return
|
|
11459
|
+
return resolved;
|
|
11460
|
+
}
|
|
11461
|
+
function resolveFeaturedImage(input) {
|
|
11462
|
+
return resolveFeaturedImageCandidates(input)[0] ?? null;
|
|
11365
11463
|
}
|
|
11366
11464
|
var import_p_limit, WAYBACK_HOSTS, MAX_CDX_RESPONSE_BYTES, MAX_CDX_ROWS, MAX_ARCHIVE_URL_CHARS, MAX_SELECTED_TIMELINE_CELLS, TIMELINE_CDX_CONCURRENCY;
|
|
11367
11465
|
var init_wayback = __esm({
|
|
@@ -36288,6 +36386,357 @@ var init_subscription_status_policy = __esm({
|
|
|
36288
36386
|
}
|
|
36289
36387
|
});
|
|
36290
36388
|
|
|
36389
|
+
// src/api/service-connections.ts
|
|
36390
|
+
function ensureServiceConnectionsSchema() {
|
|
36391
|
+
const currentDb = getDb();
|
|
36392
|
+
if (schemaReady && schemaDb5 === currentDb) return schemaReady;
|
|
36393
|
+
schemaDb5 = currentDb;
|
|
36394
|
+
schemaReady = (async () => {
|
|
36395
|
+
const db = currentDb;
|
|
36396
|
+
await db.execute(`
|
|
36397
|
+
CREATE TABLE IF NOT EXISTS service_connections (
|
|
36398
|
+
id TEXT PRIMARY KEY,
|
|
36399
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
36400
|
+
provider_config_key TEXT NOT NULL,
|
|
36401
|
+
provider TEXT,
|
|
36402
|
+
transport TEXT NOT NULL,
|
|
36403
|
+
upstream_connection_id TEXT,
|
|
36404
|
+
label TEXT,
|
|
36405
|
+
user_label TEXT,
|
|
36406
|
+
provider_account_id TEXT,
|
|
36407
|
+
provider_account_email TEXT,
|
|
36408
|
+
provider_account_name TEXT,
|
|
36409
|
+
provider_identity_checked_at TEXT,
|
|
36410
|
+
provider_identity_source_updated_at TEXT,
|
|
36411
|
+
lifecycle_status TEXT NOT NULL DEFAULT 'pending',
|
|
36412
|
+
operational_status TEXT NOT NULL DEFAULT 'unknown',
|
|
36413
|
+
reconnect_required INTEGER NOT NULL DEFAULT 0,
|
|
36414
|
+
actions_enabled INTEGER NOT NULL DEFAULT 0,
|
|
36415
|
+
read_tools_json TEXT NOT NULL DEFAULT '[]',
|
|
36416
|
+
action_tools_json TEXT NOT NULL DEFAULT '[]',
|
|
36417
|
+
tool_revision TEXT,
|
|
36418
|
+
last_checked_at TEXT,
|
|
36419
|
+
last_successful_call_at TEXT,
|
|
36420
|
+
last_failure_at TEXT,
|
|
36421
|
+
last_failure_code TEXT,
|
|
36422
|
+
last_failure_retryable INTEGER,
|
|
36423
|
+
consecutive_failures INTEGER NOT NULL DEFAULT 0,
|
|
36424
|
+
vault_name TEXT,
|
|
36425
|
+
table_name TEXT,
|
|
36426
|
+
legacy_owner_identity TEXT,
|
|
36427
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
36428
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
36429
|
+
UNIQUE(user_id, provider_config_key, upstream_connection_id)
|
|
36430
|
+
)
|
|
36431
|
+
`);
|
|
36432
|
+
try {
|
|
36433
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN user_label TEXT`);
|
|
36434
|
+
} catch {
|
|
36435
|
+
}
|
|
36436
|
+
try {
|
|
36437
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_id TEXT`);
|
|
36438
|
+
} catch {
|
|
36439
|
+
}
|
|
36440
|
+
try {
|
|
36441
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_email TEXT`);
|
|
36442
|
+
} catch {
|
|
36443
|
+
}
|
|
36444
|
+
try {
|
|
36445
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_name TEXT`);
|
|
36446
|
+
} catch {
|
|
36447
|
+
}
|
|
36448
|
+
try {
|
|
36449
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_checked_at TEXT`);
|
|
36450
|
+
} catch {
|
|
36451
|
+
}
|
|
36452
|
+
try {
|
|
36453
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_source_updated_at TEXT`);
|
|
36454
|
+
} catch {
|
|
36455
|
+
}
|
|
36456
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_user_updated ON service_connections(user_id, updated_at DESC)`);
|
|
36457
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_upstream ON service_connections(provider_config_key, upstream_connection_id)`);
|
|
36458
|
+
await db.execute(`
|
|
36459
|
+
CREATE TABLE IF NOT EXISTS service_connection_health_events (
|
|
36460
|
+
id TEXT PRIMARY KEY,
|
|
36461
|
+
connection_id TEXT NOT NULL REFERENCES service_connections(id) ON DELETE CASCADE,
|
|
36462
|
+
operational_status TEXT NOT NULL,
|
|
36463
|
+
failure_code TEXT,
|
|
36464
|
+
retryable INTEGER,
|
|
36465
|
+
evidence_source TEXT NOT NULL,
|
|
36466
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
36467
|
+
)
|
|
36468
|
+
`);
|
|
36469
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS service_connection_health_events_connection ON service_connection_health_events(connection_id, created_at DESC)`);
|
|
36470
|
+
await db.execute(`
|
|
36471
|
+
CREATE TABLE IF NOT EXISTS service_connection_action_audit (
|
|
36472
|
+
id TEXT PRIMARY KEY,
|
|
36473
|
+
connection_id TEXT NOT NULL REFERENCES service_connections(id) ON DELETE CASCADE,
|
|
36474
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
36475
|
+
tool TEXT NOT NULL,
|
|
36476
|
+
request_id TEXT NOT NULL,
|
|
36477
|
+
status TEXT NOT NULL,
|
|
36478
|
+
error_code TEXT,
|
|
36479
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
36480
|
+
UNIQUE(user_id, request_id)
|
|
36481
|
+
)
|
|
36482
|
+
`);
|
|
36483
|
+
try {
|
|
36484
|
+
await db.execute(`ALTER TABLE service_connection_action_audit ADD COLUMN request_digest TEXT`);
|
|
36485
|
+
} catch {
|
|
36486
|
+
}
|
|
36487
|
+
try {
|
|
36488
|
+
await db.execute(`ALTER TABLE service_connection_action_audit ADD COLUMN result_json TEXT`);
|
|
36489
|
+
} catch {
|
|
36490
|
+
}
|
|
36491
|
+
})().catch((error) => {
|
|
36492
|
+
schemaReady = null;
|
|
36493
|
+
schemaDb5 = null;
|
|
36494
|
+
throw error;
|
|
36495
|
+
});
|
|
36496
|
+
return schemaReady;
|
|
36497
|
+
}
|
|
36498
|
+
function parseTools(value) {
|
|
36499
|
+
if (typeof value !== "string") return [];
|
|
36500
|
+
try {
|
|
36501
|
+
const parsed = JSON.parse(value);
|
|
36502
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
36503
|
+
} catch {
|
|
36504
|
+
return [];
|
|
36505
|
+
}
|
|
36506
|
+
}
|
|
36507
|
+
function rowToRecord(row) {
|
|
36508
|
+
return {
|
|
36509
|
+
id: String(row.id),
|
|
36510
|
+
userId: Number(row.user_id),
|
|
36511
|
+
providerConfigKey: String(row.provider_config_key),
|
|
36512
|
+
provider: typeof row.provider === "string" ? row.provider : null,
|
|
36513
|
+
transport: row.transport === "remote_mcp" ? "remote_mcp" : "nango",
|
|
36514
|
+
upstreamConnectionId: typeof row.upstream_connection_id === "string" ? row.upstream_connection_id : null,
|
|
36515
|
+
label: typeof row.label === "string" ? row.label : null,
|
|
36516
|
+
userLabel: typeof row.user_label === "string" ? row.user_label : null,
|
|
36517
|
+
providerAccountId: typeof row.provider_account_id === "string" ? row.provider_account_id : null,
|
|
36518
|
+
providerAccountEmail: typeof row.provider_account_email === "string" ? row.provider_account_email : null,
|
|
36519
|
+
providerAccountName: typeof row.provider_account_name === "string" ? row.provider_account_name : null,
|
|
36520
|
+
providerIdentityCheckedAt: typeof row.provider_identity_checked_at === "string" ? row.provider_identity_checked_at : null,
|
|
36521
|
+
providerIdentitySourceUpdatedAt: typeof row.provider_identity_source_updated_at === "string" ? row.provider_identity_source_updated_at : null,
|
|
36522
|
+
lifecycleStatus: String(row.lifecycle_status),
|
|
36523
|
+
operationalStatus: String(row.operational_status),
|
|
36524
|
+
reconnectRequired: Number(row.reconnect_required) === 1,
|
|
36525
|
+
actionsEnabled: Number(row.actions_enabled) === 1,
|
|
36526
|
+
readTools: parseTools(row.read_tools_json),
|
|
36527
|
+
actionTools: parseTools(row.action_tools_json),
|
|
36528
|
+
toolRevision: typeof row.tool_revision === "string" ? row.tool_revision : null,
|
|
36529
|
+
lastCheckedAt: typeof row.last_checked_at === "string" ? row.last_checked_at : null,
|
|
36530
|
+
lastSuccessfulCallAt: typeof row.last_successful_call_at === "string" ? row.last_successful_call_at : null,
|
|
36531
|
+
lastFailureAt: typeof row.last_failure_at === "string" ? row.last_failure_at : null,
|
|
36532
|
+
lastFailureCode: typeof row.last_failure_code === "string" ? row.last_failure_code : null,
|
|
36533
|
+
lastFailureRetryable: row.last_failure_retryable === null || row.last_failure_retryable === void 0 ? null : Number(row.last_failure_retryable) === 1,
|
|
36534
|
+
consecutiveFailures: Number(row.consecutive_failures ?? 0),
|
|
36535
|
+
vaultName: typeof row.vault_name === "string" ? row.vault_name : null,
|
|
36536
|
+
tableName: typeof row.table_name === "string" ? row.table_name : null,
|
|
36537
|
+
createdAt: String(row.created_at),
|
|
36538
|
+
updatedAt: String(row.updated_at)
|
|
36539
|
+
};
|
|
36540
|
+
}
|
|
36541
|
+
async function userIdForIdentity(identity) {
|
|
36542
|
+
const user = await getUserByEmail(identity.normalize("NFKC").trim().toLowerCase());
|
|
36543
|
+
if (!user) throw new Error("service_connection_user_not_found");
|
|
36544
|
+
return Number(user.id);
|
|
36545
|
+
}
|
|
36546
|
+
async function reconcileDiscoveredNangoConnections(identity, discovered) {
|
|
36547
|
+
await ensureServiceConnectionsSchema();
|
|
36548
|
+
const db = getDb();
|
|
36549
|
+
const userId = await userIdForIdentity(identity);
|
|
36550
|
+
for (const connection of discovered) {
|
|
36551
|
+
await db.execute({
|
|
36552
|
+
sql: `
|
|
36553
|
+
INSERT INTO service_connections (
|
|
36554
|
+
id, user_id, provider_config_key, provider, transport, upstream_connection_id,
|
|
36555
|
+
label, lifecycle_status, reconnect_required, actions_enabled, legacy_owner_identity, created_at, updated_at
|
|
36556
|
+
) VALUES (?, ?, ?, ?, 'nango', ?, ?, ?, ?, 1, ?, COALESCE(?, datetime('now')), COALESCE(?, datetime('now')))
|
|
36557
|
+
ON CONFLICT(user_id, provider_config_key, upstream_connection_id) DO UPDATE SET
|
|
36558
|
+
provider = excluded.provider,
|
|
36559
|
+
label = COALESCE(excluded.label, service_connections.label),
|
|
36560
|
+
lifecycle_status = excluded.lifecycle_status,
|
|
36561
|
+
reconnect_required = excluded.reconnect_required,
|
|
36562
|
+
updated_at = excluded.updated_at
|
|
36563
|
+
`,
|
|
36564
|
+
args: [
|
|
36565
|
+
(0, import_node_crypto21.randomUUID)(),
|
|
36566
|
+
userId,
|
|
36567
|
+
connection.providerConfigKey,
|
|
36568
|
+
connection.provider,
|
|
36569
|
+
connection.upstreamConnectionId,
|
|
36570
|
+
connection.label,
|
|
36571
|
+
connection.lifecycleStatus,
|
|
36572
|
+
connection.reconnectRequired ? 1 : 0,
|
|
36573
|
+
identity,
|
|
36574
|
+
connection.createdAt,
|
|
36575
|
+
connection.updatedAt
|
|
36576
|
+
]
|
|
36577
|
+
});
|
|
36578
|
+
}
|
|
36579
|
+
return listServiceConnections(identity, "nango");
|
|
36580
|
+
}
|
|
36581
|
+
async function listServiceConnections(identity, transport) {
|
|
36582
|
+
await ensureServiceConnectionsSchema();
|
|
36583
|
+
const userId = await userIdForIdentity(identity);
|
|
36584
|
+
const result = await getDb().execute({
|
|
36585
|
+
sql: `SELECT * FROM service_connections WHERE user_id = ?${transport ? " AND transport = ?" : ""} AND lifecycle_status <> 'disconnected' ORDER BY updated_at DESC`,
|
|
36586
|
+
args: transport ? [userId, transport] : [userId]
|
|
36587
|
+
});
|
|
36588
|
+
return result.rows.map((row) => rowToRecord(row));
|
|
36589
|
+
}
|
|
36590
|
+
async function getOwnedServiceConnection(identity, connectionId) {
|
|
36591
|
+
await ensureServiceConnectionsSchema();
|
|
36592
|
+
const userId = await userIdForIdentity(identity);
|
|
36593
|
+
const result = await getDb().execute({
|
|
36594
|
+
sql: "SELECT * FROM service_connections WHERE id = ? AND user_id = ? LIMIT 1",
|
|
36595
|
+
args: [connectionId, userId]
|
|
36596
|
+
});
|
|
36597
|
+
const row = result.rows[0];
|
|
36598
|
+
return row ? rowToRecord(row) : null;
|
|
36599
|
+
}
|
|
36600
|
+
async function updateServiceConnectionTools(connectionId, readTools, actionTools, toolRevision) {
|
|
36601
|
+
await ensureServiceConnectionsSchema();
|
|
36602
|
+
await getDb().execute({
|
|
36603
|
+
sql: `UPDATE service_connections SET read_tools_json = ?, action_tools_json = ?, tool_revision = ?, updated_at = datetime('now') WHERE id = ?`,
|
|
36604
|
+
args: [JSON.stringify(readTools), JSON.stringify(actionTools), toolRevision, connectionId]
|
|
36605
|
+
});
|
|
36606
|
+
}
|
|
36607
|
+
async function setServiceConnectionProviderIdentity(args) {
|
|
36608
|
+
await ensureServiceConnectionsSchema();
|
|
36609
|
+
await getDb().execute({
|
|
36610
|
+
sql: `
|
|
36611
|
+
UPDATE service_connections SET
|
|
36612
|
+
provider_account_id = ?, provider_account_email = ?, provider_account_name = ?,
|
|
36613
|
+
provider_identity_checked_at = datetime('now'), provider_identity_source_updated_at = ?,
|
|
36614
|
+
updated_at = datetime('now')
|
|
36615
|
+
WHERE id = ?
|
|
36616
|
+
`,
|
|
36617
|
+
args: [
|
|
36618
|
+
args.providerAccountId ?? null,
|
|
36619
|
+
args.providerAccountEmail?.toLowerCase() ?? null,
|
|
36620
|
+
args.providerAccountName ?? null,
|
|
36621
|
+
args.sourceUpdatedAt,
|
|
36622
|
+
args.connectionId
|
|
36623
|
+
]
|
|
36624
|
+
});
|
|
36625
|
+
}
|
|
36626
|
+
async function setServiceConnectionUserLabel(identity, connectionId, userLabel) {
|
|
36627
|
+
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
36628
|
+
if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
|
|
36629
|
+
await getDb().execute({
|
|
36630
|
+
sql: `UPDATE service_connections SET user_label = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
36631
|
+
args: [userLabel, connectionId, connection.userId]
|
|
36632
|
+
});
|
|
36633
|
+
return userLabel;
|
|
36634
|
+
}
|
|
36635
|
+
async function recordServiceConnectionHealth(args) {
|
|
36636
|
+
await ensureServiceConnectionsSchema();
|
|
36637
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
36638
|
+
await getDb().execute({
|
|
36639
|
+
sql: `
|
|
36640
|
+
UPDATE service_connections SET
|
|
36641
|
+
operational_status = ?, last_checked_at = ?,
|
|
36642
|
+
last_successful_call_at = CASE WHEN ? = 'available' THEN ? ELSE last_successful_call_at END,
|
|
36643
|
+
last_failure_at = CASE WHEN ? IN ('degraded', 'unavailable') THEN ? ELSE NULL END,
|
|
36644
|
+
last_failure_code = ?, last_failure_retryable = ?,
|
|
36645
|
+
consecutive_failures = CASE WHEN ? = 'available' THEN 0 ELSE consecutive_failures + 1 END,
|
|
36646
|
+
updated_at = datetime('now')
|
|
36647
|
+
WHERE id = ?
|
|
36648
|
+
`,
|
|
36649
|
+
args: [
|
|
36650
|
+
args.operationalStatus,
|
|
36651
|
+
now,
|
|
36652
|
+
args.operationalStatus,
|
|
36653
|
+
now,
|
|
36654
|
+
args.operationalStatus,
|
|
36655
|
+
now,
|
|
36656
|
+
args.failureCode ?? null,
|
|
36657
|
+
args.retryable == null ? null : args.retryable ? 1 : 0,
|
|
36658
|
+
args.operationalStatus,
|
|
36659
|
+
args.connectionId
|
|
36660
|
+
]
|
|
36661
|
+
});
|
|
36662
|
+
await getDb().execute({
|
|
36663
|
+
sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
36664
|
+
args: [(0, import_node_crypto21.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
|
|
36665
|
+
});
|
|
36666
|
+
}
|
|
36667
|
+
async function setServiceConnectionActions(identity, connectionId, enabled) {
|
|
36668
|
+
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
36669
|
+
if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
|
|
36670
|
+
await getDb().execute({
|
|
36671
|
+
sql: `UPDATE service_connections SET actions_enabled = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
36672
|
+
args: [enabled ? 1 : 0, connectionId, connection.userId]
|
|
36673
|
+
});
|
|
36674
|
+
return enabled;
|
|
36675
|
+
}
|
|
36676
|
+
async function markServiceConnectionDisconnected(identity, connectionId) {
|
|
36677
|
+
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
36678
|
+
if (!connection) throw new Error("service_connection_not_found");
|
|
36679
|
+
await getDb().execute({
|
|
36680
|
+
sql: `UPDATE service_connections SET lifecycle_status = 'disconnected', operational_status = 'unknown', reconnect_required = 0, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
36681
|
+
args: [connectionId, connection.userId]
|
|
36682
|
+
});
|
|
36683
|
+
const remaining = await getDb().execute({
|
|
36684
|
+
sql: `SELECT COUNT(*) AS count FROM service_connections WHERE user_id = ? AND transport = 'nango' AND lifecycle_status = 'connected'`,
|
|
36685
|
+
args: [connection.userId]
|
|
36686
|
+
});
|
|
36687
|
+
return Number(remaining.rows[0]?.count ?? 0);
|
|
36688
|
+
}
|
|
36689
|
+
async function recordServiceConnectionAction(args) {
|
|
36690
|
+
await ensureServiceConnectionsSchema();
|
|
36691
|
+
const connection = await getOwnedServiceConnection(args.identity, args.connectionId);
|
|
36692
|
+
if (!connection) throw new Error("service_connection_not_found");
|
|
36693
|
+
await getDb().execute({
|
|
36694
|
+
sql: `UPDATE service_connection_action_audit SET status = ?, error_code = ?, result_json = ? WHERE user_id = ? AND request_id = ? AND connection_id = ?`,
|
|
36695
|
+
args: [args.status, args.errorCode ?? null, args.result === void 0 ? null : JSON.stringify(args.result), connection.userId, args.requestId, connection.id]
|
|
36696
|
+
});
|
|
36697
|
+
}
|
|
36698
|
+
async function claimServiceConnectionAction(args) {
|
|
36699
|
+
await ensureServiceConnectionsSchema();
|
|
36700
|
+
const connection = await getOwnedServiceConnection(args.identity, args.connectionId);
|
|
36701
|
+
if (!connection) throw new Error("service_connection_not_found");
|
|
36702
|
+
const inserted = await getDb().execute({
|
|
36703
|
+
sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
|
|
36704
|
+
args: [(0, import_node_crypto21.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
|
|
36705
|
+
});
|
|
36706
|
+
if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
|
|
36707
|
+
const existing = await getDb().execute({
|
|
36708
|
+
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`,
|
|
36709
|
+
args: [connection.userId, args.requestId]
|
|
36710
|
+
});
|
|
36711
|
+
const row = existing.rows[0];
|
|
36712
|
+
const conflict = String(row?.connection_id ?? "") !== connection.id || String(row?.tool ?? "") !== args.tool || String(row?.request_digest ?? "") !== args.requestDigest;
|
|
36713
|
+
let result = void 0;
|
|
36714
|
+
if (!conflict && typeof row?.result_json === "string") {
|
|
36715
|
+
try {
|
|
36716
|
+
result = JSON.parse(row.result_json);
|
|
36717
|
+
} catch {
|
|
36718
|
+
result = void 0;
|
|
36719
|
+
}
|
|
36720
|
+
}
|
|
36721
|
+
return {
|
|
36722
|
+
claimed: false,
|
|
36723
|
+
status: String(row?.status ?? "unknown"),
|
|
36724
|
+
errorCode: typeof row?.error_code === "string" ? row.error_code : null,
|
|
36725
|
+
conflict,
|
|
36726
|
+
...result !== void 0 ? { result } : {}
|
|
36727
|
+
};
|
|
36728
|
+
}
|
|
36729
|
+
var import_node_crypto21, schemaReady, schemaDb5;
|
|
36730
|
+
var init_service_connections = __esm({
|
|
36731
|
+
"src/api/service-connections.ts"() {
|
|
36732
|
+
"use strict";
|
|
36733
|
+
import_node_crypto21 = require("crypto");
|
|
36734
|
+
init_db();
|
|
36735
|
+
schemaReady = null;
|
|
36736
|
+
schemaDb5 = null;
|
|
36737
|
+
}
|
|
36738
|
+
});
|
|
36739
|
+
|
|
36291
36740
|
// src/api/connected-account-billing.ts
|
|
36292
36741
|
function connectedBillingPeriodStart(now = /* @__PURE__ */ new Date()) {
|
|
36293
36742
|
return `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
@@ -36302,6 +36751,87 @@ async function chargedConnectionIds(userId, periodStart) {
|
|
|
36302
36751
|
});
|
|
36303
36752
|
return new Set(result.rows.map((row) => String(row.connection_id)));
|
|
36304
36753
|
}
|
|
36754
|
+
async function inheritDisconnectedConnectionCharge(userId, connectionId, periodStart) {
|
|
36755
|
+
await ensureServiceConnectionsSchema();
|
|
36756
|
+
const normalizedUserId = Number(userId);
|
|
36757
|
+
const db = getDb();
|
|
36758
|
+
await db.batch([
|
|
36759
|
+
{
|
|
36760
|
+
sql: `
|
|
36761
|
+
INSERT OR IGNORE INTO connected_account_charge_transfers (
|
|
36762
|
+
user_id, period_start, from_connection_id, to_connection_id, provider_config_key
|
|
36763
|
+
)
|
|
36764
|
+
SELECT ?, ?, previous.id, replacement.id, replacement.provider_config_key
|
|
36765
|
+
FROM service_connections AS replacement
|
|
36766
|
+
JOIN service_connections AS previous
|
|
36767
|
+
ON previous.user_id = replacement.user_id
|
|
36768
|
+
AND previous.transport = 'nango'
|
|
36769
|
+
AND previous.provider_config_key = replacement.provider_config_key
|
|
36770
|
+
AND previous.lifecycle_status = 'disconnected'
|
|
36771
|
+
JOIN connected_account_charges AS charge
|
|
36772
|
+
ON charge.user_id = previous.user_id
|
|
36773
|
+
AND charge.connection_id = previous.id
|
|
36774
|
+
AND charge.period_start = ?
|
|
36775
|
+
WHERE replacement.id = ?
|
|
36776
|
+
AND replacement.user_id = ?
|
|
36777
|
+
AND replacement.transport = 'nango'
|
|
36778
|
+
AND replacement.lifecycle_status IN ('connected', 'needs_reauth')
|
|
36779
|
+
AND NOT EXISTS (
|
|
36780
|
+
SELECT 1 FROM connected_account_charges AS existing
|
|
36781
|
+
WHERE existing.user_id = replacement.user_id
|
|
36782
|
+
AND existing.connection_id = replacement.id
|
|
36783
|
+
AND existing.period_start = ?
|
|
36784
|
+
)
|
|
36785
|
+
ORDER BY previous.updated_at DESC, previous.id ASC
|
|
36786
|
+
LIMIT 1
|
|
36787
|
+
`,
|
|
36788
|
+
args: [normalizedUserId, periodStart, periodStart, connectionId, normalizedUserId, periodStart]
|
|
36789
|
+
},
|
|
36790
|
+
{
|
|
36791
|
+
sql: `
|
|
36792
|
+
UPDATE connected_account_charges
|
|
36793
|
+
SET connection_id = ?
|
|
36794
|
+
WHERE user_id = ?
|
|
36795
|
+
AND period_start = ?
|
|
36796
|
+
AND connection_id = (
|
|
36797
|
+
SELECT transfer.from_connection_id
|
|
36798
|
+
FROM connected_account_charge_transfers AS transfer
|
|
36799
|
+
JOIN connected_account_charges AS current_charge
|
|
36800
|
+
ON current_charge.user_id = transfer.user_id
|
|
36801
|
+
AND current_charge.period_start = transfer.period_start
|
|
36802
|
+
AND current_charge.connection_id = transfer.from_connection_id
|
|
36803
|
+
WHERE transfer.user_id = ?
|
|
36804
|
+
AND transfer.period_start = ?
|
|
36805
|
+
AND transfer.to_connection_id = ?
|
|
36806
|
+
ORDER BY transfer.transferred_at DESC, transfer.id DESC
|
|
36807
|
+
LIMIT 1
|
|
36808
|
+
)
|
|
36809
|
+
AND NOT EXISTS (
|
|
36810
|
+
SELECT 1 FROM connected_account_charges AS existing
|
|
36811
|
+
WHERE existing.user_id = ?
|
|
36812
|
+
AND existing.connection_id = ?
|
|
36813
|
+
AND existing.period_start = ?
|
|
36814
|
+
)
|
|
36815
|
+
`,
|
|
36816
|
+
args: [
|
|
36817
|
+
connectionId,
|
|
36818
|
+
normalizedUserId,
|
|
36819
|
+
periodStart,
|
|
36820
|
+
normalizedUserId,
|
|
36821
|
+
periodStart,
|
|
36822
|
+
connectionId,
|
|
36823
|
+
normalizedUserId,
|
|
36824
|
+
connectionId,
|
|
36825
|
+
periodStart
|
|
36826
|
+
]
|
|
36827
|
+
}
|
|
36828
|
+
], "write");
|
|
36829
|
+
const inherited = await db.execute({
|
|
36830
|
+
sql: "SELECT 1 FROM connected_account_charges WHERE user_id = ? AND connection_id = ? AND period_start = ? LIMIT 1",
|
|
36831
|
+
args: [normalizedUserId, connectionId, periodStart]
|
|
36832
|
+
});
|
|
36833
|
+
return inherited.rows.length > 0;
|
|
36834
|
+
}
|
|
36305
36835
|
async function getConnectedAccountBillingState2(userId) {
|
|
36306
36836
|
const result = await getDb().execute({
|
|
36307
36837
|
sql: "SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",
|
|
@@ -36430,6 +36960,7 @@ async function reconcileConnectedAccountBilling(user, connections) {
|
|
|
36430
36960
|
const alreadyCharged = await chargedConnectionIds(user.id, periodStart);
|
|
36431
36961
|
const unpaid = billableConnectionIds.filter((id) => !alreadyCharged.has(id));
|
|
36432
36962
|
for (const connectionId of unpaid) {
|
|
36963
|
+
if (await inheritDisconnectedConnectionCharge(user.id, connectionId, periodStart)) continue;
|
|
36433
36964
|
const feeMc = CONNECTED_ACTIVE_CONNECTION_MONTHLY_MC;
|
|
36434
36965
|
const debit = await debitMcIdempotent(
|
|
36435
36966
|
user.id,
|
|
@@ -36491,6 +37022,7 @@ var init_connected_account_billing = __esm({
|
|
|
36491
37022
|
init_db();
|
|
36492
37023
|
init_rates();
|
|
36493
37024
|
init_connected_cost_telemetry();
|
|
37025
|
+
init_service_connections();
|
|
36494
37026
|
DEFAULT_CONNECTED_ACCOUNT_PRICE_ID = "price_1TtYPAS8aAcsk3TGHOgrDZiQ";
|
|
36495
37027
|
BILLABLE_LIFECYCLE = /* @__PURE__ */ new Set(["connected", "needs_reauth"]);
|
|
36496
37028
|
ConnectedAccountBillingError = class extends Error {
|
|
@@ -40059,7 +40591,7 @@ async function applyAdjustment(input) {
|
|
|
40059
40591
|
const credits = validateCredits(input.credits, input.confirmLarge === true);
|
|
40060
40592
|
const reason = validateReason(input.reason);
|
|
40061
40593
|
const actor = validateActor(input.actor);
|
|
40062
|
-
const reference = input.reference?.trim() || `adj_${(0,
|
|
40594
|
+
const reference = input.reference?.trim() || `adj_${(0, import_node_crypto22.randomUUID)()}`;
|
|
40063
40595
|
if (reference.length > 120) throw new AdminCreditError(400, "reference must be at most 120 characters");
|
|
40064
40596
|
const db = getDb();
|
|
40065
40597
|
const existing = await db.execute({
|
|
@@ -40204,11 +40736,11 @@ async function lookupAccount(target, limit = 20) {
|
|
|
40204
40736
|
}))
|
|
40205
40737
|
};
|
|
40206
40738
|
}
|
|
40207
|
-
var
|
|
40739
|
+
var import_node_crypto22, MIN_ADJUSTMENT_CREDITS, LARGE_ADJUSTMENT_CREDITS, MAX_ADJUSTMENT_CREDITS, MIN_REASON_LENGTH, MAX_REASON_LENGTH, MAX_ACTOR_LENGTH, AdminCreditError;
|
|
40208
40740
|
var init_admin_credits = __esm({
|
|
40209
40741
|
"src/api/admin-credits.ts"() {
|
|
40210
40742
|
"use strict";
|
|
40211
|
-
|
|
40743
|
+
import_node_crypto22 = require("crypto");
|
|
40212
40744
|
init_db();
|
|
40213
40745
|
init_rates();
|
|
40214
40746
|
MIN_ADJUSTMENT_CREDITS = 1;
|
|
@@ -42173,7 +42705,7 @@ async function packageMapsMedia(args) {
|
|
|
42173
42705
|
files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
|
|
42174
42706
|
files.push({ path: "images.jsonl", content: Buffer.from(cleanImages.map((image) => JSON.stringify(image)).join("\n") + "\n") });
|
|
42175
42707
|
const archive = await zipBuffer(files);
|
|
42176
|
-
const id = (0,
|
|
42708
|
+
const id = (0, import_node_crypto23.randomBytes)(6).toString("hex");
|
|
42177
42709
|
const pointer = await createPrivateArtifact({
|
|
42178
42710
|
policy: policy3(),
|
|
42179
42711
|
ownerId: args.ownerId,
|
|
@@ -42186,13 +42718,13 @@ async function packageMapsMedia(args) {
|
|
|
42186
42718
|
const localPath = token() ? null : (0, import_node_path13.join)(process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0, import_node_path13.join)((0, import_node_os9.homedir)(), "Downloads", "mcp-scraper"), "blobs", pointer.artifactId);
|
|
42187
42719
|
return { media: args.media, artifact: { ...pointer, localPath } };
|
|
42188
42720
|
}
|
|
42189
|
-
var import_node_os9, import_node_path13,
|
|
42721
|
+
var import_node_os9, import_node_path13, import_node_crypto23, import_yazl2, import_p_limit5, MAPS_MEDIA_ARTIFACT_PREFIX, MAPS_MEDIA_ARTIFACT_TTL_MS, MAPS_MEDIA_DOWNLOAD_TTL_MS, MAX_IMAGE_BYTES2, MAX_ARCHIVE_IMAGE_BYTES, MAX_INLINE_IMAGE_BYTES, MAX_INLINE_TOTAL_BYTES, DOWNLOAD_CONCURRENCY, MAX_REDIRECTS;
|
|
42190
42722
|
var init_maps_media_artifacts = __esm({
|
|
42191
42723
|
"src/api/maps-media-artifacts.ts"() {
|
|
42192
42724
|
"use strict";
|
|
42193
42725
|
import_node_os9 = require("os");
|
|
42194
42726
|
import_node_path13 = require("path");
|
|
42195
|
-
|
|
42727
|
+
import_node_crypto23 = require("crypto");
|
|
42196
42728
|
import_yazl2 = require("yazl");
|
|
42197
42729
|
import_p_limit5 = __toESM(require("p-limit"), 1);
|
|
42198
42730
|
init_private_artifacts();
|
|
@@ -42816,7 +43348,7 @@ function retryDelaySeconds(attempts) {
|
|
|
42816
43348
|
}
|
|
42817
43349
|
async function dispatchPendingDirectoryWorkflows(limit = 25) {
|
|
42818
43350
|
const rows = await claimDirectoryWorkflowOutbox({
|
|
42819
|
-
workerId: `directory-dispatch-${process.pid}-${(0,
|
|
43351
|
+
workerId: `directory-dispatch-${process.pid}-${(0, import_node_crypto24.randomUUID)().slice(0, 8)}`,
|
|
42820
43352
|
limit
|
|
42821
43353
|
});
|
|
42822
43354
|
const result = { claimed: rows.length, dispatched: 0, failed: 0 };
|
|
@@ -42838,11 +43370,11 @@ async function dispatchPendingDirectoryWorkflows(limit = 25) {
|
|
|
42838
43370
|
}
|
|
42839
43371
|
return result;
|
|
42840
43372
|
}
|
|
42841
|
-
var
|
|
43373
|
+
var import_node_crypto24;
|
|
42842
43374
|
var init_directory_workflow_dispatch = __esm({
|
|
42843
43375
|
"src/api/directory-workflow-dispatch.ts"() {
|
|
42844
43376
|
"use strict";
|
|
42845
|
-
|
|
43377
|
+
import_node_crypto24 = require("crypto");
|
|
42846
43378
|
init_client();
|
|
42847
43379
|
init_directory_workflow_repository();
|
|
42848
43380
|
}
|
|
@@ -42854,7 +43386,7 @@ function safeOptions(options) {
|
|
|
42854
43386
|
return safe2;
|
|
42855
43387
|
}
|
|
42856
43388
|
function requestFingerprint(options) {
|
|
42857
|
-
return (0,
|
|
43389
|
+
return (0, import_node_crypto25.createHash)("sha256").update(JSON.stringify(safeOptions(options))).digest("hex");
|
|
42858
43390
|
}
|
|
42859
43391
|
function idempotencyKey(raw) {
|
|
42860
43392
|
if (raw !== void 0) {
|
|
@@ -42863,14 +43395,14 @@ function idempotencyKey(raw) {
|
|
|
42863
43395
|
if (trimmed.length > 500) return { ok: false, message: "Idempotency-Key must be 500 characters or fewer." };
|
|
42864
43396
|
return { ok: true, key: trimmed };
|
|
42865
43397
|
}
|
|
42866
|
-
return { ok: true, key: `directory-${(0,
|
|
43398
|
+
return { ok: true, key: `directory-${(0, import_node_crypto25.randomUUID)()}` };
|
|
42867
43399
|
}
|
|
42868
43400
|
function debitKeyFor(userId, responseKey) {
|
|
42869
|
-
const digest2 = (0,
|
|
43401
|
+
const digest2 = (0, import_node_crypto25.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
|
|
42870
43402
|
return `directory-workflow:${userId}:${digest2}`;
|
|
42871
43403
|
}
|
|
42872
43404
|
function jobId() {
|
|
42873
|
-
return `dir_${(0,
|
|
43405
|
+
return `dir_${(0, import_node_crypto25.randomUUID)().replace(/-/g, "")}`;
|
|
42874
43406
|
}
|
|
42875
43407
|
function publicStatus(job, result) {
|
|
42876
43408
|
if (job.status === "failed") return "failed";
|
|
@@ -42979,7 +43511,7 @@ async function runSynchronously(c, user, options, plan, responseKey) {
|
|
|
42979
43511
|
const csv = renderDirectoryWorkflowCsv(result);
|
|
42980
43512
|
const artifact = await createDirectoryCsvArtifact({
|
|
42981
43513
|
ownerId: String(user.id),
|
|
42982
|
-
jobId: (0,
|
|
43514
|
+
jobId: (0, import_node_crypto25.createHash)("sha256").update(debitKey2).digest("hex").slice(0, 32),
|
|
42983
43515
|
createdAt: result.extractedAt,
|
|
42984
43516
|
filename: `${options.state}-${options.query}-directory.csv`,
|
|
42985
43517
|
csv,
|
|
@@ -43035,11 +43567,11 @@ async function runSynchronously(c, user, options, plan, responseKey) {
|
|
|
43035
43567
|
await releaseConcurrencyGate(gate.lockId);
|
|
43036
43568
|
}
|
|
43037
43569
|
}
|
|
43038
|
-
var
|
|
43570
|
+
var import_node_crypto25, import_hono16, directoryApp;
|
|
43039
43571
|
var init_directory_routes = __esm({
|
|
43040
43572
|
"src/api/directory-routes.ts"() {
|
|
43041
43573
|
"use strict";
|
|
43042
|
-
|
|
43574
|
+
import_node_crypto25 = require("crypto");
|
|
43043
43575
|
import_hono16 = require("hono");
|
|
43044
43576
|
init_api_auth();
|
|
43045
43577
|
init_db();
|
|
@@ -43257,15 +43789,15 @@ function bounded(value, field, min, max) {
|
|
|
43257
43789
|
return normalized;
|
|
43258
43790
|
}
|
|
43259
43791
|
function newLeadListUploadId() {
|
|
43260
|
-
return `upl_${(0,
|
|
43792
|
+
return `upl_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
|
|
43261
43793
|
}
|
|
43262
43794
|
function newImportedLeadListId() {
|
|
43263
|
-
return `lst_${(0,
|
|
43795
|
+
return `lst_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
|
|
43264
43796
|
}
|
|
43265
43797
|
async function ensureLeadListImportRepositorySchema() {
|
|
43266
43798
|
const db = getDb();
|
|
43267
|
-
if (
|
|
43268
|
-
|
|
43799
|
+
if (schemaDb6 !== db) {
|
|
43800
|
+
schemaDb6 = db;
|
|
43269
43801
|
schemaPromise5 = null;
|
|
43270
43802
|
}
|
|
43271
43803
|
if (!schemaPromise5) {
|
|
@@ -43460,13 +43992,13 @@ async function deleteExpiredLeadListInputRecords(now = /* @__PURE__ */ new Date(
|
|
|
43460
43992
|
], "write");
|
|
43461
43993
|
return { uploads: Number(uploads.rowsAffected ?? 0), lists: Number(lists.rowsAffected ?? 0) };
|
|
43462
43994
|
}
|
|
43463
|
-
var
|
|
43995
|
+
var import_node_crypto26, schemaDb6, schemaPromise5;
|
|
43464
43996
|
var init_lead_list_import_repository = __esm({
|
|
43465
43997
|
"src/api/lead-list-import-repository.ts"() {
|
|
43466
43998
|
"use strict";
|
|
43467
|
-
|
|
43999
|
+
import_node_crypto26 = require("crypto");
|
|
43468
44000
|
init_db();
|
|
43469
|
-
|
|
44001
|
+
schemaDb6 = null;
|
|
43470
44002
|
schemaPromise5 = null;
|
|
43471
44003
|
}
|
|
43472
44004
|
});
|
|
@@ -43506,7 +44038,7 @@ function safeFilenameHint(value) {
|
|
|
43506
44038
|
return cleaned || null;
|
|
43507
44039
|
}
|
|
43508
44040
|
function requestFingerprint2(filenameHint) {
|
|
43509
|
-
return (0,
|
|
44041
|
+
return (0, import_node_crypto27.createHash)("sha256").update(JSON.stringify({ filenameHint })).digest("hex");
|
|
43510
44042
|
}
|
|
43511
44043
|
function publicBaseUrl() {
|
|
43512
44044
|
const configured = process.env.MCP_SCRAPER_PUBLIC_BASE_URL?.trim() || process.env.PUBLIC_BASE_URL?.trim() || (process.env.VERCEL_PROJECT_PRODUCTION_URL ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}` : "") || "https://mcpscraper.dev";
|
|
@@ -43662,7 +44194,7 @@ async function inspectLeadListUpload(uploadId, ownerId2) {
|
|
|
43662
44194
|
maxBytes: LEAD_LIST_UPLOAD_MAX_BYTES
|
|
43663
44195
|
});
|
|
43664
44196
|
if (!buffer) return null;
|
|
43665
|
-
const sha2565 = (0,
|
|
44197
|
+
const sha2565 = (0, import_node_crypto27.createHash)("sha256").update(buffer).digest("hex");
|
|
43666
44198
|
const completed = await completeLeadListUpload({
|
|
43667
44199
|
id: record.id,
|
|
43668
44200
|
ownerId: record.ownerId,
|
|
@@ -43786,11 +44318,11 @@ async function cleanupExpiredLeadListInputs(args = {}) {
|
|
|
43786
44318
|
const records = await deleteExpiredLeadListInputRecords(now);
|
|
43787
44319
|
return { deletedBlobs, deletedUploadRecords: records.uploads, deletedListRecords: records.lists };
|
|
43788
44320
|
}
|
|
43789
|
-
var
|
|
44321
|
+
var import_node_crypto27, import_promises11, import_node_os10, import_node_path14, LEAD_LIST_UPLOAD_PREFIX, LEAD_LIST_NORMALIZED_PREFIX, LEAD_LIST_UPLOAD_MAX_BYTES, LEAD_LIST_UPLOAD_URL_TTL_MS, LEAD_LIST_UPLOAD_SOURCE_TTL_MS, LEAD_LIST_NORMALIZED_TTL_MS, LEAD_LIST_DOWNLOAD_TTL_MS, LEAD_LIST_UPLOAD_MIME_TYPES;
|
|
43790
44322
|
var init_lead_list_input_artifacts = __esm({
|
|
43791
44323
|
"src/api/lead-list-input-artifacts.ts"() {
|
|
43792
44324
|
"use strict";
|
|
43793
|
-
|
|
44325
|
+
import_node_crypto27 = require("crypto");
|
|
43794
44326
|
import_promises11 = require("fs/promises");
|
|
43795
44327
|
import_node_os10 = require("os");
|
|
43796
44328
|
import_node_path14 = require("path");
|
|
@@ -44173,7 +44705,7 @@ function canonicalize(value) {
|
|
|
44173
44705
|
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${canonicalize(object[key])}`).join(",")}}`;
|
|
44174
44706
|
}
|
|
44175
44707
|
function fingerprint(value) {
|
|
44176
|
-
return (0,
|
|
44708
|
+
return (0, import_node_crypto28.createHash)("sha256").update(canonicalize(value)).digest("hex");
|
|
44177
44709
|
}
|
|
44178
44710
|
async function safeJson(c) {
|
|
44179
44711
|
try {
|
|
@@ -44250,11 +44782,11 @@ function textDelimiter(raw, text2, observedMime) {
|
|
|
44250
44782
|
if (observedMime === "text/tab-separated-values") return " ";
|
|
44251
44783
|
return detectLeadListTextDelimiter(text2);
|
|
44252
44784
|
}
|
|
44253
|
-
var
|
|
44785
|
+
var import_node_crypto28, import_hono17, leadListInputApp;
|
|
44254
44786
|
var init_lead_list_input_routes = __esm({
|
|
44255
44787
|
"src/api/lead-list-input-routes.ts"() {
|
|
44256
44788
|
"use strict";
|
|
44257
|
-
|
|
44789
|
+
import_node_crypto28 = require("crypto");
|
|
44258
44790
|
import_hono17 = require("hono");
|
|
44259
44791
|
init_api_auth();
|
|
44260
44792
|
init_lead_list_input_artifacts();
|
|
@@ -44446,7 +44978,7 @@ function retryDelaySeconds2(attempts) {
|
|
|
44446
44978
|
return Math.min(3600, Math.max(15, 15 * 2 ** Math.min(8, Math.max(0, attempts - 1))));
|
|
44447
44979
|
}
|
|
44448
44980
|
async function dispatchPendingLeadListEnrichments(limit = 25) {
|
|
44449
|
-
const rows = await claimLeadListEnrichmentOutbox({ workerId: `lead-list-dispatch-${process.pid}-${(0,
|
|
44981
|
+
const rows = await claimLeadListEnrichmentOutbox({ workerId: `lead-list-dispatch-${process.pid}-${(0, import_node_crypto29.randomUUID)().slice(0, 8)}`, limit });
|
|
44450
44982
|
const result = { claimed: rows.length, dispatched: 0, failed: 0 };
|
|
44451
44983
|
for (const row of rows) {
|
|
44452
44984
|
if (!row.claimToken) continue;
|
|
@@ -44465,11 +44997,11 @@ async function dispatchPendingLeadListEnrichments(limit = 25) {
|
|
|
44465
44997
|
}
|
|
44466
44998
|
return result;
|
|
44467
44999
|
}
|
|
44468
|
-
var
|
|
45000
|
+
var import_node_crypto29;
|
|
44469
45001
|
var init_lead_list_enrichment_dispatch = __esm({
|
|
44470
45002
|
"src/api/lead-list-enrichment-dispatch.ts"() {
|
|
44471
45003
|
"use strict";
|
|
44472
|
-
|
|
45004
|
+
import_node_crypto29 = require("crypto");
|
|
44473
45005
|
init_client();
|
|
44474
45006
|
init_lead_list_enrichment_repository();
|
|
44475
45007
|
}
|
|
@@ -44485,15 +45017,15 @@ function idempotencyKey2(raw) {
|
|
|
44485
45017
|
return { ok: true, value };
|
|
44486
45018
|
}
|
|
44487
45019
|
function newJobId() {
|
|
44488
|
-
return `lle_${(0,
|
|
45020
|
+
return `lle_${(0, import_node_crypto30.randomUUID)().replace(/-/g, "")}`;
|
|
44489
45021
|
}
|
|
44490
45022
|
function debitKeyFor2(userId, key) {
|
|
44491
|
-
const digest2 = (0,
|
|
45023
|
+
const digest2 = (0, import_node_crypto30.createHash)("sha256").update(String(userId)).update("\0").update(key).digest("hex");
|
|
44492
45024
|
return `lead-list-enrichment:${userId}:${digest2}`;
|
|
44493
45025
|
}
|
|
44494
45026
|
function inlineSourceDigest(headers, rows) {
|
|
44495
45027
|
const values = rows.map((row) => headers.map((header) => row[header] ?? null));
|
|
44496
|
-
return (0,
|
|
45028
|
+
return (0, import_node_crypto30.createHash)("sha256").update(stableCanonicalJson({ headers, values })).digest("hex");
|
|
44497
45029
|
}
|
|
44498
45030
|
function issueMessage(error) {
|
|
44499
45031
|
const issue = error.issues[0];
|
|
@@ -44691,11 +45223,11 @@ async function resolveInput(ownerId2, parsed) {
|
|
|
44691
45223
|
sourceDigest: sourceDigest ?? inlineSourceDigest(normalized.headers, rows)
|
|
44692
45224
|
};
|
|
44693
45225
|
}
|
|
44694
|
-
var
|
|
45226
|
+
var import_node_crypto30, import_hono18, import_zod31, SourceSchema, StartSchema, TERMINAL, leadListEnrichmentApp;
|
|
44695
45227
|
var init_lead_list_enrichment_routes = __esm({
|
|
44696
45228
|
"src/api/lead-list-enrichment-routes.ts"() {
|
|
44697
45229
|
"use strict";
|
|
44698
|
-
|
|
45230
|
+
import_node_crypto30 = require("crypto");
|
|
44699
45231
|
import_hono18 = require("hono");
|
|
44700
45232
|
import_zod31 = require("zod");
|
|
44701
45233
|
init_api_auth();
|
|
@@ -48162,7 +48694,7 @@ async function readManifestFromSummary(summary) {
|
|
|
48162
48694
|
function webhookSignature(body, timestamp2) {
|
|
48163
48695
|
const secret2 = process.env.MCP_SCRAPER_WEBHOOK_SECRET?.trim();
|
|
48164
48696
|
if (!secret2) return null;
|
|
48165
|
-
return (0,
|
|
48697
|
+
return (0, import_node_crypto31.createHmac)("sha256", secret2).update(`${timestamp2}.${body}`).digest("hex");
|
|
48166
48698
|
}
|
|
48167
48699
|
async function deliverWorkflowWebhook(input) {
|
|
48168
48700
|
if (!input.webhookUrl) return;
|
|
@@ -48369,11 +48901,11 @@ async function dispatchDueWorkflowSchedules(apiUrl, limit = 3) {
|
|
|
48369
48901
|
}
|
|
48370
48902
|
return { dispatched: results.length, results };
|
|
48371
48903
|
}
|
|
48372
|
-
var
|
|
48904
|
+
var import_node_crypto31, import_promises14, import_hono21, import_zod41, workflowApp, WorkflowInputSchema, WorkflowIdSchema, CadenceSchema, ScheduleStatusSchema, RunBodySchema, ScheduleCreateSchema, SchedulePatchSchema, TERMINAL_RUN_STATUSES;
|
|
48373
48905
|
var init_workflow_routes = __esm({
|
|
48374
48906
|
"src/api/workflow-routes.ts"() {
|
|
48375
48907
|
"use strict";
|
|
48376
|
-
|
|
48908
|
+
import_node_crypto31 = require("crypto");
|
|
48377
48909
|
import_promises14 = require("fs/promises");
|
|
48378
48910
|
import_hono21 = require("hono");
|
|
48379
48911
|
import_zod41 = require("zod");
|
|
@@ -48654,7 +49186,7 @@ var init_workflow_routes = __esm({
|
|
|
48654
49186
|
// src/serp-intelligence/page-snapshot-extractor.ts
|
|
48655
49187
|
function sha2562(value) {
|
|
48656
49188
|
if (!value) return null;
|
|
48657
|
-
return (0,
|
|
49189
|
+
return (0, import_node_crypto32.createHash)("sha256").update(value).digest("hex");
|
|
48658
49190
|
}
|
|
48659
49191
|
function countWords(markdown) {
|
|
48660
49192
|
const matches = markdown.trim().match(/\b[\p{L}\p{N}][\p{L}\p{N}'-]*\b/gu);
|
|
@@ -48954,11 +49486,11 @@ async function capturePageSnapshots(targets, options = {}) {
|
|
|
48954
49486
|
}
|
|
48955
49487
|
};
|
|
48956
49488
|
}
|
|
48957
|
-
var
|
|
49489
|
+
var import_node_crypto32, import_p_limit6, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_CONTENT_CHARS;
|
|
48958
49490
|
var init_page_snapshot_extractor = __esm({
|
|
48959
49491
|
"src/serp-intelligence/page-snapshot-extractor.ts"() {
|
|
48960
49492
|
"use strict";
|
|
48961
|
-
|
|
49493
|
+
import_node_crypto32 = require("crypto");
|
|
48962
49494
|
import_p_limit6 = __toESM(require("p-limit"), 1);
|
|
48963
49495
|
init_kpo_extractor();
|
|
48964
49496
|
init_url_utils();
|
|
@@ -49455,21 +49987,21 @@ async function logRequestEventBestEffort(input) {
|
|
|
49455
49987
|
}
|
|
49456
49988
|
}
|
|
49457
49989
|
function captureBillingKeys(userId, suppliedKey, body) {
|
|
49458
|
-
const responseKey = suppliedKey?.trim() || (0,
|
|
49459
|
-
const requestFingerprint3 = (0,
|
|
49460
|
-
const keyDigest = (0,
|
|
49990
|
+
const responseKey = suppliedKey?.trim() || (0, import_node_crypto33.randomUUID)();
|
|
49991
|
+
const requestFingerprint3 = (0, import_node_crypto33.createHash)("sha256").update(JSON.stringify(body)).digest("hex");
|
|
49992
|
+
const keyDigest = (0, import_node_crypto33.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
|
|
49461
49993
|
return {
|
|
49462
49994
|
responseKey,
|
|
49463
49995
|
debitKey: `serp-capture:${userId}:${keyDigest}`,
|
|
49464
49996
|
debitDescription: `${body.query} [request:${requestFingerprint3}]`
|
|
49465
49997
|
};
|
|
49466
49998
|
}
|
|
49467
|
-
var import_hono22,
|
|
49999
|
+
var import_hono22, import_node_crypto33, SERP_INTELLIGENCE_RATE_LIMIT, SERP_INTELLIGENCE_RATE_WINDOW_SECONDS, POST_CAPTURE_ROUTE_LABEL, POST_PAGE_SNAPSHOTS_ROUTE_LABEL, SERP_CAPTURE_BILLING_SOURCE, serpIntelligenceApp;
|
|
49468
50000
|
var init_serp_intelligence_routes = __esm({
|
|
49469
50001
|
"src/api/serp-intelligence-routes.ts"() {
|
|
49470
50002
|
"use strict";
|
|
49471
50003
|
import_hono22 = require("hono");
|
|
49472
|
-
|
|
50004
|
+
import_node_crypto33 = require("crypto");
|
|
49473
50005
|
init_browser_service_env();
|
|
49474
50006
|
init_page_snapshot_extractor();
|
|
49475
50007
|
init_serp_capture_service();
|
|
@@ -49706,7 +50238,7 @@ var PACKAGE_VERSION;
|
|
|
49706
50238
|
var init_version = __esm({
|
|
49707
50239
|
"src/version.ts"() {
|
|
49708
50240
|
"use strict";
|
|
49709
|
-
PACKAGE_VERSION = "0.
|
|
50241
|
+
PACKAGE_VERSION = "0.67.0";
|
|
49710
50242
|
}
|
|
49711
50243
|
});
|
|
49712
50244
|
|
|
@@ -49775,11 +50307,21 @@ seam is noted so you can chain them.
|
|
|
49775
50307
|
- Plan a new or edited entity -> **commons_prepare_entity**. This is the Commons equivalent of
|
|
49776
50308
|
prepare-memory-write: it returns the live entity profile contract, duplicate candidates, tag reuse/review/create
|
|
49777
50309
|
guidance, recommended sections, optional sections, and heading guidance before any write occurs.
|
|
49778
|
-
-
|
|
49779
|
-
|
|
49780
|
-
|
|
50310
|
+
- Host a featured image -> **commons_host_image**. The normal write sequence is **commons_prepare_entity ->
|
|
50311
|
+
compose the payload -> commons_host_image -> commons_validate_entity -> commons_submit_entity**. Put the
|
|
50312
|
+
permanent returned URL in featuredImage.url. Use imageBase64 for pasted or AI-generated bytes, artifactId for an
|
|
50313
|
+
owner-scoped MCP Scraper image artifact, or sourceUrl for either a public HTTPS image or a webpage containing one.
|
|
50314
|
+
Webpage discovery tries Open Graph, Twitter, JSON-LD, then meaningful content images. Never pass a caller-local
|
|
50315
|
+
path, opaque chat attachment reference, or private/authenticated URL.
|
|
50316
|
+
- Validate a composed entity without writing -> **commons_validate_entity**. Use it after image hosting and before
|
|
50317
|
+
submit. Validation does not fetch or store image bytes; an external/unregistered featured image is reported in
|
|
50318
|
+
imageDiagnostics as not publishable until commons_host_image returns its permanent Commons URL. Validation also
|
|
50319
|
+
catches missing source/body evidence, duplicate candidates, and unsupported placeholder sections.
|
|
49781
50320
|
- Create or edit a public wiki entity -> **commons_submit_entity**. This is a governed proposal write,
|
|
49782
|
-
not direct rendered HTML editing.
|
|
50321
|
+
not direct rendered HTML editing. Submit retains defensive auto-hosting for a stable direct public HTTPS image,
|
|
50322
|
+
but image ingress failure returns an actionable tool error: host different bytes or a different stable source,
|
|
50323
|
+
replace featuredImage.url, validate again, and retry the same intended write with the same idempotency key.
|
|
50324
|
+
Pick the closest entity type
|
|
49783
50325
|
first: SoftwareApplication, Organization, Person, Event, Place, Taxon, ScienceConcept, MathConcept, TechArticle,
|
|
49784
50326
|
Trail, or PublicArticle.
|
|
49785
50327
|
Creating a new entity costs 10 Credits; editing an existing entity costs 2 Credits. Preparation, validation,
|
|
@@ -51521,7 +52063,7 @@ var init_contracts = __esm({
|
|
|
51521
52063
|
});
|
|
51522
52064
|
|
|
51523
52065
|
// src/mcp/mcp-tool-schemas.ts
|
|
51524
|
-
var import_zod45, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, HarvestPaaHostedInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AnalyzeSiteSimilarityInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, SiteExportReadInputSchema, SiteExportImageInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LeadScalarSchema2, LeadRowSchema, LeadColumnMapSchema2, LeadRowsImportSourceSchema, LeadCsvTextImportSourceSchema, LeadUploadImportSourceSchema, LeadListUploadStartInputSchema, LeadListImportInputSchema, LeadRowsEnrichmentSourceSchema, ImportedLeadListSourceSchema, LeadListEnrichInputSchema, LeadListEnrichStatusInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsGetEntityLinksetInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsClaimInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsHostImageInputSchema, CommonsGetProposalInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomImageSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, CommonsPublicationSubdomainSchema, CommonsPreparePublicationInputSchema, CommonsValidatePublicationInputSchema, CommonsClaimPublicationInputSchema, CommonsPublishEditorialInputSchema, CommonsUpdateEditorialArticleInputSchema, CommonsGetPublicationInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LeadSuggestedColumnOutputSchema, LeadColumnMapSuggestionOutputSchema, LeadArtifactOutputSchema, LeadListUploadStartOutputSchema, LeadListImportOutputSchema, LeadCandidateOutputSchema, LeadProgressOutputSchema, LeadBillingOutputSchema, LeadAssociatedPersonSourceOutputSchema, LeadAssociatedPersonOutputSchema, LeadSampleRowOutputSchema, LeadListEnrichmentOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, PageMediaAssetOutput, PageMediaArtifactOutput, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, SiteExportReadOutputSchema, SiteExportImageOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
|
|
52066
|
+
var import_zod45, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, HarvestPaaHostedInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AnalyzeSiteSimilarityInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, SiteExportReadInputSchema, SiteExportImageInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LeadScalarSchema2, LeadRowSchema, LeadColumnMapSchema2, LeadRowsImportSourceSchema, LeadCsvTextImportSourceSchema, LeadUploadImportSourceSchema, LeadListUploadStartInputSchema, LeadListImportInputSchema, LeadRowsEnrichmentSourceSchema, ImportedLeadListSourceSchema, LeadListEnrichInputSchema, LeadListEnrichStatusInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsGetEntityLinksetInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsClaimInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsHostImageInputSchema, CommonsHostImageOutputSchema, CommonsGetProposalInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, CommonsFeaturedImageDiagnosticsOutputSchema, CommonsValidateEntityOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomImageSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, CommonsPublicationSubdomainSchema, CommonsPreparePublicationInputSchema, CommonsValidatePublicationInputSchema, CommonsClaimPublicationInputSchema, CommonsPublishEditorialInputSchema, CommonsUpdateEditorialArticleInputSchema, CommonsGetPublicationInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LeadSuggestedColumnOutputSchema, LeadColumnMapSuggestionOutputSchema, LeadArtifactOutputSchema, LeadListUploadStartOutputSchema, LeadListImportOutputSchema, LeadCandidateOutputSchema, LeadProgressOutputSchema, LeadBillingOutputSchema, LeadAssociatedPersonSourceOutputSchema, LeadAssociatedPersonOutputSchema, LeadSampleRowOutputSchema, LeadListEnrichmentOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, PageMediaAssetOutput, PageMediaArtifactOutput, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, SiteExportReadOutputSchema, SiteExportImageOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
|
|
51525
52067
|
var init_mcp_tool_schemas = __esm({
|
|
51526
52068
|
"src/mcp/mcp-tool-schemas.ts"() {
|
|
51527
52069
|
"use strict";
|
|
@@ -51934,7 +52476,7 @@ var init_mcp_tool_schemas = __esm({
|
|
|
51934
52476
|
idOrSlug: import_zod45.z.string().trim().min(1).max(180).describe("Transparent Commons TPW-Q entity id or /wiki/ slug whose governed RFC 9264 linkset should be read.")
|
|
51935
52477
|
};
|
|
51936
52478
|
CommonsFeaturedImageInputSchema = import_zod45.z.object({
|
|
51937
|
-
url: import_zod45.z.string().url().describe("
|
|
52479
|
+
url: import_zod45.z.string().url().describe("For a publish-ready entity, use the permanent URL returned by commons_host_image. Validation reports any unregistered external URL as not ready. Do not use a chat attachment reference, caller-local path, private URL, or HTML page URL here; pass the original image or webpage source to commons_host_image first."),
|
|
51938
52480
|
alt: import_zod45.z.string().trim().max(500).optional(),
|
|
51939
52481
|
caption: import_zod45.z.string().trim().max(1e3).optional(),
|
|
51940
52482
|
sourceUrl: import_zod45.z.string().url().optional(),
|
|
@@ -52026,14 +52568,14 @@ var init_mcp_tool_schemas = __esm({
|
|
|
52026
52568
|
maxCandidates: import_zod45.z.number().int().min(1).max(20).default(8).describe("Maximum duplicate candidates to return.")
|
|
52027
52569
|
};
|
|
52028
52570
|
CommonsSubmitEntityInputSchema = {
|
|
52029
|
-
idempotencyKey: import_zod45.z.string().trim().min(8).max(200).describe("Required unique opaque ID for this intended Commons write. Reuse
|
|
52571
|
+
idempotencyKey: import_zod45.z.string().trim().min(8).max(200).describe("Required unique opaque ID for this intended Commons write. Reuse it when retrying the same intended write after a timeout or after correcting a pre-persistence image-ingress failure; use a new value for each intentional create or edit."),
|
|
52030
52572
|
title: import_zod45.z.string().trim().min(3).max(180).describe("Public page title. This is also used for duplicate slug/entity checks when slug is omitted."),
|
|
52031
52573
|
description: import_zod45.z.string().trim().min(8).max(600).describe("Short article summary and schema.org description."),
|
|
52032
52574
|
slug: import_zod45.z.string().trim().max(180).optional().describe("Optional URL slug. Omit to derive one from title."),
|
|
52033
52575
|
entityId: import_zod45.z.string().trim().max(80).optional().describe("Existing Transparent Public Wiki entity id when proposing an edit. New entities normally omit this and receive a TPW-Q id."),
|
|
52034
52576
|
entityType: import_zod45.z.string().trim().max(120).default("PublicArticle").describe("Backend Wikidata-style type. Prefer precise entity classes such as SoftwareApplication, Organization, Person, Event, Place, Taxon, ScienceConcept, MathConcept, TechArticle, or PublicArticle; the public article structure should match the selected type."),
|
|
52035
52577
|
disambiguationName: import_zod45.z.string().trim().max(240).optional().describe("Clarifying name used when the concept could be confused with another entity."),
|
|
52036
|
-
featuredImage: CommonsFeaturedImageInputSchema.optional().describe("Required for auto-published public entities. The image is also added to the media manifest if absent."),
|
|
52578
|
+
featuredImage: CommonsFeaturedImageInputSchema.optional().describe("Required for auto-published public entities. First call commons_host_image with attachment bytes, an image artifact, a direct image URL, or a webpage URL, then place its returned permanent URL here. Validation does not fetch external images and reports unregistered URLs as not publishable. Submit retains a compatibility fallback for public URLs. The image is also added to the media manifest if absent."),
|
|
52037
52579
|
source: CommonsSourceInputSchema.optional().describe("Source provenance. Store original URL, source byline, and origin canonical here; canonical does not replace rights review."),
|
|
52038
52580
|
tags: import_zod45.z.array(import_zod45.z.string().trim().min(1).max(80)).max(24).optional().describe("Standardized topic tags. Use existing/searchable concepts when possible."),
|
|
52039
52581
|
keywords: import_zod45.z.array(import_zod45.z.string().trim().min(1).max(120)).max(50).optional().describe("SEO and retrieval keywords."),
|
|
@@ -52095,12 +52637,31 @@ var init_mcp_tool_schemas = __esm({
|
|
|
52095
52637
|
idOrSlug: import_zod45.z.string().trim().min(1).max(180).describe("Published entity id or /wiki/ slug whose contribution ledger should be read.")
|
|
52096
52638
|
};
|
|
52097
52639
|
CommonsHostImageInputSchema = {
|
|
52098
|
-
sourceUrl: import_zod45.z.string().url().optional().describe("Public
|
|
52099
|
-
imageBase64: import_zod45.z.string().min(16).optional().describe("Base64 image bytes for an image
|
|
52640
|
+
sourceUrl: import_zod45.z.string().url().optional().describe("Public HTTPS direct image URL or webpage URL. A direct image is hosted as-is; an HTML page is inspected for Open Graph, Twitter, JSON-LD, and meaningful content images, then the first usable candidate is hosted. Temporary signed URLs are consumed immediately and query credentials are not retained as provenance."),
|
|
52641
|
+
imageBase64: import_zod45.z.string().min(16).optional().describe("Base64 image bytes for an image already available to the caller, including a pasted attachment or AI-generated image. Use sourceType to preserve whether it was uploaded or generated. Keep the complete serialized request within the client transport limit."),
|
|
52642
|
+
artifactId: import_zod45.z.string().trim().min(1).max(500).optional().describe("Owner-scoped image artifact returned by an MCP Scraper media or screenshot workflow. The server reauthorizes ownership and reads the bytes directly; do not construct an artifact ID."),
|
|
52643
|
+
sourceType: import_zod45.z.enum(["uploaded", "ai_generated"]).optional().describe("Provenance for imageBase64 or artifactId. Use ai_generated only when an image model created the supplied bytes; otherwise use uploaded or omit it."),
|
|
52100
52644
|
alt: import_zod45.z.string().trim().max(500).optional().describe("Alt text describing the image for readers who cannot see it."),
|
|
52101
52645
|
license: import_zod45.z.string().trim().max(240).optional().describe("License or usage terms of the original image, preserved with the hosted copy."),
|
|
52102
52646
|
attribution: import_zod45.z.string().trim().max(500).optional().describe("Credit line for the original photographer, publication, or archive.")
|
|
52103
52647
|
};
|
|
52648
|
+
CommonsHostImageOutputSchema = {
|
|
52649
|
+
ok: import_zod45.z.boolean(),
|
|
52650
|
+
data: import_zod45.z.object({
|
|
52651
|
+
id: import_zod45.z.string().describe("Stable Commons image ID."),
|
|
52652
|
+
url: import_zod45.z.string().url().describe("Permanent Commons URL to use as featuredImage.url."),
|
|
52653
|
+
contentType: import_zod45.z.enum(["image/jpeg", "image/png", "image/gif", "image/webp"]),
|
|
52654
|
+
bytes: import_zod45.z.number().int().positive(),
|
|
52655
|
+
digest: import_zod45.z.string(),
|
|
52656
|
+
sourceUrl: import_zod45.z.string().url().nullable().describe("Sanitized direct asset URL that supplied the hosted bytes, when applicable."),
|
|
52657
|
+
sourcePageUrl: import_zod45.z.string().url().nullable().describe("Sanitized webpage URL from which the image was discovered, when applicable."),
|
|
52658
|
+
sourceKind: import_zod45.z.enum(["uploaded", "ai_generated", "artifact", "direct_url", "webpage", "registered"]),
|
|
52659
|
+
selectionSource: import_zod45.z.enum(["provided_bytes", "artifact", "direct_url", "og:image", "twitter:image", "json-ld", "content-image", "registered"]),
|
|
52660
|
+
reused: import_zod45.z.boolean().describe("True when identical image bytes were already stored.")
|
|
52661
|
+
}).optional(),
|
|
52662
|
+
error: import_zod45.z.string().optional(),
|
|
52663
|
+
message: import_zod45.z.string().optional()
|
|
52664
|
+
};
|
|
52104
52665
|
CommonsGetProposalInputSchema = {
|
|
52105
52666
|
proposalId: import_zod45.z.string().trim().min(1).max(120).describe("Proposal id returned by commons_submit_entity, in the form commons-proposal-<uuid>.")
|
|
52106
52667
|
};
|
|
@@ -52128,6 +52689,30 @@ var init_mcp_tool_schemas = __esm({
|
|
|
52128
52689
|
error: import_zod45.z.string().optional(),
|
|
52129
52690
|
message: import_zod45.z.string().optional()
|
|
52130
52691
|
};
|
|
52692
|
+
CommonsFeaturedImageDiagnosticsOutputSchema = import_zod45.z.object({
|
|
52693
|
+
state: import_zod45.z.enum(["missing", "external_unhosted", "commons_hosted"]).describe("Storage-readiness state for the candidate featured image."),
|
|
52694
|
+
url: import_zod45.z.string().url().nullable().describe("Normalized candidate featured-image URL, or null when none was supplied."),
|
|
52695
|
+
commonsImageId: import_zod45.z.string().nullable().describe("Registered Commons image ID when metadata proves the image is hosted, otherwise null."),
|
|
52696
|
+
readyForPublish: import_zod45.z.boolean().describe("Whether Commons storage metadata proves this featured image is ready to publish."),
|
|
52697
|
+
submitWillAttemptAutoHost: import_zod45.z.boolean().describe("Whether submit will use its compatibility fallback to try hosting this external image."),
|
|
52698
|
+
requiredAction: import_zod45.z.string().nullable().describe("Action the caller must take before submit, or null when the image is ready.")
|
|
52699
|
+
}).describe("Featured-image storage readiness determined without fetching or storing external bytes.");
|
|
52700
|
+
CommonsValidateEntityOutputSchema = {
|
|
52701
|
+
ok: import_zod45.z.boolean(),
|
|
52702
|
+
data: import_zod45.z.object({
|
|
52703
|
+
ok: import_zod45.z.literal(true),
|
|
52704
|
+
valid: import_zod45.z.boolean(),
|
|
52705
|
+
publishable: import_zod45.z.boolean(),
|
|
52706
|
+
autoPublishEligible: import_zod45.z.boolean(),
|
|
52707
|
+
errors: import_zod45.z.array(import_zod45.z.string()),
|
|
52708
|
+
warnings: import_zod45.z.array(import_zod45.z.string()),
|
|
52709
|
+
imageDiagnostics: import_zod45.z.object({
|
|
52710
|
+
featuredImage: CommonsFeaturedImageDiagnosticsOutputSchema
|
|
52711
|
+
}).describe("Read-only image readiness and recovery guidance for this payload.")
|
|
52712
|
+
}).passthrough().optional(),
|
|
52713
|
+
error: import_zod45.z.string().optional(),
|
|
52714
|
+
message: import_zod45.z.string().optional()
|
|
52715
|
+
};
|
|
52131
52716
|
DirectoryWorkflowStatusInputSchema = {
|
|
52132
52717
|
jobId: import_zod45.z.string().trim().min(1).describe("The jobId returned by directory_workflow. Poll until status is complete, partial, empty, or failed.")
|
|
52133
52718
|
};
|
|
@@ -54839,11 +55424,11 @@ function requireTasksCapability(capabilityValue) {
|
|
|
54839
55424
|
);
|
|
54840
55425
|
}
|
|
54841
55426
|
function taskKey(secret2) {
|
|
54842
|
-
return (0,
|
|
55427
|
+
return (0, import_node_crypto34.createHash)("sha256").update("mcp-scraper-task-handle\0", "utf8").update(secret2, "utf8").digest();
|
|
54843
55428
|
}
|
|
54844
55429
|
function encodeTaskHandle(payload, secret2) {
|
|
54845
|
-
const nonce = (0,
|
|
54846
|
-
const cipher = (0,
|
|
55430
|
+
const nonce = (0, import_node_crypto34.randomBytes)(12);
|
|
55431
|
+
const cipher = (0, import_node_crypto34.createCipheriv)("aes-256-gcm", taskKey(secret2), nonce);
|
|
54847
55432
|
const ciphertext = Buffer.concat([
|
|
54848
55433
|
cipher.update(JSON.stringify(payload), "utf8"),
|
|
54849
55434
|
cipher.final()
|
|
@@ -54860,7 +55445,7 @@ function decodeTaskHandle(taskId, secret2, ownerId2) {
|
|
|
54860
55445
|
const nonce = bytes.subarray(0, 12);
|
|
54861
55446
|
const tag = bytes.subarray(bytes.length - 16);
|
|
54862
55447
|
const ciphertext = bytes.subarray(12, bytes.length - 16);
|
|
54863
|
-
const decipher = (0,
|
|
55448
|
+
const decipher = (0, import_node_crypto34.createDecipheriv)("aes-256-gcm", taskKey(secret2), nonce);
|
|
54864
55449
|
decipher.setAuthTag(tag);
|
|
54865
55450
|
const parsed = JSON.parse(Buffer.concat([
|
|
54866
55451
|
decipher.update(ciphertext),
|
|
@@ -55224,12 +55809,12 @@ async function handleMcpTasksHttpRequest(request, executor, options) {
|
|
|
55224
55809
|
return taskHttpError(id, error instanceof import_server.ProtocolError ? error : new import_server.ProtocolError(-32603, error instanceof Error ? error.message : "Internal error"));
|
|
55225
55810
|
}
|
|
55226
55811
|
}
|
|
55227
|
-
var import_server,
|
|
55812
|
+
var import_server, import_node_crypto34, import_zod46, MCP_TASKS_EXTENSION_ID, TASK_HANDLE_VERSION, TASK_TTL_MS, TASK_POLL_INTERVAL_MS, TASK_INVALID_PARAMS, TASK_MISSING_CAPABILITY, TASK_START_TOOL_NAMES;
|
|
55228
55813
|
var init_mcp_tasks_extension = __esm({
|
|
55229
55814
|
"src/mcp/mcp-tasks-extension.ts"() {
|
|
55230
55815
|
"use strict";
|
|
55231
55816
|
import_server = require("@modelcontextprotocol/server");
|
|
55232
|
-
|
|
55817
|
+
import_node_crypto34 = require("crypto");
|
|
55233
55818
|
import_zod46 = require("zod");
|
|
55234
55819
|
init_mcp_tool_schemas();
|
|
55235
55820
|
MCP_TASKS_EXTENSION_ID = "io.modelcontextprotocol/tasks";
|
|
@@ -55537,7 +56122,7 @@ var init_analytics_mcp_tools = __esm({
|
|
|
55537
56122
|
|
|
55538
56123
|
// src/mcp/paa-mcp-server.ts
|
|
55539
56124
|
function hashOwnerId(callerKey) {
|
|
55540
|
-
return (0,
|
|
56125
|
+
return (0, import_node_crypto35.createHash)("sha256").update(callerKey).digest("hex").slice(0, 24);
|
|
55541
56126
|
}
|
|
55542
56127
|
function liveWebToolAnnotations(title) {
|
|
55543
56128
|
return {
|
|
@@ -56024,9 +56609,9 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
56024
56609
|
}, async (input) => executor.commonsPrepareEntity(input));
|
|
56025
56610
|
server.registerTool("commons_validate_entity", {
|
|
56026
56611
|
title: "Transparent Commons Validate Entity",
|
|
56027
|
-
description: "
|
|
56612
|
+
description: "Read-only validation for a proposed Transparent Commons entity payload. Call it after commons_prepare_entity, composing the page, and commons_host_image. It does not fetch or store image bytes: an external or otherwise unregistered featured image remains not publishable until commons_host_image returns the permanent Commons URL. Inspect imageDiagnostics for readiness and the required action before commons_submit_entity. Also checks source/body evidence, existing-entity conflict state, heading profile alignment, and unsupported placeholder sections.",
|
|
56028
56613
|
inputSchema: CommonsValidateEntityInputSchema,
|
|
56029
|
-
outputSchema: recordOutputSchema("commons_validate_entity",
|
|
56614
|
+
outputSchema: recordOutputSchema("commons_validate_entity", CommonsValidateEntityOutputSchema),
|
|
56030
56615
|
annotations: {
|
|
56031
56616
|
title: "Transparent Commons Validate Entity",
|
|
56032
56617
|
readOnlyHint: true,
|
|
@@ -56037,7 +56622,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
56037
56622
|
}, async (input) => executor.commonsValidateEntity(input));
|
|
56038
56623
|
server.registerTool("commons_submit_entity", {
|
|
56039
56624
|
title: "Transparent Commons Governed Entity Write",
|
|
56040
|
-
description: "
|
|
56625
|
+
description: "Charged governed write that creates or proposes an edit to a Transparent Commons public wiki entity. A new entity costs 10 Credits; an edit costs 2 Credits. Preparation, image hosting, and validation are free, and idempotent retries are not charged twice. Normal workflow is commons_prepare_entity, compose the page, commons_host_image, commons_validate_entity, then submit with the returned permanent featuredImage.url. Submit retains a compatibility fallback that auto-hosts a stable direct public HTTPS image URL; if image ingress fails, it returns an actionable image tool error so you can host different bytes or a different stable source, replace the URL, validate again, and retry the same intended write with the same idempotencyKey. This never edits rendered HTML directly. Transparent Commons is an open wiki: any account may edit any entity, and every applied edit is recorded on the public contribution ledger. Publishing requires a ready featured image and body/source evidence. Pass baseRevision when editing so the ledger can show whether the edit was written on top of a newer revision. Requires idempotencyKey.",
|
|
56041
56626
|
inputSchema: CommonsSubmitEntityInputSchema,
|
|
56042
56627
|
outputSchema: recordOutputSchema("commons_submit_entity", CommonsGenericOutputSchema),
|
|
56043
56628
|
annotations: {
|
|
@@ -56063,9 +56648,9 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
56063
56648
|
}, async (input) => executor.commonsGetEntityLedger(input));
|
|
56064
56649
|
server.registerTool("commons_host_image", {
|
|
56065
56650
|
title: "Host a Transparent Commons Image",
|
|
56066
|
-
description: "
|
|
56651
|
+
description: "Turn one supplied image source into a permanent Transparent Commons image before validation and submission. Provide exactly one of imageBase64 for a pasted or AI-generated image, artifactId for an owner-scoped MCP Scraper image artifact, or sourceUrl for either a direct public HTTPS image or a webpage containing the desired image. Webpages are inspected in deterministic order for Open Graph, Twitter, JSON-LD, and meaningful content images; unusable candidates fall through to the next candidate. Returns the permanent URL for featuredImage.url plus source and selection provenance. JPEG, PNG, GIF, and WebP are accepted up to 10 MB; SVG is rejected. Identical bytes are stored once. Never pass a caller-local path or private/authenticated URL.",
|
|
56067
56652
|
inputSchema: CommonsHostImageInputSchema,
|
|
56068
|
-
outputSchema: recordOutputSchema("commons_host_image",
|
|
56653
|
+
outputSchema: recordOutputSchema("commons_host_image", CommonsHostImageOutputSchema),
|
|
56069
56654
|
annotations: {
|
|
56070
56655
|
title: "Host a Transparent Commons Image",
|
|
56071
56656
|
readOnlyHint: false,
|
|
@@ -56525,7 +57110,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
56525
57110
|
annotations: { title: "Set Scheduled Action Connections", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
56526
57111
|
}, async (input) => executor.setScheduledActionConnections(input));
|
|
56527
57112
|
}
|
|
56528
|
-
var import_server2, import_zod48, import_node_fs11, import_node_path17,
|
|
57113
|
+
var import_server2, import_zod48, import_node_fs11, import_node_path17, import_node_crypto35, ACTION_CONFIRMATION_SCHEMA;
|
|
56529
57114
|
var init_paa_mcp_server = __esm({
|
|
56530
57115
|
"src/mcp/paa-mcp-server.ts"() {
|
|
56531
57116
|
"use strict";
|
|
@@ -56533,7 +57118,7 @@ var init_paa_mcp_server = __esm({
|
|
|
56533
57118
|
import_zod48 = require("zod");
|
|
56534
57119
|
import_node_fs11 = require("fs");
|
|
56535
57120
|
import_node_path17 = require("path");
|
|
56536
|
-
|
|
57121
|
+
import_node_crypto35 = require("crypto");
|
|
56537
57122
|
init_version();
|
|
56538
57123
|
init_rates();
|
|
56539
57124
|
init_mcp_response_formatter();
|
|
@@ -56655,11 +57240,11 @@ function analyticsReportPath(input, report) {
|
|
|
56655
57240
|
const suffix2 = query.size ? `?${query.toString()}` : "";
|
|
56656
57241
|
return `/analytics/sites/${encodeURIComponent(input.siteId)}/${report}${suffix2}`;
|
|
56657
57242
|
}
|
|
56658
|
-
var
|
|
57243
|
+
var import_node_crypto36, HttpMcpToolExecutor;
|
|
56659
57244
|
var init_http_mcp_tool_executor = __esm({
|
|
56660
57245
|
"src/mcp/http-mcp-tool-executor.ts"() {
|
|
56661
57246
|
"use strict";
|
|
56662
|
-
|
|
57247
|
+
import_node_crypto36 = require("crypto");
|
|
56663
57248
|
init_harvest_timeout();
|
|
56664
57249
|
init_browser_service_env();
|
|
56665
57250
|
init_errors();
|
|
@@ -56728,13 +57313,13 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
56728
57313
|
});
|
|
56729
57314
|
}
|
|
56730
57315
|
async callDirectoryWorkflowStart(body, explicitIdempotencyKey) {
|
|
56731
|
-
const idempotencyKey4 = `mcp-directory-${(0,
|
|
57316
|
+
const idempotencyKey4 = `mcp-directory-${(0, import_node_crypto36.createHash)("sha256").update(explicitIdempotencyKey).digest("hex")}`;
|
|
56732
57317
|
return this.call("/directory/run", body, this.timeoutMs, "POST", {
|
|
56733
57318
|
"Idempotency-Key": idempotencyKey4
|
|
56734
57319
|
});
|
|
56735
57320
|
}
|
|
56736
57321
|
async callSiteExtractStart(toolName, body, explicitIdempotencyKey) {
|
|
56737
|
-
const idempotencyKey4 = `mcp-site-${(0,
|
|
57322
|
+
const idempotencyKey4 = `mcp-site-${(0, import_node_crypto36.createHash)("sha256").update(toolName).update("\0").update(explicitIdempotencyKey).digest("hex")}`;
|
|
56738
57323
|
return this.call("/extract-site", body, this.timeoutMs, "POST", {
|
|
56739
57324
|
"Idempotency-Key": idempotencyKey4
|
|
56740
57325
|
});
|
|
@@ -57178,7 +57763,7 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
57178
57763
|
report: input.report,
|
|
57179
57764
|
format: input.format
|
|
57180
57765
|
}, this.timeoutMs, "POST", {
|
|
57181
|
-
"Idempotency-Key": `analytics-export-${(0,
|
|
57766
|
+
"Idempotency-Key": `analytics-export-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
|
|
57182
57767
|
});
|
|
57183
57768
|
}
|
|
57184
57769
|
commonsSearchEntities(input) {
|
|
@@ -57217,7 +57802,7 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
57217
57802
|
commonsSubmitEntity(input) {
|
|
57218
57803
|
const { idempotencyKey: idempotencyKey4, ...body } = input;
|
|
57219
57804
|
return this.call("/commons/entities/propose", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
|
|
57220
|
-
"Idempotency-Key": `commons-${(0,
|
|
57805
|
+
"Idempotency-Key": `commons-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
|
|
57221
57806
|
});
|
|
57222
57807
|
}
|
|
57223
57808
|
commonsGetEntityLedger(input) {
|
|
@@ -57232,7 +57817,7 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
57232
57817
|
commonsUpdateEditorialArticle(input) {
|
|
57233
57818
|
const { idempotencyKey: idempotencyKey4, ...body } = input;
|
|
57234
57819
|
return this.call("/commons/publications/articles", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
|
|
57235
|
-
"Idempotency-Key": `commons-article-${(0,
|
|
57820
|
+
"Idempotency-Key": `commons-article-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
|
|
57236
57821
|
});
|
|
57237
57822
|
}
|
|
57238
57823
|
commonsSaveFilter(input) {
|
|
@@ -57253,13 +57838,13 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
57253
57838
|
commonsClaimPublication(input) {
|
|
57254
57839
|
const { idempotencyKey: idempotencyKey4, ...body } = input;
|
|
57255
57840
|
return this.call("/commons/publications/claim", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
|
|
57256
|
-
"Idempotency-Key": `commons-publication-claim-${(0,
|
|
57841
|
+
"Idempotency-Key": `commons-publication-claim-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
|
|
57257
57842
|
});
|
|
57258
57843
|
}
|
|
57259
57844
|
commonsPublishEditorial(input) {
|
|
57260
57845
|
const { idempotencyKey: idempotencyKey4, ...body } = input;
|
|
57261
57846
|
return this.call("/commons/publications/publish", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
|
|
57262
|
-
"Idempotency-Key": `commons-publication-publish-${(0,
|
|
57847
|
+
"Idempotency-Key": `commons-publication-publish-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
|
|
57263
57848
|
});
|
|
57264
57849
|
}
|
|
57265
57850
|
commonsGetPublication(input) {
|
|
@@ -57267,13 +57852,13 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
57267
57852
|
return this.getJson(input.subdomain ? `/commons/publications/${encodeURIComponent(input.subdomain)}?${query}` : `/commons/publications/me?${query}`);
|
|
57268
57853
|
}
|
|
57269
57854
|
async captureSerpSnapshot(input) {
|
|
57270
|
-
const fingerprint2 = (0,
|
|
57855
|
+
const fingerprint2 = (0, import_node_crypto36.createHash)("sha256").update(JSON.stringify(input)).digest("hex");
|
|
57271
57856
|
const now = Date.now();
|
|
57272
57857
|
for (const [pendingFingerprint, pendingEntry] of this.pendingSerpCaptureBillingKeys) {
|
|
57273
57858
|
if (pendingEntry.expiresAt <= now) this.pendingSerpCaptureBillingKeys.delete(pendingFingerprint);
|
|
57274
57859
|
}
|
|
57275
57860
|
const pending = this.pendingSerpCaptureBillingKeys.get(fingerprint2);
|
|
57276
|
-
const idempotencyKey4 = pending && pending.expiresAt > now ? pending.key : (0,
|
|
57861
|
+
const idempotencyKey4 = pending && pending.expiresAt > now ? pending.key : (0, import_node_crypto36.randomUUID)();
|
|
57277
57862
|
this.pendingSerpCaptureBillingKeys.set(fingerprint2, {
|
|
57278
57863
|
key: idempotencyKey4,
|
|
57279
57864
|
expiresAt: now + 15 * 6e4
|
|
@@ -63283,7 +63868,7 @@ async function createImageSourceArtifact(args) {
|
|
|
63283
63868
|
if (args.content.length === 0 || args.content.length > IMAGE_SOURCE_MAX_BYTES) {
|
|
63284
63869
|
throw new Error("image_source_size_invalid");
|
|
63285
63870
|
}
|
|
63286
|
-
const id = (0,
|
|
63871
|
+
const id = (0, import_node_crypto37.randomUUID)().replaceAll("-", "");
|
|
63287
63872
|
return createPrivateArtifact({
|
|
63288
63873
|
policy: policy4(),
|
|
63289
63874
|
ownerId: args.ownerId,
|
|
@@ -63302,11 +63887,11 @@ async function readOwnedImageSourceArtifact(args) {
|
|
|
63302
63887
|
maxBytes: IMAGE_SOURCE_MAX_BYTES
|
|
63303
63888
|
});
|
|
63304
63889
|
}
|
|
63305
|
-
var
|
|
63890
|
+
var import_node_crypto37, IMAGE_SOURCE_ARTIFACT_PREFIX, IMAGE_SOURCE_ARTIFACT_TTL_MS, IMAGE_SOURCE_DOWNLOAD_TTL_MS, IMAGE_SOURCE_MAX_BYTES, ALLOWED_IMAGE_TYPES;
|
|
63306
63891
|
var init_image_source_artifacts = __esm({
|
|
63307
63892
|
"src/api/image-source-artifacts.ts"() {
|
|
63308
63893
|
"use strict";
|
|
63309
|
-
|
|
63894
|
+
import_node_crypto37 = require("crypto");
|
|
63310
63895
|
init_private_artifacts();
|
|
63311
63896
|
IMAGE_SOURCE_ARTIFACT_PREFIX = "image-sources/";
|
|
63312
63897
|
IMAGE_SOURCE_ARTIFACT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -64920,7 +65505,7 @@ async function runBrowserAgentMigration() {
|
|
|
64920
65505
|
}
|
|
64921
65506
|
async function createExtensionRow(input) {
|
|
64922
65507
|
const db = getDb();
|
|
64923
|
-
const id = `bext_${(0,
|
|
65508
|
+
const id = `bext_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
|
|
64924
65509
|
await db.execute({
|
|
64925
65510
|
sql: `INSERT INTO browser_agent_extensions (id, user_id, name, backend_id, backend_name, source, source_url, size_bytes)
|
|
64926
65511
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -64955,7 +65540,7 @@ async function deleteExtensionRow(userId, name) {
|
|
|
64955
65540
|
}
|
|
64956
65541
|
async function createAuthConnectionRow(input) {
|
|
64957
65542
|
const db = getDb();
|
|
64958
|
-
const connectionId = `authc_${(0,
|
|
65543
|
+
const connectionId = `authc_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
|
|
64959
65544
|
await db.execute({
|
|
64960
65545
|
sql: `INSERT INTO browser_auth_connections (connection_id, domain, profile, account_email, note, status, browser_agent_session_id)
|
|
64961
65546
|
VALUES (?, ?, ?, ?, ?, 'NEEDS_AUTH', ?)`,
|
|
@@ -65040,7 +65625,7 @@ async function deleteProfileLabel(userId, profile) {
|
|
|
65040
65625
|
}
|
|
65041
65626
|
async function createSessionRow(input) {
|
|
65042
65627
|
const db = getDb();
|
|
65043
|
-
const id = `bas_${(0,
|
|
65628
|
+
const id = `bas_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
|
|
65044
65629
|
await db.execute({
|
|
65045
65630
|
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)
|
|
65046
65631
|
VALUES (?, ?, ?, ?, 'open', ?, ?, ?, datetime('now'))`,
|
|
@@ -65117,7 +65702,7 @@ async function recordAction(input) {
|
|
|
65117
65702
|
sql: `INSERT INTO browser_agent_actions (id, session_id, type, params_json, ok, error)
|
|
65118
65703
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
65119
65704
|
args: [
|
|
65120
|
-
`baa_${(0,
|
|
65705
|
+
`baa_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`,
|
|
65121
65706
|
input.sessionId,
|
|
65122
65707
|
input.type,
|
|
65123
65708
|
input.params == null ? null : JSON.stringify(input.params),
|
|
@@ -65157,11 +65742,11 @@ async function listReplayRows(sessionId) {
|
|
|
65157
65742
|
});
|
|
65158
65743
|
return res.rows;
|
|
65159
65744
|
}
|
|
65160
|
-
var
|
|
65745
|
+
var import_node_crypto38, _ready2, _migrationPromise2, ORPHANED_SESSION_STATUS;
|
|
65161
65746
|
var init_browser_agent_db = __esm({
|
|
65162
65747
|
"src/api/browser-agent-db.ts"() {
|
|
65163
65748
|
"use strict";
|
|
65164
|
-
|
|
65749
|
+
import_node_crypto38 = require("crypto");
|
|
65165
65750
|
init_db();
|
|
65166
65751
|
_ready2 = false;
|
|
65167
65752
|
_migrationPromise2 = null;
|
|
@@ -66303,7 +66888,7 @@ var init_browser_agent_service = __esm({
|
|
|
66303
66888
|
|
|
66304
66889
|
// src/api/serp-identity-db.ts
|
|
66305
66890
|
async function createSerpIdentityRow(input) {
|
|
66306
|
-
const id = `serpi_${(0,
|
|
66891
|
+
const id = `serpi_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
|
|
66307
66892
|
await getDb().execute({
|
|
66308
66893
|
sql: `INSERT INTO serp_identities
|
|
66309
66894
|
(id, user_id, name, kernel_profile_name, kernel_proxy_id, proxy_type, country, status)
|
|
@@ -66348,11 +66933,11 @@ async function deleteSerpIdentityRow(userId, name) {
|
|
|
66348
66933
|
args: [userId, name]
|
|
66349
66934
|
});
|
|
66350
66935
|
}
|
|
66351
|
-
var
|
|
66936
|
+
var import_node_crypto39;
|
|
66352
66937
|
var init_serp_identity_db = __esm({
|
|
66353
66938
|
"src/api/serp-identity-db.ts"() {
|
|
66354
66939
|
"use strict";
|
|
66355
|
-
|
|
66940
|
+
import_node_crypto39 = require("crypto");
|
|
66356
66941
|
init_db();
|
|
66357
66942
|
}
|
|
66358
66943
|
});
|
|
@@ -66364,8 +66949,8 @@ function kernelClient() {
|
|
|
66364
66949
|
return new import_sdk10.default({ apiKey });
|
|
66365
66950
|
}
|
|
66366
66951
|
function backendName(userId, name, resource) {
|
|
66367
|
-
const digest2 = (0,
|
|
66368
|
-
const nonce = (0,
|
|
66952
|
+
const digest2 = (0, import_node_crypto40.createHash)("sha256").update(`${userId}:${name}`).digest("hex").slice(0, 16);
|
|
66953
|
+
const nonce = (0, import_node_crypto40.randomUUID)().replace(/-/g, "").slice(0, 8);
|
|
66369
66954
|
return `mcp-serp-${resource}-${digest2}-${nonce}`;
|
|
66370
66955
|
}
|
|
66371
66956
|
function isNotFound2(error) {
|
|
@@ -66443,11 +67028,11 @@ async function deleteSerpIdentity(userId, name) {
|
|
|
66443
67028
|
throw error;
|
|
66444
67029
|
}
|
|
66445
67030
|
}
|
|
66446
|
-
var
|
|
67031
|
+
var import_node_crypto40, import_sdk10, MAX_SERP_IDENTITIES_PER_USER;
|
|
66447
67032
|
var init_serp_identity_service = __esm({
|
|
66448
67033
|
"src/api/serp-identity-service.ts"() {
|
|
66449
67034
|
"use strict";
|
|
66450
|
-
|
|
67035
|
+
import_node_crypto40 = require("crypto");
|
|
66451
67036
|
import_sdk10 = __toESM(require("@onkernel/sdk"), 1);
|
|
66452
67037
|
init_browser_service_env();
|
|
66453
67038
|
init_serp_identity_db();
|
|
@@ -67158,7 +67743,7 @@ function buildBrowserAgentRoutes() {
|
|
|
67158
67743
|
}
|
|
67159
67744
|
const existing = await getExtensionRow(user.id, name);
|
|
67160
67745
|
if (existing) return c.json({ error: `an extension named "${name}" already exists \u2014 delete it first or pick another name` }, 409);
|
|
67161
|
-
const backendName2 = `u${user.id}_${(0,
|
|
67746
|
+
const backendName2 = `u${user.id}_${(0, import_node_crypto41.randomUUID)().replace(/-/g, "")}`;
|
|
67162
67747
|
try {
|
|
67163
67748
|
const imported = await importExtensionFromStore(storeUrl, backendName2);
|
|
67164
67749
|
const row = await createExtensionRow({
|
|
@@ -67217,11 +67802,11 @@ function buildBrowserAgentRoutes() {
|
|
|
67217
67802
|
});
|
|
67218
67803
|
return app2;
|
|
67219
67804
|
}
|
|
67220
|
-
var
|
|
67805
|
+
var import_node_crypto41, import_hono24, auth, DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS, EXTENSION_NAME_RE, SERP_IDENTITY_NAME_RE;
|
|
67221
67806
|
var init_browser_agent_routes = __esm({
|
|
67222
67807
|
"src/api/browser-agent-routes.ts"() {
|
|
67223
67808
|
"use strict";
|
|
67224
|
-
|
|
67809
|
+
import_node_crypto41 = require("crypto");
|
|
67225
67810
|
import_hono24 = require("hono");
|
|
67226
67811
|
init_api_auth();
|
|
67227
67812
|
init_errors();
|
|
@@ -67779,7 +68364,7 @@ async function getKeys() {
|
|
|
67779
68364
|
const privateKey = await (0, import_jose2.importPKCS8)(pem, "RS256", { extractable: true });
|
|
67780
68365
|
const full = await (0, import_jose2.exportJWK)(privateKey);
|
|
67781
68366
|
const publicJwk = { kty: full.kty, n: full.n, e: full.e };
|
|
67782
|
-
const kid = (0,
|
|
68367
|
+
const kid = (0, import_node_crypto42.createHash)("sha256").update(JSON.stringify({ e: publicJwk.e, kty: publicJwk.kty, n: publicJwk.n })).digest("base64url").slice(0, 16);
|
|
67783
68368
|
publicJwk.kid = kid;
|
|
67784
68369
|
publicJwk.alg = "RS256";
|
|
67785
68370
|
publicJwk.use = "sig";
|
|
@@ -67958,23 +68543,23 @@ async function validateAuthRequest(p) {
|
|
|
67958
68543
|
}
|
|
67959
68544
|
function pkceMatches(verifier, challenge) {
|
|
67960
68545
|
if (!verifier) return false;
|
|
67961
|
-
const computed = (0,
|
|
68546
|
+
const computed = (0, import_node_crypto42.createHash)("sha256").update(verifier).digest("base64url");
|
|
67962
68547
|
return computed === challenge;
|
|
67963
68548
|
}
|
|
67964
68549
|
async function mintAccessToken(identity, scope, plan, audience) {
|
|
67965
68550
|
const { privateKey, kid } = await getKeys();
|
|
67966
|
-
return new import_jose2.SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti((0,
|
|
68551
|
+
return new import_jose2.SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti((0, import_node_crypto42.randomUUID)()).setExpirationTime(`${ACCESS_TTL_SECONDS}s`).sign(privateKey);
|
|
67967
68552
|
}
|
|
67968
68553
|
function tokenErrorResponse(c, error, description, status) {
|
|
67969
68554
|
return c.json({ error, error_description: description }, status);
|
|
67970
68555
|
}
|
|
67971
|
-
var import_hono26, import_cookie,
|
|
68556
|
+
var import_hono26, import_cookie, import_node_crypto42, import_jose2, ISSUER, RESOURCE, SCRAPER_RESOURCE, MEMORY_SCOPES, SCRAPER_SCOPES, SUPPORTED_SCOPES, ACCESS_TTL_SECONDS, REFRESH_TTL_SECONDS, CODE_TTL_SECONDS, ROTATION_GRACE_SECONDS, OAUTH_DATABASE_TIMEOUT_MS, OAuthDatabaseUnavailableError, secureCookies, sessionCookieOptions, cachedKeys, oauthApp;
|
|
67972
68557
|
var init_oauth_routes = __esm({
|
|
67973
68558
|
"src/api/oauth-routes.ts"() {
|
|
67974
68559
|
"use strict";
|
|
67975
68560
|
import_hono26 = require("hono");
|
|
67976
68561
|
import_cookie = require("hono/cookie");
|
|
67977
|
-
|
|
68562
|
+
import_node_crypto42 = require("crypto");
|
|
67978
68563
|
import_jose2 = require("jose");
|
|
67979
68564
|
init_session();
|
|
67980
68565
|
init_db();
|
|
@@ -68081,7 +68666,7 @@ var init_oauth_routes = __esm({
|
|
|
68081
68666
|
}
|
|
68082
68667
|
}
|
|
68083
68668
|
const clientName = typeof body.client_name === "string" ? body.client_name : null;
|
|
68084
|
-
const clientId = `client_${(0,
|
|
68669
|
+
const clientId = `client_${(0, import_node_crypto42.randomBytes)(16).toString("hex")}`;
|
|
68085
68670
|
await withOAuthDatabaseDeadline("register-client", registerClient(clientId, redirectUris, clientName));
|
|
68086
68671
|
console.log("[oauth-dcr] register OK client_id=%s redirect_uris=%s", clientId, JSON.stringify(redirectUris));
|
|
68087
68672
|
return c.json({
|
|
@@ -68134,7 +68719,7 @@ var init_oauth_routes = __esm({
|
|
|
68134
68719
|
if (action3 === "deny") return redirectWithError(p.redirect_uri, p.state, "access_denied");
|
|
68135
68720
|
if (action3 !== "approve") return c.text("unsupported action", 400);
|
|
68136
68721
|
const scope = negotiateScope(p.scope, user, p.resource);
|
|
68137
|
-
const code = `code_${(0,
|
|
68722
|
+
const code = `code_${(0, import_node_crypto42.randomBytes)(32).toString("base64url")}`;
|
|
68138
68723
|
const expiresAt = new Date(Date.now() + CODE_TTL_SECONDS * 1e3).toISOString();
|
|
68139
68724
|
await withOAuthDatabaseDeadline("put-authorization-code", putCode({
|
|
68140
68725
|
code,
|
|
@@ -68173,7 +68758,7 @@ var init_oauth_routes = __esm({
|
|
|
68173
68758
|
const plan = user ? resolvePlan(user) : "free";
|
|
68174
68759
|
const audience = record.resource ?? RESOURCE();
|
|
68175
68760
|
const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
|
|
68176
|
-
const refreshToken = `rt_${(0,
|
|
68761
|
+
const refreshToken = `rt_${(0, import_node_crypto42.randomBytes)(40).toString("base64url")}`;
|
|
68177
68762
|
await withOAuthDatabaseDeadline("put-refresh-token", putRefresh({
|
|
68178
68763
|
refresh_token: refreshToken,
|
|
68179
68764
|
client_id: clientId,
|
|
@@ -68203,7 +68788,7 @@ var init_oauth_routes = __esm({
|
|
|
68203
68788
|
const plan = user ? resolvePlan(user) : "free";
|
|
68204
68789
|
const audience = record.resource ?? RESOURCE();
|
|
68205
68790
|
const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
|
|
68206
|
-
const nextRefresh = `rt_${(0,
|
|
68791
|
+
const nextRefresh = `rt_${(0, import_node_crypto42.randomBytes)(40).toString("base64url")}`;
|
|
68207
68792
|
await withOAuthDatabaseDeadline("rotate-refresh-token", rotateRefresh(refreshToken, {
|
|
68208
68793
|
refresh_token: nextRefresh,
|
|
68209
68794
|
client_id: record.client_id,
|
|
@@ -69623,7 +70208,7 @@ function quoteUntrusted(value) {
|
|
|
69623
70208
|
return clean3.split("\n").map((line) => `> ${line}`).join("\n");
|
|
69624
70209
|
}
|
|
69625
70210
|
function shortHash(value, length = 24) {
|
|
69626
|
-
return (0,
|
|
70211
|
+
return (0, import_node_crypto43.createHash)("sha256").update(value).digest("hex").slice(0, length);
|
|
69627
70212
|
}
|
|
69628
70213
|
function safePathPart(value) {
|
|
69629
70214
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "contact";
|
|
@@ -70111,11 +70696,11 @@ async function captureSupportMessage(memoryKey, email, options) {
|
|
|
70111
70696
|
direction: options.direction
|
|
70112
70697
|
};
|
|
70113
70698
|
}
|
|
70114
|
-
var
|
|
70699
|
+
var import_node_crypto43, SUPPORT_TAGS, ISSUE_TAGS;
|
|
70115
70700
|
var init_resend_support_thread = __esm({
|
|
70116
70701
|
"src/api/resend-support-thread.ts"() {
|
|
70117
70702
|
"use strict";
|
|
70118
|
-
|
|
70703
|
+
import_node_crypto43 = require("crypto");
|
|
70119
70704
|
init_memory();
|
|
70120
70705
|
SUPPORT_TAGS = [
|
|
70121
70706
|
{
|
|
@@ -70700,7 +71285,7 @@ async function claimWorkshopRegistration(input) {
|
|
|
70700
71285
|
) < ?
|
|
70701
71286
|
`,
|
|
70702
71287
|
args: [
|
|
70703
|
-
(0,
|
|
71288
|
+
(0, import_node_crypto44.randomUUID)(),
|
|
70704
71289
|
input.eventSlug,
|
|
70705
71290
|
input.email,
|
|
70706
71291
|
input.firstName,
|
|
@@ -70717,7 +71302,7 @@ async function claimWorkshopRegistration(input) {
|
|
|
70717
71302
|
id, event_slug, email, first_name, last_name, status
|
|
70718
71303
|
) VALUES (?, ?, ?, ?, ?, 'waitlisted')
|
|
70719
71304
|
`,
|
|
70720
|
-
args: [(0,
|
|
71305
|
+
args: [(0, import_node_crypto44.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
|
|
70721
71306
|
});
|
|
70722
71307
|
const waitlisted = await getWorkshopRegistration(input.eventSlug, input.email);
|
|
70723
71308
|
if (!waitlisted) throw new Error("workshop registration could not be claimed");
|
|
@@ -70732,7 +71317,7 @@ async function registerWorkshopInterest(input) {
|
|
|
70732
71317
|
id, event_slug, email, first_name, last_name, status
|
|
70733
71318
|
) VALUES (?, ?, ?, ?, ?, 'interested')
|
|
70734
71319
|
`,
|
|
70735
|
-
args: [(0,
|
|
71320
|
+
args: [(0, import_node_crypto44.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
|
|
70736
71321
|
});
|
|
70737
71322
|
const registration = await getWorkshopRegistration(input.eventSlug, input.email);
|
|
70738
71323
|
if (!registration) throw new Error("workshop interest could not be recorded");
|
|
@@ -70762,11 +71347,11 @@ async function updateWorkshopRegistration(id, patch) {
|
|
|
70762
71347
|
args: [...entries.map(([, value]) => value), id]
|
|
70763
71348
|
});
|
|
70764
71349
|
}
|
|
70765
|
-
var
|
|
71350
|
+
var import_node_crypto44;
|
|
70766
71351
|
var init_workshop_registration_repository = __esm({
|
|
70767
71352
|
"src/api/workshop-registration-repository.ts"() {
|
|
70768
71353
|
"use strict";
|
|
70769
|
-
|
|
71354
|
+
import_node_crypto44 = require("crypto");
|
|
70770
71355
|
init_db();
|
|
70771
71356
|
}
|
|
70772
71357
|
});
|
|
@@ -71339,7 +71924,7 @@ function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
|
|
|
71339
71924
|
articleCount: articles.length,
|
|
71340
71925
|
wordCount: totalWordCount,
|
|
71341
71926
|
bytes,
|
|
71342
|
-
sha256: (0,
|
|
71927
|
+
sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex"),
|
|
71343
71928
|
warnings
|
|
71344
71929
|
};
|
|
71345
71930
|
}
|
|
@@ -71422,14 +72007,14 @@ ${provenance}`;
|
|
|
71422
72007
|
...rendered,
|
|
71423
72008
|
html,
|
|
71424
72009
|
bytes: Buffer.byteLength(html),
|
|
71425
|
-
sha256: (0,
|
|
72010
|
+
sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex")
|
|
71426
72011
|
};
|
|
71427
72012
|
}
|
|
71428
|
-
var
|
|
72013
|
+
var import_node_crypto45, import_node_fs14, import_node_path21, import_marked, runtimeEntryDir, assetCache;
|
|
71429
72014
|
var init_render = __esm({
|
|
71430
72015
|
"src/editorial-reading-room/render.ts"() {
|
|
71431
72016
|
"use strict";
|
|
71432
|
-
|
|
72017
|
+
import_node_crypto45 = require("crypto");
|
|
71433
72018
|
import_node_fs14 = require("fs");
|
|
71434
72019
|
import_node_path21 = require("path");
|
|
71435
72020
|
import_marked = require("marked");
|
|
@@ -71573,8 +72158,9 @@ var init_editorial_reading_room_routes = __esm({
|
|
|
71573
72158
|
|
|
71574
72159
|
// src/api/commons-image-store.ts
|
|
71575
72160
|
async function ensureCommonsImageSchema() {
|
|
71576
|
-
|
|
71577
|
-
|
|
72161
|
+
const db = getDb();
|
|
72162
|
+
if (imageSchemaReady && imageSchemaDb === db) return;
|
|
72163
|
+
await db.execute(`
|
|
71578
72164
|
CREATE TABLE IF NOT EXISTS commons_images (
|
|
71579
72165
|
id TEXT PRIMARY KEY,
|
|
71580
72166
|
user_id INTEGER NOT NULL,
|
|
@@ -71590,8 +72176,9 @@ async function ensureCommonsImageSchema() {
|
|
|
71590
72176
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
71591
72177
|
)
|
|
71592
72178
|
`);
|
|
71593
|
-
await
|
|
72179
|
+
await db.execute(`CREATE UNIQUE INDEX IF NOT EXISTS commons_images_digest ON commons_images(digest)`);
|
|
71594
72180
|
imageSchemaReady = true;
|
|
72181
|
+
imageSchemaDb = db;
|
|
71595
72182
|
}
|
|
71596
72183
|
function sniffImageFormat(bytes) {
|
|
71597
72184
|
if (bytes.length < 12) return null;
|
|
@@ -71621,44 +72208,85 @@ async function assertPublicHttpsUrl(rawUrl) {
|
|
|
71621
72208
|
try {
|
|
71622
72209
|
url = new URL(rawUrl);
|
|
71623
72210
|
} catch {
|
|
71624
|
-
throw new CommonsImageError("image_url_invalid",
|
|
72211
|
+
throw new CommonsImageError("image_url_invalid", "The image source is not a valid URL.");
|
|
71625
72212
|
}
|
|
71626
72213
|
if (url.protocol !== "https:") {
|
|
71627
72214
|
throw new CommonsImageError("image_url_not_https", "Commons image sources must be https URLs.");
|
|
71628
72215
|
}
|
|
72216
|
+
if (url.username || url.password) {
|
|
72217
|
+
throw new CommonsImageError("image_url_invalid", "Commons image source URLs cannot contain embedded credentials.");
|
|
72218
|
+
}
|
|
71629
72219
|
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
71630
72220
|
const addresses = (0, import_node_net2.isIP)(hostname) ? [{ address: hostname }] : await (0, import_promises16.lookup)(hostname, { all: true }).catch(() => []);
|
|
71631
72221
|
if (!addresses.length) {
|
|
71632
|
-
throw new CommonsImageError("image_host_unresolvable",
|
|
72222
|
+
throw new CommonsImageError("image_host_unresolvable", "The image source host could not be resolved.");
|
|
71633
72223
|
}
|
|
71634
72224
|
if (addresses.some((entry) => isBlockedAddress(entry.address))) {
|
|
71635
72225
|
throw new CommonsImageError("image_host_blocked", "Commons image sources cannot resolve to private or loopback addresses.");
|
|
71636
72226
|
}
|
|
71637
72227
|
return url;
|
|
71638
72228
|
}
|
|
71639
|
-
async function
|
|
72229
|
+
async function readResponseBytes(response, maxBytes) {
|
|
72230
|
+
const declaredLength = Number(response.headers.get("content-length") ?? "0");
|
|
72231
|
+
if (declaredLength > maxBytes) {
|
|
72232
|
+
throw new CommonsImageError("image_too_large", `Source exceeds the ${maxBytes} byte limit.`, 413);
|
|
72233
|
+
}
|
|
72234
|
+
if (!response.body) return Buffer.alloc(0);
|
|
72235
|
+
const reader = response.body.getReader();
|
|
72236
|
+
const chunks = [];
|
|
72237
|
+
let total = 0;
|
|
72238
|
+
while (true) {
|
|
72239
|
+
const { done, value } = await reader.read();
|
|
72240
|
+
if (done) break;
|
|
72241
|
+
total += value.byteLength;
|
|
72242
|
+
if (total > maxBytes) {
|
|
72243
|
+
await reader.cancel().catch(() => void 0);
|
|
72244
|
+
throw new CommonsImageError("image_too_large", `Source exceeds the ${maxBytes} byte limit.`, 413);
|
|
72245
|
+
}
|
|
72246
|
+
chunks.push(Buffer.from(value));
|
|
72247
|
+
}
|
|
72248
|
+
return Buffer.concat(chunks, total);
|
|
72249
|
+
}
|
|
72250
|
+
async function downloadPublicSource(sourceUrl) {
|
|
71640
72251
|
let current = sourceUrl;
|
|
71641
72252
|
for (let redirect = 0; redirect <= COMMONS_IMAGE_MAX_REDIRECTS; redirect += 1) {
|
|
71642
72253
|
const url = await assertPublicHttpsUrl(current);
|
|
71643
|
-
|
|
72254
|
+
let response;
|
|
72255
|
+
try {
|
|
72256
|
+
response = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(COMMONS_IMAGE_FETCH_TIMEOUT_MS) });
|
|
72257
|
+
} catch (error) {
|
|
72258
|
+
if (error instanceof CommonsImageError) throw error;
|
|
72259
|
+
throw new CommonsImageError(
|
|
72260
|
+
"image_source_unavailable",
|
|
72261
|
+
"The image source could not be reached by Commons.",
|
|
72262
|
+
422,
|
|
72263
|
+
true
|
|
72264
|
+
);
|
|
72265
|
+
}
|
|
71644
72266
|
if (response.status >= 300 && response.status < 400) {
|
|
71645
72267
|
const location2 = response.headers.get("location");
|
|
71646
72268
|
if (!location2) throw new CommonsImageError("image_redirect_invalid", "Image source redirected without a destination.");
|
|
71647
|
-
|
|
72269
|
+
try {
|
|
72270
|
+
current = new URL(location2, url).toString();
|
|
72271
|
+
} catch {
|
|
72272
|
+
throw new CommonsImageError("image_redirect_invalid", "Image source redirected to an invalid destination.");
|
|
72273
|
+
}
|
|
71648
72274
|
continue;
|
|
71649
72275
|
}
|
|
71650
72276
|
if (!response.ok) {
|
|
71651
|
-
|
|
71652
|
-
|
|
71653
|
-
|
|
71654
|
-
|
|
71655
|
-
|
|
71656
|
-
|
|
71657
|
-
|
|
71658
|
-
if (bytes.length > COMMONS_IMAGE_MAX_BYTES) {
|
|
71659
|
-
throw new CommonsImageError("image_too_large", `Image exceeds the ${COMMONS_IMAGE_MAX_BYTES} byte limit.`, 413);
|
|
72277
|
+
const retryable = response.status === 408 || response.status === 425 || response.status === 429 || response.status >= 500;
|
|
72278
|
+
throw new CommonsImageError(
|
|
72279
|
+
"image_source_unavailable",
|
|
72280
|
+
`The image source returned HTTP ${response.status} and could not be hosted.`,
|
|
72281
|
+
422,
|
|
72282
|
+
retryable
|
|
72283
|
+
);
|
|
71660
72284
|
}
|
|
71661
|
-
return
|
|
72285
|
+
return {
|
|
72286
|
+
bytes: await readResponseBytes(response, COMMONS_IMAGE_MAX_BYTES),
|
|
72287
|
+
finalUrl: url.toString(),
|
|
72288
|
+
contentType: (response.headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase()
|
|
72289
|
+
};
|
|
71662
72290
|
}
|
|
71663
72291
|
throw new CommonsImageError("image_redirect_limit", "Image source exceeded the redirect limit.");
|
|
71664
72292
|
}
|
|
@@ -71671,20 +72299,160 @@ function decodeBase64Image(value) {
|
|
|
71671
72299
|
}
|
|
71672
72300
|
return bytes;
|
|
71673
72301
|
}
|
|
72302
|
+
function safeProvenanceUrl(rawUrl) {
|
|
72303
|
+
try {
|
|
72304
|
+
const url = new URL(rawUrl);
|
|
72305
|
+
url.username = "";
|
|
72306
|
+
url.password = "";
|
|
72307
|
+
url.search = "";
|
|
72308
|
+
url.hash = "";
|
|
72309
|
+
return url.toString();
|
|
72310
|
+
} catch {
|
|
72311
|
+
return null;
|
|
72312
|
+
}
|
|
72313
|
+
}
|
|
72314
|
+
function looksLikeHtml(bytes, declaredType) {
|
|
72315
|
+
if (declaredType === "text/html" || declaredType === "application/xhtml+xml") return true;
|
|
72316
|
+
const prefix = bytes.subarray(0, 512).toString("utf8").trimStart().toLowerCase();
|
|
72317
|
+
return prefix.startsWith("<!doctype html") || prefix.startsWith("<html") || prefix.startsWith("<head");
|
|
72318
|
+
}
|
|
72319
|
+
async function resolveCommonsImageSource(input) {
|
|
72320
|
+
const supplied = [input.sourceUrl, input.imageBase64, input.artifactId].filter((value) => Boolean(value)).length;
|
|
72321
|
+
if (supplied === 0) {
|
|
72322
|
+
throw new CommonsImageError("image_source_missing", "Provide exactly one of sourceUrl, imageBase64, or artifactId.");
|
|
72323
|
+
}
|
|
72324
|
+
if (supplied !== 1) {
|
|
72325
|
+
throw new CommonsImageError("image_source_ambiguous", "Provide only one image source: sourceUrl, imageBase64, or artifactId.");
|
|
72326
|
+
}
|
|
72327
|
+
if (input.sourceUrl && input.sourceType) {
|
|
72328
|
+
throw new CommonsImageError("image_source_type_invalid", "sourceType applies only to imageBase64 or artifactId, not sourceUrl.");
|
|
72329
|
+
}
|
|
72330
|
+
if (input.imageBase64) {
|
|
72331
|
+
return {
|
|
72332
|
+
bytes: decodeBase64Image(input.imageBase64),
|
|
72333
|
+
sourceUrl: null,
|
|
72334
|
+
sourcePageUrl: null,
|
|
72335
|
+
sourceKind: input.sourceType ?? "uploaded",
|
|
72336
|
+
selectionSource: "provided_bytes"
|
|
72337
|
+
};
|
|
72338
|
+
}
|
|
72339
|
+
if (input.artifactId) {
|
|
72340
|
+
const bytes = await readOwnedImageSourceArtifact({ artifactId: input.artifactId, ownerId: String(input.userId) });
|
|
72341
|
+
if (!bytes) {
|
|
72342
|
+
throw new CommonsImageError("image_artifact_not_found", "The image artifact was not found, expired, or is not owned by this account.", 422);
|
|
72343
|
+
}
|
|
72344
|
+
if (bytes.length > COMMONS_IMAGE_MAX_BYTES) {
|
|
72345
|
+
throw new CommonsImageError("image_too_large", `Image exceeds the ${COMMONS_IMAGE_MAX_BYTES} byte limit.`, 413);
|
|
72346
|
+
}
|
|
72347
|
+
return {
|
|
72348
|
+
bytes,
|
|
72349
|
+
sourceUrl: null,
|
|
72350
|
+
sourcePageUrl: null,
|
|
72351
|
+
sourceKind: input.sourceType ?? "artifact",
|
|
72352
|
+
selectionSource: "artifact"
|
|
72353
|
+
};
|
|
72354
|
+
}
|
|
72355
|
+
const requestedUrl = String(input.sourceUrl);
|
|
72356
|
+
const downloaded = await downloadPublicSource(requestedUrl);
|
|
72357
|
+
if (sniffImageFormat(downloaded.bytes)) {
|
|
72358
|
+
return {
|
|
72359
|
+
bytes: downloaded.bytes,
|
|
72360
|
+
sourceUrl: safeProvenanceUrl(downloaded.finalUrl),
|
|
72361
|
+
sourcePageUrl: null,
|
|
72362
|
+
sourceKind: "direct_url",
|
|
72363
|
+
selectionSource: "direct_url"
|
|
72364
|
+
};
|
|
72365
|
+
}
|
|
72366
|
+
if (!looksLikeHtml(downloaded.bytes, downloaded.contentType)) {
|
|
72367
|
+
throw new CommonsImageError(
|
|
72368
|
+
"image_format_unsupported",
|
|
72369
|
+
"The supplied URL returned neither a supported image nor an HTML webpage with a discoverable image.",
|
|
72370
|
+
415
|
|
72371
|
+
);
|
|
72372
|
+
}
|
|
72373
|
+
if (downloaded.bytes.length > COMMONS_IMAGE_PAGE_MAX_BYTES) {
|
|
72374
|
+
throw new CommonsImageError("image_page_too_large", `Webpage exceeds the ${COMMONS_IMAGE_PAGE_MAX_BYTES} byte discovery limit.`, 413);
|
|
72375
|
+
}
|
|
72376
|
+
const html = downloaded.bytes.toString("utf8");
|
|
72377
|
+
const document2 = loadHtmlDocument(html);
|
|
72378
|
+
const candidates = resolveFeaturedImageCandidates({
|
|
72379
|
+
html,
|
|
72380
|
+
meta: document2.meta,
|
|
72381
|
+
schema: document2.schema,
|
|
72382
|
+
pageUrl: downloaded.finalUrl
|
|
72383
|
+
}).slice(0, COMMONS_IMAGE_MAX_PAGE_CANDIDATES);
|
|
72384
|
+
if (!candidates.length) {
|
|
72385
|
+
throw new CommonsImageError("page_image_not_found", "The webpage did not expose a usable featured or content image.", 422);
|
|
72386
|
+
}
|
|
72387
|
+
let sawRetryableFailure = false;
|
|
72388
|
+
for (const candidate of candidates) {
|
|
72389
|
+
try {
|
|
72390
|
+
const image = await downloadPublicSource(candidate.url);
|
|
72391
|
+
if (!sniffImageFormat(image.bytes)) continue;
|
|
72392
|
+
return {
|
|
72393
|
+
bytes: image.bytes,
|
|
72394
|
+
sourceUrl: safeProvenanceUrl(image.finalUrl),
|
|
72395
|
+
sourcePageUrl: safeProvenanceUrl(downloaded.finalUrl),
|
|
72396
|
+
sourceKind: "webpage",
|
|
72397
|
+
selectionSource: candidate.source
|
|
72398
|
+
};
|
|
72399
|
+
} catch (error) {
|
|
72400
|
+
if (error instanceof CommonsImageError && error.retryable) sawRetryableFailure = true;
|
|
72401
|
+
}
|
|
72402
|
+
}
|
|
72403
|
+
throw new CommonsImageError(
|
|
72404
|
+
"page_image_candidates_unusable",
|
|
72405
|
+
"The webpage exposed image candidates, but none returned supported public image bytes.",
|
|
72406
|
+
422,
|
|
72407
|
+
sawRetryableFailure
|
|
72408
|
+
);
|
|
72409
|
+
}
|
|
71674
72410
|
function commonsImageBlobStore() {
|
|
71675
72411
|
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();
|
|
71676
72412
|
return token6 ? new VercelBlobStore(token6) : getBlobStore();
|
|
71677
72413
|
}
|
|
71678
|
-
function
|
|
71679
|
-
|
|
72414
|
+
function normalizeCommonsImageUrl(rawUrl) {
|
|
72415
|
+
try {
|
|
72416
|
+
const url = new URL(rawUrl.trim());
|
|
72417
|
+
if (url.protocol !== "https:") return null;
|
|
72418
|
+
url.hash = "";
|
|
72419
|
+
return url.toString();
|
|
72420
|
+
} catch {
|
|
72421
|
+
return null;
|
|
72422
|
+
}
|
|
72423
|
+
}
|
|
72424
|
+
async function findCommonsHostedImageByUrl(rawUrl) {
|
|
72425
|
+
const url = normalizeCommonsImageUrl(rawUrl);
|
|
72426
|
+
if (!url) return null;
|
|
72427
|
+
await ensureCommonsImageSchema();
|
|
72428
|
+
const result = await getDb().execute({
|
|
72429
|
+
sql: "SELECT id, url, content_type, bytes, digest, source_url FROM commons_images WHERE url = ? LIMIT 1",
|
|
72430
|
+
args: [url]
|
|
72431
|
+
});
|
|
72432
|
+
const row = result.rows[0];
|
|
72433
|
+
if (!row) return null;
|
|
72434
|
+
return {
|
|
72435
|
+
id: String(row.id),
|
|
72436
|
+
url: String(row.url),
|
|
72437
|
+
contentType: String(row.content_type),
|
|
72438
|
+
bytes: Number(row.bytes),
|
|
72439
|
+
digest: String(row.digest),
|
|
72440
|
+
sourceUrl: row.source_url ? String(row.source_url) : null,
|
|
72441
|
+
sourcePageUrl: null,
|
|
72442
|
+
sourceKind: "registered",
|
|
72443
|
+
selectionSource: "registered",
|
|
72444
|
+
reused: true
|
|
72445
|
+
};
|
|
71680
72446
|
}
|
|
71681
72447
|
async function hostCommonsImage(input) {
|
|
71682
72448
|
if (hostOverride) return hostOverride(input);
|
|
71683
72449
|
await ensureCommonsImageSchema();
|
|
71684
|
-
if (
|
|
71685
|
-
|
|
72450
|
+
if (input.sourceUrl && !input.imageBase64 && !input.artifactId && !input.sourceType) {
|
|
72451
|
+
const registered6 = await findCommonsHostedImageByUrl(input.sourceUrl);
|
|
72452
|
+
if (registered6) return registered6;
|
|
71686
72453
|
}
|
|
71687
|
-
const
|
|
72454
|
+
const resolved = await resolveCommonsImageSource(input);
|
|
72455
|
+
const bytes = resolved.bytes;
|
|
71688
72456
|
const contentType = sniffImageFormat(bytes);
|
|
71689
72457
|
if (!contentType) {
|
|
71690
72458
|
throw new CommonsImageError(
|
|
@@ -71693,7 +72461,7 @@ async function hostCommonsImage(input) {
|
|
|
71693
72461
|
415
|
|
71694
72462
|
);
|
|
71695
72463
|
}
|
|
71696
|
-
const digest2 = (0,
|
|
72464
|
+
const digest2 = (0, import_node_crypto46.createHash)("sha256").update(bytes).digest("hex");
|
|
71697
72465
|
const existing = await getDb().execute({
|
|
71698
72466
|
sql: "SELECT id, url, content_type, bytes, source_url FROM commons_images WHERE digest = ? LIMIT 1",
|
|
71699
72467
|
args: [digest2]
|
|
@@ -71706,7 +72474,10 @@ async function hostCommonsImage(input) {
|
|
|
71706
72474
|
contentType: String(reusable.content_type),
|
|
71707
72475
|
bytes: Number(reusable.bytes),
|
|
71708
72476
|
digest: digest2,
|
|
71709
|
-
sourceUrl:
|
|
72477
|
+
sourceUrl: resolved.sourceUrl,
|
|
72478
|
+
sourcePageUrl: resolved.sourcePageUrl,
|
|
72479
|
+
sourceKind: resolved.sourceKind,
|
|
72480
|
+
selectionSource: resolved.selectionSource,
|
|
71710
72481
|
reused: true
|
|
71711
72482
|
};
|
|
71712
72483
|
}
|
|
@@ -71735,19 +72506,31 @@ async function hostCommonsImage(input) {
|
|
|
71735
72506
|
stored.url,
|
|
71736
72507
|
contentType,
|
|
71737
72508
|
bytes.length,
|
|
71738
|
-
|
|
72509
|
+
resolved.sourceUrl,
|
|
71739
72510
|
input.alt ?? null,
|
|
71740
72511
|
input.license ?? null,
|
|
71741
72512
|
input.attribution ?? null,
|
|
71742
72513
|
(/* @__PURE__ */ new Date()).toISOString()
|
|
71743
72514
|
]
|
|
71744
72515
|
});
|
|
71745
|
-
return {
|
|
72516
|
+
return {
|
|
72517
|
+
id,
|
|
72518
|
+
url: stored.url,
|
|
72519
|
+
contentType,
|
|
72520
|
+
bytes: bytes.length,
|
|
72521
|
+
digest: digest2,
|
|
72522
|
+
sourceUrl: resolved.sourceUrl,
|
|
72523
|
+
sourcePageUrl: resolved.sourcePageUrl,
|
|
72524
|
+
sourceKind: resolved.sourceKind,
|
|
72525
|
+
selectionSource: resolved.selectionSource,
|
|
72526
|
+
reused: false
|
|
72527
|
+
};
|
|
71746
72528
|
}
|
|
71747
72529
|
async function hostEntityImages(input) {
|
|
71748
72530
|
const rewrite = { mediaUrls: /* @__PURE__ */ new Map(), dropped: [] };
|
|
71749
72531
|
const featuredUrl = input.featuredImage?.url;
|
|
71750
|
-
|
|
72532
|
+
const registeredFeatured = featuredUrl ? await findCommonsHostedImageByUrl(featuredUrl) : null;
|
|
72533
|
+
if (featuredUrl && !registeredFeatured) {
|
|
71751
72534
|
const hosted = await hostCommonsImage({
|
|
71752
72535
|
userId: input.userId,
|
|
71753
72536
|
sourceUrl: featuredUrl,
|
|
@@ -71755,17 +72538,19 @@ async function hostEntityImages(input) {
|
|
|
71755
72538
|
license: input.featuredImage?.license,
|
|
71756
72539
|
attribution: input.featuredImage?.sourceUrl
|
|
71757
72540
|
}).catch((error) => {
|
|
71758
|
-
|
|
72541
|
+
if (error instanceof CommonsImageError) throw error;
|
|
71759
72542
|
throw new CommonsImageError(
|
|
71760
|
-
|
|
71761
|
-
|
|
72543
|
+
"featured_image_unhostable",
|
|
72544
|
+
"The featured image could not be hosted, so the entity was not persisted with a link that could break.",
|
|
71762
72545
|
422
|
|
71763
72546
|
);
|
|
71764
72547
|
});
|
|
71765
72548
|
rewrite.featuredImageUrl = hosted.url;
|
|
72549
|
+
rewrite.mediaUrls.set(featuredUrl, hosted.url);
|
|
71766
72550
|
}
|
|
71767
72551
|
for (const item of input.media ?? []) {
|
|
71768
|
-
if (
|
|
72552
|
+
if (item.url && rewrite.mediaUrls.has(item.url)) continue;
|
|
72553
|
+
if (!item.url || await findCommonsHostedImageByUrl(item.url)) continue;
|
|
71769
72554
|
try {
|
|
71770
72555
|
const hosted = await hostCommonsImage({
|
|
71771
72556
|
userId: input.userId,
|
|
@@ -71781,29 +72566,40 @@ async function hostEntityImages(input) {
|
|
|
71781
72566
|
}
|
|
71782
72567
|
return rewrite;
|
|
71783
72568
|
}
|
|
71784
|
-
var
|
|
72569
|
+
var import_node_crypto46, import_promises16, import_node_net2, COMMONS_IMAGE_MAX_BYTES, COMMONS_IMAGE_MAX_REDIRECTS, COMMONS_IMAGE_FETCH_TIMEOUT_MS, COMMONS_IMAGE_PAGE_MAX_BYTES, COMMONS_IMAGE_MAX_PAGE_CANDIDATES, COMMONS_IMAGE_RETRY_GUIDANCE, CommonsImageError, imageSchemaReady, imageSchemaDb, hostOverride;
|
|
71785
72570
|
var init_commons_image_store = __esm({
|
|
71786
72571
|
"src/api/commons-image-store.ts"() {
|
|
71787
72572
|
"use strict";
|
|
71788
|
-
|
|
72573
|
+
import_node_crypto46 = require("crypto");
|
|
71789
72574
|
import_promises16 = require("dns/promises");
|
|
71790
72575
|
import_node_net2 = require("net");
|
|
71791
72576
|
init_blob_store();
|
|
71792
72577
|
init_db();
|
|
72578
|
+
init_image_source_artifacts();
|
|
72579
|
+
init_html_document();
|
|
72580
|
+
init_wayback();
|
|
71793
72581
|
COMMONS_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
71794
72582
|
COMMONS_IMAGE_MAX_REDIRECTS = 3;
|
|
71795
72583
|
COMMONS_IMAGE_FETCH_TIMEOUT_MS = 2e4;
|
|
72584
|
+
COMMONS_IMAGE_PAGE_MAX_BYTES = 2 * 1024 * 1024;
|
|
72585
|
+
COMMONS_IMAGE_MAX_PAGE_CANDIDATES = 12;
|
|
72586
|
+
COMMONS_IMAGE_RETRY_GUIDANCE = "Call commons_host_image with imageBase64, an owner-authorized artifactId, a public HTTPS image URL, or a public HTTPS webpage containing a usable featured image. Replace featuredImage.url with the returned permanent URL, validate again, then retry commons_submit_entity with the same idempotency key.";
|
|
71796
72587
|
CommonsImageError = class extends Error {
|
|
71797
|
-
constructor(code, message, httpStatus = 400) {
|
|
72588
|
+
constructor(code, message, httpStatus = 400, retryable = false, retryGuidance = COMMONS_IMAGE_RETRY_GUIDANCE) {
|
|
71798
72589
|
super(message);
|
|
71799
72590
|
this.code = code;
|
|
71800
72591
|
this.httpStatus = httpStatus;
|
|
72592
|
+
this.retryable = retryable;
|
|
72593
|
+
this.retryGuidance = retryGuidance;
|
|
71801
72594
|
this.name = "CommonsImageError";
|
|
71802
72595
|
}
|
|
71803
72596
|
code;
|
|
71804
72597
|
httpStatus;
|
|
72598
|
+
retryable;
|
|
72599
|
+
retryGuidance;
|
|
71805
72600
|
};
|
|
71806
72601
|
imageSchemaReady = false;
|
|
72602
|
+
imageSchemaDb = null;
|
|
71807
72603
|
hostOverride = null;
|
|
71808
72604
|
}
|
|
71809
72605
|
});
|
|
@@ -71910,7 +72706,7 @@ function buildCommonsLinkset(entity, claims) {
|
|
|
71910
72706
|
context[claim.predicate] = targets;
|
|
71911
72707
|
}
|
|
71912
72708
|
const document2 = { linkset: [context] };
|
|
71913
|
-
const etag = `"${(0,
|
|
72709
|
+
const etag = `"${(0, import_node_crypto47.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
|
|
71914
72710
|
return { document: document2, etag, publicUrl, linksetUrl, profile: COMMONS_RELATIONSHIP_PROFILE };
|
|
71915
72711
|
}
|
|
71916
72712
|
function commonsLinksetDiscoveryHeader(idOrSlug) {
|
|
@@ -71978,11 +72774,11 @@ function normalizeHreflang(value) {
|
|
|
71978
72774
|
const languages = [...new Set(value.map((item) => optionalText(item, 80)).filter((item) => Boolean(item)))];
|
|
71979
72775
|
return languages.length ? languages : void 0;
|
|
71980
72776
|
}
|
|
71981
|
-
var
|
|
72777
|
+
var import_node_crypto47, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
|
|
71982
72778
|
var init_commons_linksets = __esm({
|
|
71983
72779
|
"src/api/commons-linksets.ts"() {
|
|
71984
72780
|
"use strict";
|
|
71985
|
-
|
|
72781
|
+
import_node_crypto47 = require("crypto");
|
|
71986
72782
|
COMMONS_LINKSET_MEDIA_TYPE = "application/linkset+json";
|
|
71987
72783
|
COMMONS_RELATIONSHIP_PROFILE = "https://mcpscraper.dev/commons/profiles/relationships/v1";
|
|
71988
72784
|
REGISTERED_RELATIONS = /* @__PURE__ */ new Set([
|
|
@@ -72025,9 +72821,9 @@ var init_commons_linksets = __esm({
|
|
|
72025
72821
|
|
|
72026
72822
|
// src/api/commons-repository.ts
|
|
72027
72823
|
async function ensureCommonsSchema() {
|
|
72028
|
-
if (
|
|
72824
|
+
if (schemaReady2) return;
|
|
72029
72825
|
if (await schemaObjectsPresent(COMMONS_SCHEMA_OBJECTS)) {
|
|
72030
|
-
|
|
72826
|
+
schemaReady2 = true;
|
|
72031
72827
|
return;
|
|
72032
72828
|
}
|
|
72033
72829
|
const db = getDb();
|
|
@@ -72250,7 +73046,7 @@ async function ensureCommonsSchema() {
|
|
|
72250
73046
|
args: [COMMONS_SCHEMA_VERSION]
|
|
72251
73047
|
}
|
|
72252
73048
|
], "write");
|
|
72253
|
-
|
|
73049
|
+
schemaReady2 = true;
|
|
72254
73050
|
}
|
|
72255
73051
|
async function searchCommonsEntities(filters = {}, userId) {
|
|
72256
73052
|
await ensureCommonsSchema();
|
|
@@ -72458,7 +73254,7 @@ async function prepareCommonsEntity(input, user) {
|
|
|
72458
73254
|
normalizedEntityType: entityType,
|
|
72459
73255
|
proposedSlug: slug4 || null,
|
|
72460
73256
|
publicUrl: slug4 ? publicUrlForSlug(slug4) : null,
|
|
72461
|
-
writePath: "commons_prepare_entity -> commons_validate_entity -> commons_submit_entity"
|
|
73257
|
+
writePath: "commons_prepare_entity -> compose the entity -> commons_host_image -> commons_validate_entity -> commons_submit_entity"
|
|
72462
73258
|
},
|
|
72463
73259
|
contract,
|
|
72464
73260
|
billing: {
|
|
@@ -72480,8 +73276,8 @@ async function prepareCommonsEntity(input, user) {
|
|
|
72480
73276
|
"Search likely duplicates before submitting a new entity. If one matches, edit it with entityId and baseRevision instead of creating another page.",
|
|
72481
73277
|
"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.",
|
|
72482
73278
|
"Write neutral encyclopedia prose. Do not paste raw scrape Markdown, marketing layout fragments, or empty/filler headings.",
|
|
72483
|
-
"
|
|
72484
|
-
"Call commons_validate_entity with the composed payload before commons_submit_entity.",
|
|
73279
|
+
"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.",
|
|
73280
|
+
"Call commons_validate_entity with the composed, image-ready payload before commons_submit_entity.",
|
|
72485
73281
|
"Validation is free. Submitting a new entity costs 10 Credits; submitting an edit to an existing entity costs 2 Credits."
|
|
72486
73282
|
]
|
|
72487
73283
|
};
|
|
@@ -72496,6 +73292,8 @@ async function validateCommonsEntity(input, user) {
|
|
|
72496
73292
|
const contract = commonsEntityProfileFor(input.entityType, input.seo?.schemaOrgType);
|
|
72497
73293
|
const source = normalizeSource(input.source);
|
|
72498
73294
|
const featuredImage = normalizeFeaturedImage(input.featuredImage);
|
|
73295
|
+
const normalizedFeaturedImageUrl = featuredImage?.url ? normalizeCommonsImageUrl(featuredImage.url) : null;
|
|
73296
|
+
const registeredFeaturedImage = normalizedFeaturedImageUrl ? await findCommonsHostedImageByUrl(normalizedFeaturedImageUrl) : null;
|
|
72499
73297
|
const bodyMarkdown = safeCandidateBody(input.bodyMarkdown, input.contentSections);
|
|
72500
73298
|
const headings = extractMarkdownHeadings(bodyMarkdown);
|
|
72501
73299
|
const placeholderSections = unsupportedPlaceholderSections(bodyMarkdown);
|
|
@@ -72533,15 +73331,44 @@ async function validateCommonsEntity(input, user) {
|
|
|
72533
73331
|
if (input.reviewPolicy === "always_review") {
|
|
72534
73332
|
warnings.push("reviewPolicy is always_review, so the proposal will not auto-publish even if the payload is otherwise valid.");
|
|
72535
73333
|
}
|
|
73334
|
+
if (featuredImage?.url && !registeredFeaturedImage) {
|
|
73335
|
+
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.");
|
|
73336
|
+
}
|
|
72536
73337
|
if (user?.id == null) warnings.push("No authenticated user was supplied to validation; submit still requires API-key authenticated user context.");
|
|
72537
73338
|
const valid = errors.length === 0;
|
|
72538
|
-
const
|
|
73339
|
+
const imageReadyForPublish = Boolean(registeredFeaturedImage);
|
|
73340
|
+
const publishable = valid && imageReadyForPublish;
|
|
73341
|
+
const autoPublishEligible = publishable && input.reviewPolicy !== "always_review";
|
|
72539
73342
|
const tagVocabulary = await commonsTagVocabulary();
|
|
72540
73343
|
return {
|
|
72541
73344
|
ok: true,
|
|
72542
73345
|
valid,
|
|
72543
|
-
publishable
|
|
73346
|
+
publishable,
|
|
72544
73347
|
autoPublishEligible,
|
|
73348
|
+
imageDiagnostics: {
|
|
73349
|
+
featuredImage: registeredFeaturedImage ? {
|
|
73350
|
+
state: "commons_hosted",
|
|
73351
|
+
url: registeredFeaturedImage.url,
|
|
73352
|
+
commonsImageId: registeredFeaturedImage.id,
|
|
73353
|
+
readyForPublish: true,
|
|
73354
|
+
submitWillAttemptAutoHost: false,
|
|
73355
|
+
requiredAction: null
|
|
73356
|
+
} : featuredImage?.url ? {
|
|
73357
|
+
state: "external_unhosted",
|
|
73358
|
+
url: normalizedFeaturedImageUrl,
|
|
73359
|
+
commonsImageId: null,
|
|
73360
|
+
readyForPublish: false,
|
|
73361
|
+
submitWillAttemptAutoHost: true,
|
|
73362
|
+
requiredAction: "Call commons_host_image, replace featuredImage.url with its returned permanent URL, and validate again."
|
|
73363
|
+
} : {
|
|
73364
|
+
state: "missing",
|
|
73365
|
+
url: null,
|
|
73366
|
+
commonsImageId: null,
|
|
73367
|
+
readyForPublish: false,
|
|
73368
|
+
submitWillAttemptAutoHost: false,
|
|
73369
|
+
requiredAction: "Add a featured image by calling commons_host_image, then set featuredImage.url to its returned permanent URL."
|
|
73370
|
+
}
|
|
73371
|
+
},
|
|
72545
73372
|
errors,
|
|
72546
73373
|
warnings,
|
|
72547
73374
|
contract,
|
|
@@ -72622,7 +73449,7 @@ async function submitCommonsEntity(input, user) {
|
|
|
72622
73449
|
const existingClaims = existing && normalized.claims !== void 0 ? await getCommonsClaimsByEntityId(existing.id) : [];
|
|
72623
73450
|
const safety = evaluatePublishSafety(normalized, existing);
|
|
72624
73451
|
const shouldApply = safety.safe && normalized.reviewPolicy !== "always_review";
|
|
72625
|
-
const proposalId = `commons-proposal-${(0,
|
|
73452
|
+
const proposalId = `commons-proposal-${(0, import_node_crypto48.randomUUID)()}`;
|
|
72626
73453
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
72627
73454
|
const entityId = existing?.id ?? normalized.entityId ?? allocateEntityId();
|
|
72628
73455
|
const proposalStatus = shouldApply ? "accepted" : "pending_review";
|
|
@@ -72677,7 +73504,7 @@ async function submitCommonsEntity(input, user) {
|
|
|
72677
73504
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
72678
73505
|
`,
|
|
72679
73506
|
args: [
|
|
72680
|
-
`commons-ledger-${(0,
|
|
73507
|
+
`commons-ledger-${(0, import_node_crypto48.randomUUID)()}`,
|
|
72681
73508
|
nextEntity.id,
|
|
72682
73509
|
proposalId,
|
|
72683
73510
|
user.id,
|
|
@@ -72783,7 +73610,7 @@ async function saveCommonsFilter(input, user) {
|
|
|
72783
73610
|
sql: "SELECT id FROM commons_saved_filters WHERE user_id = ? AND name = ? LIMIT 1",
|
|
72784
73611
|
args: [user.id, name]
|
|
72785
73612
|
});
|
|
72786
|
-
const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0,
|
|
73613
|
+
const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto48.randomUUID)()}`;
|
|
72787
73614
|
await getDb().execute({
|
|
72788
73615
|
sql: `
|
|
72789
73616
|
INSERT INTO commons_saved_filters (id, user_id, name, description, filter_json, created_at, updated_at)
|
|
@@ -73561,7 +74388,7 @@ function commonsIndexDocuments(entity, now) {
|
|
|
73561
74388
|
return documents;
|
|
73562
74389
|
}
|
|
73563
74390
|
function commonsIndexDocumentId(entityId, documentType, documentKey) {
|
|
73564
|
-
return `commons-index-${(0,
|
|
74391
|
+
return `commons-index-${(0, import_node_crypto48.createHash)("sha256").update(`${entityId}
|
|
73565
74392
|
${documentType}
|
|
73566
74393
|
${documentKey}`).digest("hex").slice(0, 32)}`;
|
|
73567
74394
|
}
|
|
@@ -74127,11 +74954,11 @@ function jsonLikeValue(value) {
|
|
|
74127
74954
|
function escapeLike(value) {
|
|
74128
74955
|
return value.replace(/[%_]/g, "");
|
|
74129
74956
|
}
|
|
74130
|
-
var
|
|
74957
|
+
var import_node_crypto48, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES, schemaReady2, COMMONS_SCHEMA_OBJECTS, SEARCH_CANDIDATE_LIMIT, CommonsRepositoryError;
|
|
74131
74958
|
var init_commons_repository = __esm({
|
|
74132
74959
|
"src/api/commons-repository.ts"() {
|
|
74133
74960
|
"use strict";
|
|
74134
|
-
|
|
74961
|
+
import_node_crypto48 = require("crypto");
|
|
74135
74962
|
init_db();
|
|
74136
74963
|
init_commons_image_store();
|
|
74137
74964
|
init_commons_embeddings();
|
|
@@ -74286,7 +75113,7 @@ var init_commons_repository = __esm({
|
|
|
74286
75113
|
]
|
|
74287
75114
|
}
|
|
74288
75115
|
};
|
|
74289
|
-
|
|
75116
|
+
schemaReady2 = false;
|
|
74290
75117
|
COMMONS_SCHEMA_OBJECTS = [
|
|
74291
75118
|
"schema_migrations",
|
|
74292
75119
|
"commons_entities",
|
|
@@ -74510,7 +75337,7 @@ async function claimCommonsPublication(input, user) {
|
|
|
74510
75337
|
}
|
|
74511
75338
|
const existingName = await getCommonsPublicationBySubdomain(subdomain);
|
|
74512
75339
|
if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
|
|
74513
|
-
const id = `tcpub_${(0,
|
|
75340
|
+
const id = `tcpub_${(0, import_node_crypto49.randomUUID)()}`;
|
|
74514
75341
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
74515
75342
|
try {
|
|
74516
75343
|
await getDb().execute({
|
|
@@ -74562,7 +75389,7 @@ async function publishCommonsEditorial(input, user) {
|
|
|
74562
75389
|
const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
|
|
74563
75390
|
const rendered = renderEditorialReadingRoom(editionInput);
|
|
74564
75391
|
const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
|
|
74565
|
-
const editionId = `tced_${(0,
|
|
75392
|
+
const editionId = `tced_${(0, import_node_crypto49.randomUUID)()}`;
|
|
74566
75393
|
const revision = (latest?.revision ?? 0) + 1;
|
|
74567
75394
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
74568
75395
|
await getDb().batch([
|
|
@@ -74586,7 +75413,7 @@ async function publishCommonsEditorial(input, user) {
|
|
|
74586
75413
|
JSON.stringify(editionInput.articles),
|
|
74587
75414
|
html,
|
|
74588
75415
|
rendered.filename,
|
|
74589
|
-
(0,
|
|
75416
|
+
(0, import_node_crypto49.createHash)("sha256").update(html).digest("hex"),
|
|
74590
75417
|
rendered.articleCount,
|
|
74591
75418
|
rendered.wordCount,
|
|
74592
75419
|
Buffer.byteLength(html),
|
|
@@ -74644,7 +75471,7 @@ async function updateCommonsEditorialArticle(input, user) {
|
|
|
74644
75471
|
const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
|
|
74645
75472
|
const rendered = renderEditorialReadingRoom(editionInput);
|
|
74646
75473
|
const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
|
|
74647
|
-
const editionId = `tced_${(0,
|
|
75474
|
+
const editionId = `tced_${(0, import_node_crypto49.randomUUID)()}`;
|
|
74648
75475
|
const revision = latest.revision + 1;
|
|
74649
75476
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
74650
75477
|
await getDb().batch([
|
|
@@ -74668,7 +75495,7 @@ async function updateCommonsEditorialArticle(input, user) {
|
|
|
74668
75495
|
JSON.stringify(nextArticles),
|
|
74669
75496
|
html,
|
|
74670
75497
|
rendered.filename,
|
|
74671
|
-
(0,
|
|
75498
|
+
(0, import_node_crypto49.createHash)("sha256").update(html).digest("hex"),
|
|
74672
75499
|
rendered.articleCount,
|
|
74673
75500
|
rendered.wordCount,
|
|
74674
75501
|
Buffer.byteLength(html),
|
|
@@ -74873,11 +75700,11 @@ function addPublicMetadata(html, canonicalUrl, publicationTitle) {
|
|
|
74873
75700
|
"</head>"
|
|
74874
75701
|
].join("\n"));
|
|
74875
75702
|
}
|
|
74876
|
-
var
|
|
75703
|
+
var import_node_crypto49, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
|
|
74877
75704
|
var init_commons_publication_repository = __esm({
|
|
74878
75705
|
"src/api/commons-publication-repository.ts"() {
|
|
74879
75706
|
"use strict";
|
|
74880
|
-
|
|
75707
|
+
import_node_crypto49 = require("crypto");
|
|
74881
75708
|
init_db();
|
|
74882
75709
|
init_commons_repository();
|
|
74883
75710
|
init_render();
|
|
@@ -74964,6 +75791,18 @@ function repositoryStatus(error) {
|
|
|
74964
75791
|
if (error.httpStatus === 409) return 409;
|
|
74965
75792
|
return 400;
|
|
74966
75793
|
}
|
|
75794
|
+
function imageErrorResponse(c, error, chargeStatus) {
|
|
75795
|
+
return c.json({
|
|
75796
|
+
ok: false,
|
|
75797
|
+
error: error.code,
|
|
75798
|
+
error_code: error.code,
|
|
75799
|
+
error_type: "invalid_request",
|
|
75800
|
+
retryable: error.retryable,
|
|
75801
|
+
...chargeStatus ? { charge_status: chargeStatus } : {},
|
|
75802
|
+
message: `${error.message} ${error.retryGuidance}`,
|
|
75803
|
+
details: { retry_guidance: error.retryGuidance }
|
|
75804
|
+
}, error.httpStatus);
|
|
75805
|
+
}
|
|
74967
75806
|
async function publicationOperation(c, operation, successStatus = 200) {
|
|
74968
75807
|
try {
|
|
74969
75808
|
return c.json({ ok: true, data: await operation() }, successStatus);
|
|
@@ -75290,11 +76129,13 @@ var init_commons_routes = __esm({
|
|
|
75290
76129
|
HostImageSchema = import_zod54.z.object({
|
|
75291
76130
|
sourceUrl: import_zod54.z.string().url().optional(),
|
|
75292
76131
|
imageBase64: import_zod54.z.string().min(16).optional(),
|
|
76132
|
+
artifactId: import_zod54.z.string().min(1).max(500).optional(),
|
|
76133
|
+
sourceType: import_zod54.z.enum(["uploaded", "ai_generated"]).optional(),
|
|
75293
76134
|
alt: import_zod54.z.string().max(500).optional(),
|
|
75294
76135
|
license: import_zod54.z.string().max(240).optional(),
|
|
75295
76136
|
attribution: import_zod54.z.string().max(500).optional()
|
|
75296
|
-
}).strict().refine((body) =>
|
|
75297
|
-
message: "Provide
|
|
76137
|
+
}).strict().refine((body) => [body.sourceUrl, body.imageBase64, body.artifactId].filter(Boolean).length === 1, {
|
|
76138
|
+
message: "Provide exactly one of sourceUrl, imageBase64, or artifactId."
|
|
75298
76139
|
});
|
|
75299
76140
|
commonsApp.post("/images", auth2, async (c) => {
|
|
75300
76141
|
const parsed = HostImageSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
@@ -75304,7 +76145,7 @@ var init_commons_routes = __esm({
|
|
|
75304
76145
|
return c.json({ ok: true, data: hosted }, hosted.reused ? 200 : 201);
|
|
75305
76146
|
} catch (error) {
|
|
75306
76147
|
if (error instanceof CommonsImageError) {
|
|
75307
|
-
return c
|
|
76148
|
+
return imageErrorResponse(c, error);
|
|
75308
76149
|
}
|
|
75309
76150
|
throw error;
|
|
75310
76151
|
}
|
|
@@ -75368,7 +76209,14 @@ var init_commons_routes = __esm({
|
|
|
75368
76209
|
}
|
|
75369
76210
|
return c.json({ ok: true, data: result, billing: publicListingWriteBilling(billing) }, 201);
|
|
75370
76211
|
} catch (error) {
|
|
75371
|
-
|
|
76212
|
+
let chargeStatus = billing ? billing.duplicate ? "not_charged" : "refund_pending" : "not_charged";
|
|
76213
|
+
if (billing && !billing.duplicate) {
|
|
76214
|
+
const settlement = await refundListingWrite(billing, "Transparent Commons write failed before persistence.").catch(() => null);
|
|
76215
|
+
if (settlement) chargeStatus = "refunded";
|
|
76216
|
+
}
|
|
76217
|
+
if (error instanceof CommonsImageError) {
|
|
76218
|
+
return imageErrorResponse(c, error, chargeStatus);
|
|
76219
|
+
}
|
|
75372
76220
|
if (error instanceof CommonsRepositoryError) {
|
|
75373
76221
|
return c.json({ ok: false, error: error.code, message: error.message }, repositoryStatus(error));
|
|
75374
76222
|
}
|
|
@@ -76110,7 +76958,7 @@ async function migrateAnalytics() {
|
|
|
76110
76958
|
}
|
|
76111
76959
|
function normalizeSlug2(value) {
|
|
76112
76960
|
const slug4 = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
76113
|
-
return slug4 || `site-${(0,
|
|
76961
|
+
return slug4 || `site-${(0, import_node_crypto50.randomBytes)(4).toString("hex")}`;
|
|
76114
76962
|
}
|
|
76115
76963
|
function normalizeObservedHostname(origin) {
|
|
76116
76964
|
if (!origin || origin === "null") return null;
|
|
@@ -76123,7 +76971,7 @@ function normalizeObservedHostname(origin) {
|
|
|
76123
76971
|
}
|
|
76124
76972
|
}
|
|
76125
76973
|
function publicPixelId() {
|
|
76126
|
-
return `px_${(0,
|
|
76974
|
+
return `px_${(0, import_node_crypto50.randomBytes)(18).toString("base64url")}`;
|
|
76127
76975
|
}
|
|
76128
76976
|
function mapRows(rows) {
|
|
76129
76977
|
return rows;
|
|
@@ -76155,7 +77003,7 @@ async function requireEditor(client2, siteId, userId) {
|
|
|
76155
77003
|
async function createAnalyticsSite(input) {
|
|
76156
77004
|
const db = getAnalyticsPool();
|
|
76157
77005
|
const client2 = await db.connect();
|
|
76158
|
-
const id = (0,
|
|
77006
|
+
const id = (0, import_node_crypto50.randomUUID)();
|
|
76159
77007
|
const baseSlug = normalizeSlug2(input.slug || input.name);
|
|
76160
77008
|
try {
|
|
76161
77009
|
await client2.query("BEGIN");
|
|
@@ -76171,7 +77019,7 @@ async function createAnalyticsSite(input) {
|
|
|
76171
77019
|
} catch (error) {
|
|
76172
77020
|
const code = error.code;
|
|
76173
77021
|
if (code !== "23505" || attempt === 2) throw error;
|
|
76174
|
-
slug4 = `${baseSlug}-${(0,
|
|
77022
|
+
slug4 = `${baseSlug}-${(0, import_node_crypto50.randomBytes)(2).toString("hex")}`;
|
|
76175
77023
|
}
|
|
76176
77024
|
}
|
|
76177
77025
|
await client2.query(
|
|
@@ -76386,7 +77234,7 @@ async function createAnalyticsPixel(input) {
|
|
|
76386
77234
|
VALUES ($1, $2, $3, $4, $5)
|
|
76387
77235
|
RETURNING id, site_id, public_id, name, environment, status, created_at::text, NULL::text AS last_event_at`,
|
|
76388
77236
|
[
|
|
76389
|
-
(0,
|
|
77237
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
76390
77238
|
input.siteId,
|
|
76391
77239
|
publicPixelId(),
|
|
76392
77240
|
input.name.trim(),
|
|
@@ -76463,7 +77311,7 @@ async function setAnalyticsPixelDomainState(input) {
|
|
|
76463
77311
|
SELECT $1, p.id, $4, $5 FROM analytics_pixels p WHERE p.id = $2 AND p.site_id = $3
|
|
76464
77312
|
ON CONFLICT(pixel_id, hostname) DO UPDATE SET state = EXCLUDED.state, updated_at = now()
|
|
76465
77313
|
RETURNING id`,
|
|
76466
|
-
[(0,
|
|
77314
|
+
[(0, import_node_crypto50.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
|
|
76467
77315
|
);
|
|
76468
77316
|
if (!result.rowCount)
|
|
76469
77317
|
throw new AnalyticsRepositoryError(
|
|
@@ -76513,7 +77361,7 @@ function sanitizeAnalyticsProperties(value) {
|
|
|
76513
77361
|
}
|
|
76514
77362
|
async function ingestAnalyticsEvents(input) {
|
|
76515
77363
|
const db = getAnalyticsPool();
|
|
76516
|
-
const requestId = `air_${(0,
|
|
77364
|
+
const requestId = `air_${(0, import_node_crypto50.randomUUID)()}`;
|
|
76517
77365
|
const hostname = normalizeObservedHostname(input.origin);
|
|
76518
77366
|
const pixelResult = await db.query(
|
|
76519
77367
|
`SELECT p.id, p.site_id, p.status,
|
|
@@ -76534,7 +77382,7 @@ async function ingestAnalyticsEvents(input) {
|
|
|
76534
77382
|
`INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
|
|
76535
77383
|
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
|
|
76536
77384
|
[
|
|
76537
|
-
(0,
|
|
77385
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
76538
77386
|
requestId,
|
|
76539
77387
|
pixel?.site_id ?? null,
|
|
76540
77388
|
pixel?.id ?? null,
|
|
@@ -76559,7 +77407,7 @@ async function ingestAnalyticsEvents(input) {
|
|
|
76559
77407
|
`INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
|
|
76560
77408
|
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
|
|
76561
77409
|
[
|
|
76562
|
-
(0,
|
|
77410
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
76563
77411
|
requestId,
|
|
76564
77412
|
pixel.site_id,
|
|
76565
77413
|
pixel.id,
|
|
@@ -76584,7 +77432,7 @@ async function ingestAnalyticsEvents(input) {
|
|
|
76584
77432
|
`INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, rejected_count, reason_codes)
|
|
76585
77433
|
VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,
|
|
76586
77434
|
[
|
|
76587
|
-
(0,
|
|
77435
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
76588
77436
|
requestId,
|
|
76589
77437
|
pixel.site_id,
|
|
76590
77438
|
pixel.id,
|
|
@@ -76608,7 +77456,7 @@ async function ingestAnalyticsEvents(input) {
|
|
|
76608
77456
|
ON CONFLICT(pixel_id, hostname) DO UPDATE
|
|
76609
77457
|
SET last_seen_at = now(), updated_at = now()
|
|
76610
77458
|
RETURNING state`,
|
|
76611
|
-
[(0,
|
|
77459
|
+
[(0, import_node_crypto50.randomUUID)(), pixel.id, hostname]
|
|
76612
77460
|
);
|
|
76613
77461
|
if (domainResult.rows[0]?.state !== "approved") {
|
|
76614
77462
|
await db.query(
|
|
@@ -76622,7 +77470,7 @@ async function ingestAnalyticsEvents(input) {
|
|
|
76622
77470
|
`INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
|
|
76623
77471
|
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
|
|
76624
77472
|
[
|
|
76625
|
-
(0,
|
|
77473
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
76626
77474
|
requestId,
|
|
76627
77475
|
pixel.site_id,
|
|
76628
77476
|
pixel.id,
|
|
@@ -76672,7 +77520,7 @@ async function ingestAnalyticsEvents(input) {
|
|
|
76672
77520
|
$23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33
|
|
76673
77521
|
) ON CONFLICT(site_id, event_id) DO NOTHING`,
|
|
76674
77522
|
[
|
|
76675
|
-
(0,
|
|
77523
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
76676
77524
|
event2.eventId,
|
|
76677
77525
|
pixel.site_id,
|
|
76678
77526
|
pixel.id,
|
|
@@ -76730,7 +77578,7 @@ async function ingestAnalyticsEvents(input) {
|
|
|
76730
77578
|
id, request_id, site_id, pixel_id, hostname, accepted_count, rejected_count, reason_codes
|
|
76731
77579
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,
|
|
76732
77580
|
[
|
|
76733
|
-
(0,
|
|
77581
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
76734
77582
|
requestId,
|
|
76735
77583
|
pixel.site_id,
|
|
76736
77584
|
pixel.id,
|
|
@@ -76762,7 +77610,7 @@ function normalizeGeographyCode(value, max) {
|
|
|
76762
77610
|
return normalized && /^[A-Z0-9-]+$/.test(normalized) ? normalized.slice(0, max) : null;
|
|
76763
77611
|
}
|
|
76764
77612
|
function pageFingerprint(scope, siteId, filters) {
|
|
76765
|
-
return (0,
|
|
77613
|
+
return (0, import_node_crypto50.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
|
|
76766
77614
|
}
|
|
76767
77615
|
function decodePageOffset(cursor, fingerprint2) {
|
|
76768
77616
|
if (!cursor) return 0;
|
|
@@ -76812,7 +77660,7 @@ async function createAnalyticsConversion(input) {
|
|
|
76812
77660
|
404
|
|
76813
77661
|
);
|
|
76814
77662
|
}
|
|
76815
|
-
const id = (0,
|
|
77663
|
+
const id = (0, import_node_crypto50.randomUUID)();
|
|
76816
77664
|
const resolvedPerson = input.sessionId ? await db.query(
|
|
76817
77665
|
`SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id
|
|
76818
77666
|
WHERE n.site_id = $1 AND n.kind = 'session_id' AND n.value_hmac = $2 ORDER BY e.confidence DESC LIMIT 1`,
|
|
@@ -77561,7 +78409,7 @@ async function createAnalyticsCampaignLink(input) {
|
|
|
77561
78409
|
404
|
|
77562
78410
|
);
|
|
77563
78411
|
}
|
|
77564
|
-
const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0,
|
|
78412
|
+
const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto50.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
|
|
77565
78413
|
try {
|
|
77566
78414
|
const result = await db.query(
|
|
77567
78415
|
`INSERT INTO analytics_campaign_links(
|
|
@@ -77570,7 +78418,7 @@ async function createAnalyticsCampaignLink(input) {
|
|
|
77570
78418
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
|
|
77571
78419
|
RETURNING *, 0::int AS click_count`,
|
|
77572
78420
|
[
|
|
77573
|
-
(0,
|
|
78421
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
77574
78422
|
input.siteId,
|
|
77575
78423
|
input.pixelId ?? null,
|
|
77576
78424
|
input.name.trim(),
|
|
@@ -77641,7 +78489,7 @@ async function resolveAnalyticsCampaignLink(shortCode, referrer) {
|
|
|
77641
78489
|
if (!row) return null;
|
|
77642
78490
|
await db.query(
|
|
77643
78491
|
`INSERT INTO analytics_campaign_clicks(id, link_id, referrer) VALUES ($1, $2, $3)`,
|
|
77644
|
-
[(0,
|
|
78492
|
+
[(0, import_node_crypto50.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
|
|
77645
78493
|
);
|
|
77646
78494
|
return buildTaggedCampaignUrl(row);
|
|
77647
78495
|
}
|
|
@@ -77659,14 +78507,14 @@ async function createAnalyticsForm(input) {
|
|
|
77659
78507
|
404
|
|
77660
78508
|
);
|
|
77661
78509
|
const baseSlug = normalizeSlug2(input.name);
|
|
77662
|
-
const slug4 = `${baseSlug}-${(0,
|
|
77663
|
-
const publicId = `form_${(0,
|
|
78510
|
+
const slug4 = `${baseSlug}-${(0, import_node_crypto50.randomBytes)(3).toString("hex")}`;
|
|
78511
|
+
const publicId = `form_${(0, import_node_crypto50.randomBytes)(18).toString("base64url")}`;
|
|
77664
78512
|
const result = await db.query(
|
|
77665
78513
|
`INSERT INTO analytics_forms(id, public_id, site_id, pixel_id, name, slug, fields, brand, submit_label, success_message, redirect_url, consent_text, status, created_by_user_id)
|
|
77666
78514
|
VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb,$9,$10,$11,$12,$13,$14)
|
|
77667
78515
|
RETURNING *, 0::int AS submission_count`,
|
|
77668
78516
|
[
|
|
77669
|
-
(0,
|
|
78517
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
77670
78518
|
publicId,
|
|
77671
78519
|
input.siteId,
|
|
77672
78520
|
input.pixelId,
|
|
@@ -77711,7 +78559,7 @@ async function getPublicAnalyticsForm(publicId) {
|
|
|
77711
78559
|
return result.rows[0] ?? null;
|
|
77712
78560
|
}
|
|
77713
78561
|
async function recordAnalyticsFormSubmission(input) {
|
|
77714
|
-
const id = (0,
|
|
78562
|
+
const id = (0, import_node_crypto50.randomUUID)();
|
|
77715
78563
|
await getAnalyticsPool().query(
|
|
77716
78564
|
`INSERT INTO analytics_form_submissions(id, form_id, site_id, pixel_id, visitor_id, session_id, crm_person_ref, source, medium, campaign, crm_delivery_status, click_ids)
|
|
77717
78565
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb)`,
|
|
@@ -77742,7 +78590,7 @@ function sanitizeClickIds(value) {
|
|
|
77742
78590
|
return output;
|
|
77743
78591
|
}
|
|
77744
78592
|
function identityHmac(kind, value) {
|
|
77745
|
-
return (0,
|
|
78593
|
+
return (0, import_node_crypto50.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
|
|
77746
78594
|
}
|
|
77747
78595
|
async function linkAnalyticsFormIdentity(input) {
|
|
77748
78596
|
const client2 = await getAnalyticsPool().connect();
|
|
@@ -77751,7 +78599,7 @@ async function linkAnalyticsFormIdentity(input) {
|
|
|
77751
78599
|
const person = await client2.query(
|
|
77752
78600
|
`INSERT INTO analytics_people(id, site_id, crm_person_ref) VALUES ($1,$2,$3)
|
|
77753
78601
|
ON CONFLICT(site_id, crm_person_ref) DO UPDATE SET last_seen_at = now() RETURNING id`,
|
|
77754
|
-
[(0,
|
|
78602
|
+
[(0, import_node_crypto50.randomUUID)(), input.siteId, input.crmPersonRef]
|
|
77755
78603
|
);
|
|
77756
78604
|
const personId = person.rows[0].id;
|
|
77757
78605
|
const signals = [];
|
|
@@ -77798,7 +78646,7 @@ async function linkAnalyticsFormIdentity(input) {
|
|
|
77798
78646
|
`INSERT INTO analytics_identity_nodes(id, site_id, kind, value_hmac) VALUES ($1,$2,$3,$4)
|
|
77799
78647
|
ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now() RETURNING id`,
|
|
77800
78648
|
[
|
|
77801
|
-
(0,
|
|
78649
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
77802
78650
|
input.siteId,
|
|
77803
78651
|
signal.kind,
|
|
77804
78652
|
identityHmac(signal.kind, signal.value)
|
|
@@ -77809,7 +78657,7 @@ async function linkAnalyticsFormIdentity(input) {
|
|
|
77809
78657
|
VALUES ($1,$2,$3,$4,$5,$6)
|
|
77810
78658
|
ON CONFLICT(person_id, identity_node_id, evidence_kind) DO UPDATE SET last_seen_at = now(), confidence = greatest(analytics_identity_edges.confidence, EXCLUDED.confidence)`,
|
|
77811
78659
|
[
|
|
77812
|
-
(0,
|
|
78660
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
77813
78661
|
input.siteId,
|
|
77814
78662
|
personId,
|
|
77815
78663
|
node.rows[0].id,
|
|
@@ -77907,7 +78755,7 @@ async function createAnalyticsCrmImport(input) {
|
|
|
77907
78755
|
const db = getAnalyticsPool();
|
|
77908
78756
|
await requireEditor(db, input.siteId, input.userId);
|
|
77909
78757
|
const client2 = await db.connect();
|
|
77910
|
-
const id = (0,
|
|
78758
|
+
const id = (0, import_node_crypto50.randomUUID)();
|
|
77911
78759
|
try {
|
|
77912
78760
|
await client2.query("BEGIN");
|
|
77913
78761
|
await client2.query(
|
|
@@ -77927,7 +78775,7 @@ async function createAnalyticsCrmImport(input) {
|
|
|
77927
78775
|
await client2.query(
|
|
77928
78776
|
`INSERT INTO analytics_crm_import_rows(id, import_id, crm_person_ref, payload_ciphertext)
|
|
77929
78777
|
VALUES ($1,$2,$3,$4) ON CONFLICT(import_id, crm_person_ref) DO NOTHING`,
|
|
77930
|
-
[(0,
|
|
78778
|
+
[(0, import_node_crypto50.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
|
|
77931
78779
|
);
|
|
77932
78780
|
}
|
|
77933
78781
|
await client2.query("COMMIT");
|
|
@@ -77966,7 +78814,7 @@ async function createAnalyticsActivationDestination(input) {
|
|
|
77966
78814
|
`INSERT INTO analytics_activation_destinations(id, site_id, platform, name, connection_ref, external_dataset_id, event_mapping, created_by_user_id)
|
|
77967
78815
|
VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8) RETURNING *`,
|
|
77968
78816
|
[
|
|
77969
|
-
(0,
|
|
78817
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
77970
78818
|
input.siteId,
|
|
77971
78819
|
input.platform,
|
|
77972
78820
|
input.name.trim(),
|
|
@@ -78018,7 +78866,7 @@ async function queueAnalyticsActivation(input) {
|
|
|
78018
78866
|
`INSERT INTO analytics_activation_jobs(id, destination_id, conversion_id, person_id, payload_ciphertext)
|
|
78019
78867
|
VALUES ($1,$2,$3,$4,$5) ON CONFLICT(destination_id, conversion_id) DO NOTHING`,
|
|
78020
78868
|
[
|
|
78021
|
-
(0,
|
|
78869
|
+
(0, import_node_crypto50.randomUUID)(),
|
|
78022
78870
|
destination.id,
|
|
78023
78871
|
input.conversionId,
|
|
78024
78872
|
input.personId ?? null,
|
|
@@ -78030,7 +78878,7 @@ async function queueAnalyticsActivation(input) {
|
|
|
78030
78878
|
return queued;
|
|
78031
78879
|
}
|
|
78032
78880
|
async function queueAnalyticsFormDelivery(input) {
|
|
78033
|
-
const id = (0,
|
|
78881
|
+
const id = (0, import_node_crypto50.randomUUID)();
|
|
78034
78882
|
await getAnalyticsPool().query(
|
|
78035
78883
|
`INSERT INTO analytics_form_delivery_jobs(id, submission_id, owner_user_id, payload_ciphertext, last_error_code)
|
|
78036
78884
|
VALUES ($1,$2,$3,$4,$5)`,
|
|
@@ -78157,7 +79005,7 @@ async function analyticsHealth(siteId, userId) {
|
|
|
78157
79005
|
}
|
|
78158
79006
|
async function refreshAnalyticsDailyRollups(input) {
|
|
78159
79007
|
const db = getAnalyticsPool();
|
|
78160
|
-
const runId = (0,
|
|
79008
|
+
const runId = (0, import_node_crypto50.randomUUID)();
|
|
78161
79009
|
await db.query(
|
|
78162
79010
|
`INSERT INTO analytics_rollup_runs(id, window_start, window_end, status) VALUES ($1, $2, $3, 'running')`,
|
|
78163
79011
|
[runId, input.start, input.end]
|
|
@@ -78354,7 +79202,7 @@ async function createAnalyticsExport(input) {
|
|
|
78354
79202
|
`Generated ${(/* @__PURE__ */ new Date()).toISOString()} from the governed ${input.report} report contract.`
|
|
78355
79203
|
].join("\n");
|
|
78356
79204
|
}
|
|
78357
|
-
const id = (0,
|
|
79205
|
+
const id = (0, import_node_crypto50.randomUUID)();
|
|
78358
79206
|
const inserted = await getAnalyticsPool().query(
|
|
78359
79207
|
`INSERT INTO analytics_exports(id, site_id, requested_by_user_id, idempotency_key, report, format, filters, content)
|
|
78360
79208
|
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
|
|
@@ -78387,11 +79235,11 @@ async function createAnalyticsExport(input) {
|
|
|
78387
79235
|
}
|
|
78388
79236
|
return describeArtifact(replay.rows[0]);
|
|
78389
79237
|
}
|
|
78390
|
-
var
|
|
79238
|
+
var import_node_crypto50, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
|
|
78391
79239
|
var init_analytics_repository = __esm({
|
|
78392
79240
|
"src/api/analytics-repository.ts"() {
|
|
78393
79241
|
"use strict";
|
|
78394
|
-
|
|
79242
|
+
import_node_crypto50 = require("crypto");
|
|
78395
79243
|
import_pg = require("pg");
|
|
78396
79244
|
init_session();
|
|
78397
79245
|
init_analytics_attribution();
|
|
@@ -78630,8 +79478,8 @@ function dashboardCallbackUrl() {
|
|
|
78630
79478
|
}
|
|
78631
79479
|
async function createThorbitConnectUrl(userId) {
|
|
78632
79480
|
if (!bridgeConfigured()) throw new Error("Thorbit X-Ray account bridge is not configured");
|
|
78633
|
-
const state = (0,
|
|
78634
|
-
const stateHash = (0,
|
|
79481
|
+
const state = (0, import_node_crypto51.randomBytes)(32).toString("base64url");
|
|
79482
|
+
const stateHash = (0, import_node_crypto51.createHash)("sha256").update(state).digest("hex");
|
|
78635
79483
|
await getAnalyticsPool().query(
|
|
78636
79484
|
`INSERT INTO analytics_thorbit_connect_states(state_hash,user_id,expires_at)
|
|
78637
79485
|
VALUES ($1,$2,now()+interval '10 minutes')
|
|
@@ -78646,9 +79494,9 @@ async function createThorbitConnectUrl(userId) {
|
|
|
78646
79494
|
function verifyThorbitAssertion(token6) {
|
|
78647
79495
|
const [encoded, signature] = token6.split(".");
|
|
78648
79496
|
if (!encoded || !signature || !bridgeConfigured()) throw new Error("Invalid Thorbit assertion");
|
|
78649
|
-
const expected = (0,
|
|
79497
|
+
const expected = (0, import_node_crypto51.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
|
|
78650
79498
|
const actual = Buffer.from(signature, "base64url");
|
|
78651
|
-
if (actual.length !== expected.length || !(0,
|
|
79499
|
+
if (actual.length !== expected.length || !(0, import_node_crypto51.timingSafeEqual)(actual, expected)) {
|
|
78652
79500
|
throw new Error("Invalid Thorbit assertion signature");
|
|
78653
79501
|
}
|
|
78654
79502
|
const parsed = AssertionSchema.safeParse(
|
|
@@ -78660,7 +79508,7 @@ function verifyThorbitAssertion(token6) {
|
|
|
78660
79508
|
return parsed.data.entitlement;
|
|
78661
79509
|
}
|
|
78662
79510
|
async function consumeThorbitConnectCallback(input) {
|
|
78663
|
-
const stateHash = (0,
|
|
79511
|
+
const stateHash = (0, import_node_crypto51.createHash)("sha256").update(input.state).digest("hex");
|
|
78664
79512
|
const client2 = await getAnalyticsPool().connect();
|
|
78665
79513
|
try {
|
|
78666
79514
|
await client2.query("BEGIN");
|
|
@@ -78700,11 +79548,11 @@ async function disconnectAnalyticsEntitlement(userId) {
|
|
|
78700
79548
|
[userId]
|
|
78701
79549
|
);
|
|
78702
79550
|
}
|
|
78703
|
-
var
|
|
79551
|
+
var import_node_crypto51, import_zod55, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
|
|
78704
79552
|
var init_analytics_entitlement = __esm({
|
|
78705
79553
|
"src/api/analytics-entitlement.ts"() {
|
|
78706
79554
|
"use strict";
|
|
78707
|
-
|
|
79555
|
+
import_node_crypto51 = require("crypto");
|
|
78708
79556
|
import_zod55 = require("zod");
|
|
78709
79557
|
init_analytics_repository();
|
|
78710
79558
|
THORBIT_PRODUCT_URL = "https://thorbit.ai";
|
|
@@ -78844,14 +79692,14 @@ function renderPublicForm(form, placementUrl) {
|
|
|
78844
79692
|
const brand = form.brand;
|
|
78845
79693
|
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>:root{font-family:ui-sans-serif,system-ui;color:${escapeHtml8(brand.textColor)};background:${escapeHtml8(brand.backgroundColor)}}*{box-sizing:border-box}body{margin:0;padding:18px}form{display:grid;gap:14px}label{display:grid;gap:6px;font-size:13px;font-weight:650}.check{grid-template-columns:auto 1fr;align-items:center}.check span{grid-column:2}.check input{grid-column:1;grid-row:1}input,textarea,select{width:100%;min-height:44px;padding:10px 12px;border:1px solid #ccd3df;border-radius:${brand.radius}px;background:white;color:inherit;font:inherit}textarea{min-height:110px;resize:vertical}button{min-height:46px;border:0;border-radius:${brand.radius}px;color:white;background:${escapeHtml8(brand.primaryColor)};font:inherit;font-weight:750;cursor:pointer}.consent{margin:0;color:#667085;font-size:11px;line-height:1.45}.notice{display:none;padding:12px;border-radius:${brand.radius}px;background:#edf8f2;color:#17613c}.hp{position:absolute!important;left:-9999px!important}</style></head><body><form id="mcp-form">${fields}<label class="hp">Website<input name="website" autocomplete="off" tabindex="-1"></label>${form.consent_text ? `<p class="consent">${escapeHtml8(form.consent_text)}</p>` : ""}<button>${escapeHtml8(form.submit_label)}</button><div class="notice" role="status"></div></form><script>(()=>{const form=document.querySelector('#mcp-form'),notice=form.querySelector('.notice'),q=new URLSearchParams(location.search);form.addEventListener('submit',async event=>{event.preventDefault();const button=form.querySelector('button');button.disabled=true;const raw=Object.fromEntries(new FormData(form).entries()),website=String(raw.website||''),clickIds={};delete raw.website;for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}try{const response=await fetch('/analytics/forms/${escapeHtml8(form.public_id)}/submissions',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({data:raw,website,placementUrl:${JSON.stringify(placementUrl)},visitorId:q.get('visitor')||undefined,sessionId:q.get('session')||undefined,source:q.get('source')||undefined,medium:q.get('medium')||undefined,campaign:q.get('campaign')||undefined,clickIds})});if(!response.ok)throw new Error('submit');notice.textContent=${JSON.stringify(form.success_message)};notice.style.display='block';form.reset();${form.redirect_url ? `setTimeout(()=>{top.location.href=${JSON.stringify(form.redirect_url)}},700);` : ""}}catch{notice.textContent='Your response could not be submitted. Please try again.';notice.style.display='block'}finally{button.disabled=false}})})()</script></body></html>`;
|
|
78846
79694
|
}
|
|
78847
|
-
var import_hono34, import_factory6, import_zod56,
|
|
79695
|
+
var import_hono34, import_factory6, import_zod56, import_node_crypto52, import_papaparse6, analyticsApp, auth3, entitlementGuard, ThorbitApiKeySchema, ThorbitCallbackSchema, SiteInputSchema, PixelInputSchema, PixelUpdateSchema, EventSchema, IngestionSchema, ConversionSchema, ExportSchema2, CampaignLinkSchema, FormFieldSchema, FormInputSchema, FormSubmissionSchema, CrmImportSchema, ActivationDestinationSchema, BusinessModelSchema, AdSpendSchema;
|
|
78848
79696
|
var init_analytics_routes = __esm({
|
|
78849
79697
|
"src/api/analytics-routes.ts"() {
|
|
78850
79698
|
"use strict";
|
|
78851
79699
|
import_hono34 = require("hono");
|
|
78852
79700
|
import_factory6 = require("hono/factory");
|
|
78853
79701
|
import_zod56 = require("zod");
|
|
78854
|
-
|
|
79702
|
+
import_node_crypto52 = require("crypto");
|
|
78855
79703
|
import_papaparse6 = __toESM(require("papaparse"), 1);
|
|
78856
79704
|
init_api_auth();
|
|
78857
79705
|
init_db();
|
|
@@ -79182,7 +80030,7 @@ var init_analytics_routes = __esm({
|
|
|
79182
80030
|
const lastName = typeof data.last_name === "string" ? data.last_name.trim() : "";
|
|
79183
80031
|
const fullName = [firstName, lastName].filter(Boolean).join(" ") || (typeof data.name === "string" ? data.name.trim() : "") || email || "Website lead";
|
|
79184
80032
|
const identitySeed = email || `${fullName}:${Date.now()}`;
|
|
79185
|
-
const suffix2 = (0,
|
|
80033
|
+
const suffix2 = (0, import_node_crypto52.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
|
|
79186
80034
|
const path6 = `Leads/person-${suffix2}`;
|
|
79187
80035
|
const { key: memoryKey, error: memoryKeyError } = await getOrCreateUserMemoryKey(user);
|
|
79188
80036
|
const existing = memoryKey ? await memoryCall("getTool", { vault: "People", path: path6 }, memoryKey) : { ok: false, error: memoryKeyError || "memory_credential_unavailable" };
|
|
@@ -79281,9 +80129,9 @@ Submitted the published form.
|
|
|
79281
80129
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
79282
80130
|
});
|
|
79283
80131
|
const match = {
|
|
79284
|
-
...email ? { emailSha256: (0,
|
|
80132
|
+
...email ? { emailSha256: (0, import_node_crypto52.createHash)("sha256").update(email).digest("hex") } : {},
|
|
79285
80133
|
...phone ? {
|
|
79286
|
-
phoneSha256: (0,
|
|
80134
|
+
phoneSha256: (0, import_node_crypto52.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
|
|
79287
80135
|
} : {}
|
|
79288
80136
|
};
|
|
79289
80137
|
const activationQueued = await queueAnalyticsActivation({
|
|
@@ -79711,7 +80559,7 @@ Submitted the published form.
|
|
|
79711
80559
|
rejectedCount += 1;
|
|
79712
80560
|
continue;
|
|
79713
80561
|
}
|
|
79714
|
-
const digest2 = (0,
|
|
80562
|
+
const digest2 = (0, import_node_crypto52.createHash)("sha256").update(
|
|
79715
80563
|
`${parsed.data.sourceSystem}:${externalId || email || phone || `${fullName}:${index}`}`
|
|
79716
80564
|
).digest("hex").slice(0, 16);
|
|
79717
80565
|
const path6 = `Leads/person-${digest2}`;
|
|
@@ -80169,19 +81017,19 @@ var init_analytics_delivery = __esm({
|
|
|
80169
81017
|
|
|
80170
81018
|
// src/api/scheduled-artifact-owner.ts
|
|
80171
81019
|
function scheduledArtifactOwnerIdForApiKey(apiKey) {
|
|
80172
|
-
return (0,
|
|
81020
|
+
return (0, import_node_crypto53.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
|
|
80173
81021
|
}
|
|
80174
|
-
var
|
|
81022
|
+
var import_node_crypto53;
|
|
80175
81023
|
var init_scheduled_artifact_owner = __esm({
|
|
80176
81024
|
"src/api/scheduled-artifact-owner.ts"() {
|
|
80177
81025
|
"use strict";
|
|
80178
|
-
|
|
81026
|
+
import_node_crypto53 = require("crypto");
|
|
80179
81027
|
}
|
|
80180
81028
|
});
|
|
80181
81029
|
|
|
80182
81030
|
// src/api/scheduled-run-view-links.ts
|
|
80183
81031
|
async function ensureScheduledRunViewLinksSchema() {
|
|
80184
|
-
if (
|
|
81032
|
+
if (schemaReady3) return;
|
|
80185
81033
|
const db = getDb();
|
|
80186
81034
|
await db.execute(`
|
|
80187
81035
|
CREATE TABLE IF NOT EXISTS scheduled_run_view_links (
|
|
@@ -80229,10 +81077,10 @@ async function ensureScheduledRunViewLinksSchema() {
|
|
|
80229
81077
|
CREATE INDEX IF NOT EXISTS scheduled_run_view_links_expiry
|
|
80230
81078
|
ON scheduled_run_view_links(expires_at, revoked_at)
|
|
80231
81079
|
`);
|
|
80232
|
-
|
|
81080
|
+
schemaReady3 = true;
|
|
80233
81081
|
}
|
|
80234
81082
|
function tokenHash2(token6) {
|
|
80235
|
-
return (0,
|
|
81083
|
+
return (0, import_node_crypto54.createHash)("sha256").update(token6).digest("hex");
|
|
80236
81084
|
}
|
|
80237
81085
|
function mapRow(row) {
|
|
80238
81086
|
return {
|
|
@@ -80252,9 +81100,9 @@ function mapRow(row) {
|
|
|
80252
81100
|
async function createScheduledRunViewLink(input) {
|
|
80253
81101
|
await ensureScheduledRunViewLinksSchema();
|
|
80254
81102
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
80255
|
-
const token6 = (0,
|
|
81103
|
+
const token6 = (0, import_node_crypto54.randomBytes)(32).toString("base64url");
|
|
80256
81104
|
const record = {
|
|
80257
|
-
shareId: (0,
|
|
81105
|
+
shareId: (0, import_node_crypto54.randomUUID)(),
|
|
80258
81106
|
ownerId: input.ownerId,
|
|
80259
81107
|
runId: input.runId,
|
|
80260
81108
|
artifactId: input.artifactId,
|
|
@@ -80317,13 +81165,13 @@ async function revokeScheduledRunViewLink(ownerId2, runId, shareId, now = /* @__
|
|
|
80317
81165
|
});
|
|
80318
81166
|
return result.rowsAffected > 0;
|
|
80319
81167
|
}
|
|
80320
|
-
var
|
|
81168
|
+
var import_node_crypto54, schemaReady3;
|
|
80321
81169
|
var init_scheduled_run_view_links = __esm({
|
|
80322
81170
|
"src/api/scheduled-run-view-links.ts"() {
|
|
80323
81171
|
"use strict";
|
|
80324
|
-
|
|
81172
|
+
import_node_crypto54 = require("crypto");
|
|
80325
81173
|
init_db();
|
|
80326
|
-
|
|
81174
|
+
schemaReady3 = false;
|
|
80327
81175
|
}
|
|
80328
81176
|
});
|
|
80329
81177
|
|
|
@@ -80387,15 +81235,15 @@ ${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id=
|
|
|
80387
81235
|
html,
|
|
80388
81236
|
filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
|
|
80389
81237
|
bytes,
|
|
80390
|
-
sha256: (0,
|
|
81238
|
+
sha256: (0, import_node_crypto55.createHash)("sha256").update(html).digest("hex"),
|
|
80391
81239
|
generatedAt: generatedAtIso
|
|
80392
81240
|
};
|
|
80393
81241
|
}
|
|
80394
|
-
var
|
|
81242
|
+
var import_node_crypto55;
|
|
80395
81243
|
var init_render2 = __esm({
|
|
80396
81244
|
"src/personal-authority/render.ts"() {
|
|
80397
81245
|
"use strict";
|
|
80398
|
-
|
|
81246
|
+
import_node_crypto55 = require("crypto");
|
|
80399
81247
|
}
|
|
80400
81248
|
});
|
|
80401
81249
|
|
|
@@ -80498,15 +81346,15 @@ ${section2(flags.finalCta && Boolean(finalCta), `<section class="section cta" id
|
|
|
80498
81346
|
html,
|
|
80499
81347
|
filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
|
|
80500
81348
|
bytes,
|
|
80501
|
-
sha256: (0,
|
|
81349
|
+
sha256: (0, import_node_crypto56.createHash)("sha256").update(html).digest("hex"),
|
|
80502
81350
|
generatedAt: generatedAtIso
|
|
80503
81351
|
};
|
|
80504
81352
|
}
|
|
80505
|
-
var
|
|
81353
|
+
var import_node_crypto56, FONT_STACKS;
|
|
80506
81354
|
var init_render_v2 = __esm({
|
|
80507
81355
|
"src/personal-authority/render-v2.ts"() {
|
|
80508
81356
|
"use strict";
|
|
80509
|
-
|
|
81357
|
+
import_node_crypto56 = require("crypto");
|
|
80510
81358
|
init_contracts();
|
|
80511
81359
|
FONT_STACKS = Object.freeze({
|
|
80512
81360
|
"editorial-serif": "Iowan Old Style, Baskerville, Times New Roman, serif",
|
|
@@ -80622,15 +81470,15 @@ body{overflow-x:hidden}.lead-story__body,.lead-support__item,.story-card__body{m
|
|
|
80622
81470
|
@media(max-width:620px){.desk-section__grid--panorama,.desk-section__grid--mosaic,.desk-section__grid--tiles{grid-template-columns:1fr}.story-card--panorama{display:block}.story-card--panorama h3,.story-card--mosaic-lead h3{font-size:34px}.story-card--mosaic-lead{grid-column:auto;grid-row:auto}.desk-section__grid--tiles>.story-card:nth-child(3n+2){margin-top:0}}
|
|
80623
81471
|
</style></head><body data-theme="${escapeHtml11(config.theme)}" id="top" data-scheduled-renderer="newsroom_publisher_v1">${ticker}${masthead}${navigation}<main>${leadGrid}${latest}${sections}${newsletter}${pressRoom}${trustCenter}</main>${trustFooter}</body></html>`;
|
|
80624
81472
|
const bytes = Buffer.byteLength(html);
|
|
80625
|
-
const sha2565 = (0,
|
|
81473
|
+
const sha2565 = (0, import_node_crypto57.createHash)("sha256").update(html).digest("hex");
|
|
80626
81474
|
const filename2 = `${brand.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "newsroom"}-news-site.html`;
|
|
80627
81475
|
return { html, filename: filename2, bytes, sha256: sha2565, generatedAt: generatedAtIso };
|
|
80628
81476
|
}
|
|
80629
|
-
var
|
|
81477
|
+
var import_node_crypto57;
|
|
80630
81478
|
var init_render3 = __esm({
|
|
80631
81479
|
"src/newsroom-publisher/render.ts"() {
|
|
80632
81480
|
"use strict";
|
|
80633
|
-
|
|
81481
|
+
import_node_crypto57 = require("crypto");
|
|
80634
81482
|
}
|
|
80635
81483
|
});
|
|
80636
81484
|
|
|
@@ -80722,7 +81570,7 @@ function renderBlogArticleV1(input, config, generatedAt) {
|
|
|
80722
81570
|
:root{--ink:#10213d;--ink2:#1c355a;--blue:#2563eb;--blue2:#1749b6;--mint:#45d5aa;--paper:#f7f5f0;--line:#dce4ef;--card:#fff;--body:#475569;--serif:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif;--sans:Inter,Avenir Next,ui-sans-serif,system-ui,sans-serif}body[data-theme="slate"]{--ink:#1e293b;--ink2:#334155;--blue:#475569;--blue2:#1e293b;--mint:#94a3b8;--paper:#f8fafc}body[data-theme="forest"]{--ink:#12352e;--ink2:#23584c;--blue:#147d64;--blue2:#0c5b48;--mint:#79d8bd;--paper:#f4f8f3}*{box-sizing:border-box}html{scroll-behavior:smooth;background:var(--paper)}body{margin:0;color:#252a33;background:var(--paper);font-family:var(--sans);line-height:1.6}a{color:inherit}button{font:inherit}button:focus-visible,a:focus-visible{outline:3px solid var(--mint);outline-offset:3px}.progress{position:fixed;z-index:50;top:0;left:0;height:4px;width:0;background:var(--mint)}.hero{position:relative;overflow:hidden;color:#fff;background:var(--ink)}.hero:after{position:absolute;right:-110px;top:-150px;width:420px;height:420px;border:1px solid #ffffff1a;border-radius:50%;content:""}.hero-inner{position:relative;z-index:1;max-width:1240px;margin:auto;padding:58px 42px 126px}.breadcrumb{display:flex;gap:8px;margin-bottom:34px;color:#dbeafecc;font-size:14px}.eyebrow{display:block;color:var(--blue);font-size:12px;font-weight:800;letter-spacing:.16em;text-transform:uppercase}.hero .eyebrow{color:var(--mint)}h1,h2,h3,p{margin-top:0}.hero h1{max-width:1000px;margin:14px 0 24px;font:700 clamp(44px,6vw,78px)/.98 var(--serif);letter-spacing:-.035em}.hero .dek{max-width:780px;color:#dbeafe;font-size:20px}.contributors{margin-top:30px;color:#eff6ff;font-size:14px}.contributors a{font-weight:800}.meta{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:#dbeafecc}.layout{position:relative;z-index:2;display:grid;grid-template-columns:minmax(0,760px) 290px;gap:64px;max-width:1240px;margin:-78px auto 0;padding:0 42px 80px}.main{min-width:0}.takeaways{padding:38px 44px;border-radius:24px;background:#fff;box-shadow:0 24px 70px #10213d24}.takeaways h2{margin:5px 0 20px;font:700 32px/1.1 var(--serif);color:var(--ink)}.takeaways ul{display:grid;gap:16px;margin:0;padding:0;list-style:none}.takeaways li{position:relative;padding-left:26px;color:var(--body);font-size:17px}.takeaways li:before{position:absolute;left:2px;top:.7em;width:8px;height:8px;border-radius:50%;background:var(--blue);content:""}.intro{padding:40px 0 4px}.intro p,.article-section>p{margin-bottom:22px;color:var(--body);font:400 18px/1.75 var(--sans)}.article-section{scroll-margin-top:28px;padding-top:45px}.article-section h2,.faq h2{margin:6px 0 20px;color:var(--ink);font:700 38px/1.08 var(--serif);letter-spacing:-.02em}.article-section ul{display:grid;gap:10px;margin:22px 0;padding-left:24px;color:var(--body);font-size:17px}.article-section li::marker{color:var(--blue)}.callout{margin:34px 0;padding:24px 28px;border-left:4px solid var(--blue);border-radius:0 16px 16px 0;background:#fff}.callout span{color:var(--blue);font-size:11px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.callout h3{margin:5px 0 8px;color:var(--ink);font:700 24px/1.15 var(--serif)}.callout p{margin:0;color:var(--body)}.rail{padding-top:0}.sidebar-media{overflow:hidden;margin:0 0 18px;border:1px solid #dbeafe;border-radius:16px;background:#fff}.sidebar-media img,.sidebar-media svg{display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.sidebar-media figcaption{padding:10px 13px;color:#64748b;font-size:11px}.disclosure{margin-bottom:28px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe}.disclosure button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:14px;padding:15px 0;border:0;color:#64748b;background:transparent;text-align:left;font-size:12px;cursor:pointer}.disclosure button b{color:var(--blue);font-size:18px;transition:transform .2s}.disclosure button[aria-expanded="true"] b{transform:rotate(180deg)}.disclosure p{padding:0 0 16px;margin:0;color:#64748b;font-size:12px}.toc{position:sticky;top:30px;padding:22px;border:1px solid var(--line);border-radius:16px;background:#fff;box-shadow:0 2px 5px #10213d12}.toc header{display:flex;align-items:center;justify-content:space-between}.toc header strong{color:var(--ink);font:700 21px/1 var(--serif)}.toc header button{width:34px;height:34px;border:0;border-radius:50%;color:var(--blue);background:transparent;cursor:pointer}.toc nav{display:grid;margin-top:14px}.toc nav a{display:flex;align-items:flex-start;gap:11px;padding:8px 0;color:#64748b;text-decoration:none;font-size:13px;line-height:1.35}.toc nav i{width:8px;height:8px;margin-top:5px;border:2px solid #bfdbfe;border-radius:50%}.toc nav a[aria-current="location"]{color:var(--ink);font-weight:800}.toc nav a[aria-current="location"] i{border-color:var(--blue);background:var(--blue)}.back-top{width:100%;margin-top:18px;padding:16px 0 0;border:0;border-top:1px solid var(--line);color:var(--blue);background:transparent;text-align:right;font-size:13px;font-weight:800;cursor:pointer}.faq{scroll-margin-top:28px;padding-top:64px}.faq>div{border-top:1px solid var(--line);border-bottom:1px solid var(--line)}.faq article+article{border-top:1px solid var(--line)}.faq h3{margin:0}.faq h3 button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:18px;padding:19px 0;border:0;color:var(--ink);background:transparent;text-align:left;font-weight:800;cursor:pointer}.faq h3 b{color:var(--blue);font-size:22px}.faq article>p{padding:0 0 20px;margin:0;color:var(--body);font-size:16px}.author-card{margin-top:64px;padding:32px 36px;border-radius:18px;background:#fff}.article-actions{display:flex;flex-wrap:wrap;justify-content:space-between;gap:14px}.article-actions button{display:inline-flex;min-height:46px;align-items:center;justify-content:center;gap:8px;padding:8px 18px;border:2px solid var(--blue);border-radius:999px;color:var(--ink);background:#fff;font-weight:800;cursor:pointer}.article-actions button:hover{color:#fff;background:var(--blue)}.action-status{min-height:20px;margin:8px 0 0;color:var(--blue);text-align:right;font-size:13px;font-weight:700}.author-grid{display:grid;grid-template-columns:auto 1fr auto;gap:22px;align-items:center;margin-top:24px}.author-portrait{width:88px;height:88px;border-radius:50%;object-fit:cover}.author-initials{display:grid;place-items:center;color:var(--ink);background:#fecdd3;font-size:22px;font-weight:900}.author-grid span{color:#64748b;font-size:13px}.author-grid h2{margin:2px 0;color:var(--ink);font:700 27px/1 var(--serif)}.author-grid h2 a{text-decoration:none}.author-grid p{margin:8px 0;color:#64748b;font-size:14px}.author-grid nav{display:flex;flex-wrap:wrap;gap:14px}.author-grid nav a{color:#64748b;font-size:12px}.author-more{display:inline-flex;min-height:48px;align-items:center;justify-content:center;padding:10px 24px;border:2px solid var(--blue);color:var(--blue);text-align:center;text-decoration:none;font-size:13px;font-weight:800}.author-bio{margin:24px 0 0!important;padding-top:20px;border-top:1px solid var(--line);color:var(--body);font-size:16px}dialog{width:min(720px,calc(100% - 30px));max-height:calc(100dvh - 30px);padding:0;border:0;border-radius:18px;color:var(--ink);background:#fff;box-shadow:0 24px 70px #10213d38}dialog::backdrop{background:#0f172a7a;backdrop-filter:blur(2px)}dialog>div{position:relative;padding:30px}dialog h2{margin:0 50px 2px 0;font:700 30px/1.1 var(--serif)}dialog>div>p{color:#64748b}.dialog-close{position:absolute;top:13px;right:13px;width:44px;height:44px;border:0;border-radius:50%;color:var(--blue);background:transparent;font-size:29px;cursor:pointer}dialog section{border-top:1px solid var(--line)}dialog section button{display:block;width:100%;padding:17px 5px;border:0;border-bottom:1px solid var(--line);color:var(--body);background:#fff;text-align:left;line-height:1.65;cursor:pointer}.citation-status{min-height:22px;margin:14px 0 0!important;color:var(--blue)!important;font-size:13px;font-weight:800}.generated{max-width:1156px;margin:-52px auto 50px;padding:0 42px;color:#94a3b8;font-size:11px}@media(max-width:900px){.layout{display:block}.rail{display:none}.hero-inner{padding-bottom:116px}}@media(max-width:620px){.hero-inner{padding:36px 20px 104px}.hero h1{font-size:46px}.hero .dek{font-size:17px}.layout{margin-top:-70px;padding:0 16px 54px}.takeaways{padding:28px 24px}.article-section h2,.faq h2{font-size:32px}.author-card{padding:24px}.article-actions{display:grid;justify-content:start}.author-grid{grid-template-columns:1fr;gap:12px}.author-more{width:100%}dialog>div{padding:24px}.generated{padding:0 20px}}
|
|
80723
81571
|
</style></head><body data-theme="${escapeHtml12(config.theme)}" data-scheduled-renderer="blog_article_v1"><div class="progress" aria-hidden="true"></div><header class="hero"><div class="hero-inner"><nav class="breadcrumb" aria-label="Breadcrumb"><a href="${safeHref4(input.canonicalUrl)}">${escapeHtml12(input.category)}</a><span>/</span><span>Article</span></nav><span class="eyebrow">${escapeHtml12(input.eyebrow)}</span><h1>${escapeHtml12(input.title)}</h1>${input.dek ? `<p class="dek">${escapeHtml12(input.dek)}</p>` : ""}<div class="contributors">${contributorText}<div class="meta"><time datetime="${escapeHtml12(articleDate)}">Updated ${formatDate2(articleDate)}</time><span>\u2022</span><span>${input.readTimeMinutes} min read</span>${input.reviewer ? "<span>\u2022</span><strong>Expert reviewed</strong>" : ""}</div></div></div></header><main class="layout"><article class="main"><section class="takeaways"><span class="eyebrow">The short answer</span><h2>Key takeaways</h2><ul>${input.keyTakeaways.map((item) => `<li>${escapeHtml12(item)}</li>`).join("")}</ul></section><section class="intro">${input.introduction.map((paragraph) => `<p>${escapeHtml12(paragraph)}</p>`).join("")}</section>${sections}${faq}${author}</article><div class="rail">${media}${disclosure}${toc}</div></main>${config.showGeneratedAt ? `<p class="generated">Rendered ${formatDate2(generatedAtIso)} by MCP Scraper.</p>` : ""}${citationDialog}<script>(()=>{const citations=${jsonForScript(citationValues)};const canonical=${jsonForScript(input.canonicalUrl)};const title=${jsonForScript(input.title)};const progress=document.querySelector('.progress');const tocLinks=[...document.querySelectorAll('.toc nav a')];const targets=tocLinks.map(link=>document.querySelector(link.hash)).filter(Boolean);const update=()=>{const max=document.documentElement.scrollHeight-innerHeight;if(progress)progress.style.width=(max>0?scrollY/max*100:0)+'%';let active=targets[0]?.id;for(const target of targets){if(target.getBoundingClientRect().top<=innerHeight*.55)active=target.id}tocLinks.forEach(link=>link.toggleAttribute('aria-current',link.hash==='#'+active));tocLinks.forEach(link=>{if(link.hash==='#'+active)link.setAttribute('aria-current','location');else link.removeAttribute('aria-current')})};addEventListener('scroll',update,{passive:true});addEventListener('resize',update);update();document.querySelectorAll('.faq h3 button').forEach(button=>button.addEventListener('click',()=>{const panel=document.getElementById(button.getAttribute('aria-controls'));const open=button.getAttribute('aria-expanded')==='true';button.setAttribute('aria-expanded',String(!open));panel.hidden=open;button.querySelector('b').textContent=open?'+':'\u2212'}));const disclosure=document.querySelector('.disclosure button');disclosure?.addEventListener('click',()=>{const panel=document.getElementById(disclosure.getAttribute('aria-controls'));const open=disclosure.getAttribute('aria-expanded')==='true';disclosure.setAttribute('aria-expanded',String(!open));panel.hidden=open});const tocToggle=document.querySelector('.toc header button');tocToggle?.addEventListener('click',()=>{const nav=document.getElementById('toc-links');const open=tocToggle.getAttribute('aria-expanded')==='true';tocToggle.setAttribute('aria-expanded',String(!open));nav.hidden=open;tocToggle.textContent=open?'\u2304':'\u2303'});document.querySelector('.back-top')?.addEventListener('click',()=>scrollTo({top:0,behavior:'smooth'}));const copy=async text=>{try{await navigator.clipboard.writeText(text)}catch{const area=document.createElement('textarea');area.value=text;area.style.position='fixed';area.style.opacity='0';document.body.append(area);area.select();document.execCommand('copy');area.remove()}};const dialog=document.getElementById('citation-dialog');document.querySelector('[data-cite]')?.addEventListener('click',()=>dialog.showModal());document.querySelector('.dialog-close')?.addEventListener('click',()=>dialog.close());dialog?.addEventListener('click',event=>{if(event.target===dialog)dialog.close()});document.querySelectorAll('[data-citation]').forEach(button=>button.addEventListener('click',async()=>{const style=button.dataset.citation;await copy(citations[style]);document.querySelector('.citation-status').textContent=style+' citation copied to clipboard.'}));document.querySelector('[data-share]')?.addEventListener('click',async()=>{const status=document.querySelector('.action-status');if(navigator.share){try{await navigator.share({title,url:canonical});status.textContent='Sharing options opened.';return}catch(error){if(error.name==='AbortError')return}}await copy(canonical);status.textContent='Article link copied to clipboard.'})})()</script></body></html>`;
|
|
80724
81572
|
const bytes = Buffer.byteLength(html);
|
|
80725
|
-
const sha2565 = (0,
|
|
81573
|
+
const sha2565 = (0, import_node_crypto58.createHash)("sha256").update(html).digest("hex");
|
|
80726
81574
|
return {
|
|
80727
81575
|
html,
|
|
80728
81576
|
filename: "blog-article.html",
|
|
@@ -80732,11 +81580,11 @@ function renderBlogArticleV1(input, config, generatedAt) {
|
|
|
80732
81580
|
generatedAt: generatedAtIso
|
|
80733
81581
|
};
|
|
80734
81582
|
}
|
|
80735
|
-
var
|
|
81583
|
+
var import_node_crypto58;
|
|
80736
81584
|
var init_render4 = __esm({
|
|
80737
81585
|
"src/blog-article/render.ts"() {
|
|
80738
81586
|
"use strict";
|
|
80739
|
-
|
|
81587
|
+
import_node_crypto58 = require("crypto");
|
|
80740
81588
|
}
|
|
80741
81589
|
});
|
|
80742
81590
|
|
|
@@ -81067,7 +81915,7 @@ function policy6() {
|
|
|
81067
81915
|
};
|
|
81068
81916
|
}
|
|
81069
81917
|
function runStorageSegment(runId) {
|
|
81070
|
-
return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0,
|
|
81918
|
+
return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0, import_node_crypto59.createHash)("sha256").update(runId).digest("hex").slice(0, 32)}`;
|
|
81071
81919
|
}
|
|
81072
81920
|
async function createScheduledRunArtifact(args) {
|
|
81073
81921
|
if (args.rendered.bytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) {
|
|
@@ -81108,12 +81956,12 @@ async function readScheduledRunArtifact(args) {
|
|
|
81108
81956
|
if (!window2 || window2.nextOffset !== null || window2.totalBytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) return null;
|
|
81109
81957
|
return window2.text;
|
|
81110
81958
|
}
|
|
81111
|
-
var
|
|
81959
|
+
var import_node_crypto59, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
|
|
81112
81960
|
var init_scheduled_run_artifact_store = __esm({
|
|
81113
81961
|
"src/scheduled-artifacts/scheduled-run-artifact-store.ts"() {
|
|
81114
81962
|
"use strict";
|
|
81115
81963
|
init_private_artifacts();
|
|
81116
|
-
|
|
81964
|
+
import_node_crypto59 = require("crypto");
|
|
81117
81965
|
SCHEDULED_RUN_ARTIFACT_PREFIX = "scheduled-run-artifacts/";
|
|
81118
81966
|
SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
|
|
81119
81967
|
SCHEDULED_RUN_ARTIFACT_MAX_BYTES = 2e6;
|
|
@@ -81191,357 +82039,6 @@ var init_public_scheduled_result_routes = __esm({
|
|
|
81191
82039
|
}
|
|
81192
82040
|
});
|
|
81193
82041
|
|
|
81194
|
-
// src/api/service-connections.ts
|
|
81195
|
-
function ensureServiceConnectionsSchema() {
|
|
81196
|
-
const currentDb = getDb();
|
|
81197
|
-
if (schemaReady3 && schemaDb6 === currentDb) return schemaReady3;
|
|
81198
|
-
schemaDb6 = currentDb;
|
|
81199
|
-
schemaReady3 = (async () => {
|
|
81200
|
-
const db = currentDb;
|
|
81201
|
-
await db.execute(`
|
|
81202
|
-
CREATE TABLE IF NOT EXISTS service_connections (
|
|
81203
|
-
id TEXT PRIMARY KEY,
|
|
81204
|
-
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
81205
|
-
provider_config_key TEXT NOT NULL,
|
|
81206
|
-
provider TEXT,
|
|
81207
|
-
transport TEXT NOT NULL,
|
|
81208
|
-
upstream_connection_id TEXT,
|
|
81209
|
-
label TEXT,
|
|
81210
|
-
user_label TEXT,
|
|
81211
|
-
provider_account_id TEXT,
|
|
81212
|
-
provider_account_email TEXT,
|
|
81213
|
-
provider_account_name TEXT,
|
|
81214
|
-
provider_identity_checked_at TEXT,
|
|
81215
|
-
provider_identity_source_updated_at TEXT,
|
|
81216
|
-
lifecycle_status TEXT NOT NULL DEFAULT 'pending',
|
|
81217
|
-
operational_status TEXT NOT NULL DEFAULT 'unknown',
|
|
81218
|
-
reconnect_required INTEGER NOT NULL DEFAULT 0,
|
|
81219
|
-
actions_enabled INTEGER NOT NULL DEFAULT 0,
|
|
81220
|
-
read_tools_json TEXT NOT NULL DEFAULT '[]',
|
|
81221
|
-
action_tools_json TEXT NOT NULL DEFAULT '[]',
|
|
81222
|
-
tool_revision TEXT,
|
|
81223
|
-
last_checked_at TEXT,
|
|
81224
|
-
last_successful_call_at TEXT,
|
|
81225
|
-
last_failure_at TEXT,
|
|
81226
|
-
last_failure_code TEXT,
|
|
81227
|
-
last_failure_retryable INTEGER,
|
|
81228
|
-
consecutive_failures INTEGER NOT NULL DEFAULT 0,
|
|
81229
|
-
vault_name TEXT,
|
|
81230
|
-
table_name TEXT,
|
|
81231
|
-
legacy_owner_identity TEXT,
|
|
81232
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
81233
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
81234
|
-
UNIQUE(user_id, provider_config_key, upstream_connection_id)
|
|
81235
|
-
)
|
|
81236
|
-
`);
|
|
81237
|
-
try {
|
|
81238
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN user_label TEXT`);
|
|
81239
|
-
} catch {
|
|
81240
|
-
}
|
|
81241
|
-
try {
|
|
81242
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_id TEXT`);
|
|
81243
|
-
} catch {
|
|
81244
|
-
}
|
|
81245
|
-
try {
|
|
81246
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_email TEXT`);
|
|
81247
|
-
} catch {
|
|
81248
|
-
}
|
|
81249
|
-
try {
|
|
81250
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_name TEXT`);
|
|
81251
|
-
} catch {
|
|
81252
|
-
}
|
|
81253
|
-
try {
|
|
81254
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_checked_at TEXT`);
|
|
81255
|
-
} catch {
|
|
81256
|
-
}
|
|
81257
|
-
try {
|
|
81258
|
-
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_source_updated_at TEXT`);
|
|
81259
|
-
} catch {
|
|
81260
|
-
}
|
|
81261
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_user_updated ON service_connections(user_id, updated_at DESC)`);
|
|
81262
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_upstream ON service_connections(provider_config_key, upstream_connection_id)`);
|
|
81263
|
-
await db.execute(`
|
|
81264
|
-
CREATE TABLE IF NOT EXISTS service_connection_health_events (
|
|
81265
|
-
id TEXT PRIMARY KEY,
|
|
81266
|
-
connection_id TEXT NOT NULL REFERENCES service_connections(id) ON DELETE CASCADE,
|
|
81267
|
-
operational_status TEXT NOT NULL,
|
|
81268
|
-
failure_code TEXT,
|
|
81269
|
-
retryable INTEGER,
|
|
81270
|
-
evidence_source TEXT NOT NULL,
|
|
81271
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
81272
|
-
)
|
|
81273
|
-
`);
|
|
81274
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS service_connection_health_events_connection ON service_connection_health_events(connection_id, created_at DESC)`);
|
|
81275
|
-
await db.execute(`
|
|
81276
|
-
CREATE TABLE IF NOT EXISTS service_connection_action_audit (
|
|
81277
|
-
id TEXT PRIMARY KEY,
|
|
81278
|
-
connection_id TEXT NOT NULL REFERENCES service_connections(id) ON DELETE CASCADE,
|
|
81279
|
-
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
81280
|
-
tool TEXT NOT NULL,
|
|
81281
|
-
request_id TEXT NOT NULL,
|
|
81282
|
-
status TEXT NOT NULL,
|
|
81283
|
-
error_code TEXT,
|
|
81284
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
81285
|
-
UNIQUE(user_id, request_id)
|
|
81286
|
-
)
|
|
81287
|
-
`);
|
|
81288
|
-
try {
|
|
81289
|
-
await db.execute(`ALTER TABLE service_connection_action_audit ADD COLUMN request_digest TEXT`);
|
|
81290
|
-
} catch {
|
|
81291
|
-
}
|
|
81292
|
-
try {
|
|
81293
|
-
await db.execute(`ALTER TABLE service_connection_action_audit ADD COLUMN result_json TEXT`);
|
|
81294
|
-
} catch {
|
|
81295
|
-
}
|
|
81296
|
-
})().catch((error) => {
|
|
81297
|
-
schemaReady3 = null;
|
|
81298
|
-
schemaDb6 = null;
|
|
81299
|
-
throw error;
|
|
81300
|
-
});
|
|
81301
|
-
return schemaReady3;
|
|
81302
|
-
}
|
|
81303
|
-
function parseTools(value) {
|
|
81304
|
-
if (typeof value !== "string") return [];
|
|
81305
|
-
try {
|
|
81306
|
-
const parsed = JSON.parse(value);
|
|
81307
|
-
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
81308
|
-
} catch {
|
|
81309
|
-
return [];
|
|
81310
|
-
}
|
|
81311
|
-
}
|
|
81312
|
-
function rowToRecord(row) {
|
|
81313
|
-
return {
|
|
81314
|
-
id: String(row.id),
|
|
81315
|
-
userId: Number(row.user_id),
|
|
81316
|
-
providerConfigKey: String(row.provider_config_key),
|
|
81317
|
-
provider: typeof row.provider === "string" ? row.provider : null,
|
|
81318
|
-
transport: row.transport === "remote_mcp" ? "remote_mcp" : "nango",
|
|
81319
|
-
upstreamConnectionId: typeof row.upstream_connection_id === "string" ? row.upstream_connection_id : null,
|
|
81320
|
-
label: typeof row.label === "string" ? row.label : null,
|
|
81321
|
-
userLabel: typeof row.user_label === "string" ? row.user_label : null,
|
|
81322
|
-
providerAccountId: typeof row.provider_account_id === "string" ? row.provider_account_id : null,
|
|
81323
|
-
providerAccountEmail: typeof row.provider_account_email === "string" ? row.provider_account_email : null,
|
|
81324
|
-
providerAccountName: typeof row.provider_account_name === "string" ? row.provider_account_name : null,
|
|
81325
|
-
providerIdentityCheckedAt: typeof row.provider_identity_checked_at === "string" ? row.provider_identity_checked_at : null,
|
|
81326
|
-
providerIdentitySourceUpdatedAt: typeof row.provider_identity_source_updated_at === "string" ? row.provider_identity_source_updated_at : null,
|
|
81327
|
-
lifecycleStatus: String(row.lifecycle_status),
|
|
81328
|
-
operationalStatus: String(row.operational_status),
|
|
81329
|
-
reconnectRequired: Number(row.reconnect_required) === 1,
|
|
81330
|
-
actionsEnabled: Number(row.actions_enabled) === 1,
|
|
81331
|
-
readTools: parseTools(row.read_tools_json),
|
|
81332
|
-
actionTools: parseTools(row.action_tools_json),
|
|
81333
|
-
toolRevision: typeof row.tool_revision === "string" ? row.tool_revision : null,
|
|
81334
|
-
lastCheckedAt: typeof row.last_checked_at === "string" ? row.last_checked_at : null,
|
|
81335
|
-
lastSuccessfulCallAt: typeof row.last_successful_call_at === "string" ? row.last_successful_call_at : null,
|
|
81336
|
-
lastFailureAt: typeof row.last_failure_at === "string" ? row.last_failure_at : null,
|
|
81337
|
-
lastFailureCode: typeof row.last_failure_code === "string" ? row.last_failure_code : null,
|
|
81338
|
-
lastFailureRetryable: row.last_failure_retryable === null || row.last_failure_retryable === void 0 ? null : Number(row.last_failure_retryable) === 1,
|
|
81339
|
-
consecutiveFailures: Number(row.consecutive_failures ?? 0),
|
|
81340
|
-
vaultName: typeof row.vault_name === "string" ? row.vault_name : null,
|
|
81341
|
-
tableName: typeof row.table_name === "string" ? row.table_name : null,
|
|
81342
|
-
createdAt: String(row.created_at),
|
|
81343
|
-
updatedAt: String(row.updated_at)
|
|
81344
|
-
};
|
|
81345
|
-
}
|
|
81346
|
-
async function userIdForIdentity(identity) {
|
|
81347
|
-
const user = await getUserByEmail(identity.normalize("NFKC").trim().toLowerCase());
|
|
81348
|
-
if (!user) throw new Error("service_connection_user_not_found");
|
|
81349
|
-
return Number(user.id);
|
|
81350
|
-
}
|
|
81351
|
-
async function reconcileDiscoveredNangoConnections(identity, discovered) {
|
|
81352
|
-
await ensureServiceConnectionsSchema();
|
|
81353
|
-
const db = getDb();
|
|
81354
|
-
const userId = await userIdForIdentity(identity);
|
|
81355
|
-
for (const connection of discovered) {
|
|
81356
|
-
await db.execute({
|
|
81357
|
-
sql: `
|
|
81358
|
-
INSERT INTO service_connections (
|
|
81359
|
-
id, user_id, provider_config_key, provider, transport, upstream_connection_id,
|
|
81360
|
-
label, lifecycle_status, reconnect_required, actions_enabled, legacy_owner_identity, created_at, updated_at
|
|
81361
|
-
) VALUES (?, ?, ?, ?, 'nango', ?, ?, ?, ?, 1, ?, COALESCE(?, datetime('now')), COALESCE(?, datetime('now')))
|
|
81362
|
-
ON CONFLICT(user_id, provider_config_key, upstream_connection_id) DO UPDATE SET
|
|
81363
|
-
provider = excluded.provider,
|
|
81364
|
-
label = COALESCE(excluded.label, service_connections.label),
|
|
81365
|
-
lifecycle_status = excluded.lifecycle_status,
|
|
81366
|
-
reconnect_required = excluded.reconnect_required,
|
|
81367
|
-
updated_at = excluded.updated_at
|
|
81368
|
-
`,
|
|
81369
|
-
args: [
|
|
81370
|
-
(0, import_node_crypto59.randomUUID)(),
|
|
81371
|
-
userId,
|
|
81372
|
-
connection.providerConfigKey,
|
|
81373
|
-
connection.provider,
|
|
81374
|
-
connection.upstreamConnectionId,
|
|
81375
|
-
connection.label,
|
|
81376
|
-
connection.lifecycleStatus,
|
|
81377
|
-
connection.reconnectRequired ? 1 : 0,
|
|
81378
|
-
identity,
|
|
81379
|
-
connection.createdAt,
|
|
81380
|
-
connection.updatedAt
|
|
81381
|
-
]
|
|
81382
|
-
});
|
|
81383
|
-
}
|
|
81384
|
-
return listServiceConnections(identity, "nango");
|
|
81385
|
-
}
|
|
81386
|
-
async function listServiceConnections(identity, transport) {
|
|
81387
|
-
await ensureServiceConnectionsSchema();
|
|
81388
|
-
const userId = await userIdForIdentity(identity);
|
|
81389
|
-
const result = await getDb().execute({
|
|
81390
|
-
sql: `SELECT * FROM service_connections WHERE user_id = ?${transport ? " AND transport = ?" : ""} AND lifecycle_status <> 'disconnected' ORDER BY updated_at DESC`,
|
|
81391
|
-
args: transport ? [userId, transport] : [userId]
|
|
81392
|
-
});
|
|
81393
|
-
return result.rows.map((row) => rowToRecord(row));
|
|
81394
|
-
}
|
|
81395
|
-
async function getOwnedServiceConnection(identity, connectionId) {
|
|
81396
|
-
await ensureServiceConnectionsSchema();
|
|
81397
|
-
const userId = await userIdForIdentity(identity);
|
|
81398
|
-
const result = await getDb().execute({
|
|
81399
|
-
sql: "SELECT * FROM service_connections WHERE id = ? AND user_id = ? LIMIT 1",
|
|
81400
|
-
args: [connectionId, userId]
|
|
81401
|
-
});
|
|
81402
|
-
const row = result.rows[0];
|
|
81403
|
-
return row ? rowToRecord(row) : null;
|
|
81404
|
-
}
|
|
81405
|
-
async function updateServiceConnectionTools(connectionId, readTools, actionTools, toolRevision) {
|
|
81406
|
-
await ensureServiceConnectionsSchema();
|
|
81407
|
-
await getDb().execute({
|
|
81408
|
-
sql: `UPDATE service_connections SET read_tools_json = ?, action_tools_json = ?, tool_revision = ?, updated_at = datetime('now') WHERE id = ?`,
|
|
81409
|
-
args: [JSON.stringify(readTools), JSON.stringify(actionTools), toolRevision, connectionId]
|
|
81410
|
-
});
|
|
81411
|
-
}
|
|
81412
|
-
async function setServiceConnectionProviderIdentity(args) {
|
|
81413
|
-
await ensureServiceConnectionsSchema();
|
|
81414
|
-
await getDb().execute({
|
|
81415
|
-
sql: `
|
|
81416
|
-
UPDATE service_connections SET
|
|
81417
|
-
provider_account_id = ?, provider_account_email = ?, provider_account_name = ?,
|
|
81418
|
-
provider_identity_checked_at = datetime('now'), provider_identity_source_updated_at = ?,
|
|
81419
|
-
updated_at = datetime('now')
|
|
81420
|
-
WHERE id = ?
|
|
81421
|
-
`,
|
|
81422
|
-
args: [
|
|
81423
|
-
args.providerAccountId ?? null,
|
|
81424
|
-
args.providerAccountEmail?.toLowerCase() ?? null,
|
|
81425
|
-
args.providerAccountName ?? null,
|
|
81426
|
-
args.sourceUpdatedAt,
|
|
81427
|
-
args.connectionId
|
|
81428
|
-
]
|
|
81429
|
-
});
|
|
81430
|
-
}
|
|
81431
|
-
async function setServiceConnectionUserLabel(identity, connectionId, userLabel) {
|
|
81432
|
-
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
81433
|
-
if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
|
|
81434
|
-
await getDb().execute({
|
|
81435
|
-
sql: `UPDATE service_connections SET user_label = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
81436
|
-
args: [userLabel, connectionId, connection.userId]
|
|
81437
|
-
});
|
|
81438
|
-
return userLabel;
|
|
81439
|
-
}
|
|
81440
|
-
async function recordServiceConnectionHealth(args) {
|
|
81441
|
-
await ensureServiceConnectionsSchema();
|
|
81442
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
81443
|
-
await getDb().execute({
|
|
81444
|
-
sql: `
|
|
81445
|
-
UPDATE service_connections SET
|
|
81446
|
-
operational_status = ?, last_checked_at = ?,
|
|
81447
|
-
last_successful_call_at = CASE WHEN ? = 'available' THEN ? ELSE last_successful_call_at END,
|
|
81448
|
-
last_failure_at = CASE WHEN ? IN ('degraded', 'unavailable') THEN ? ELSE NULL END,
|
|
81449
|
-
last_failure_code = ?, last_failure_retryable = ?,
|
|
81450
|
-
consecutive_failures = CASE WHEN ? = 'available' THEN 0 ELSE consecutive_failures + 1 END,
|
|
81451
|
-
updated_at = datetime('now')
|
|
81452
|
-
WHERE id = ?
|
|
81453
|
-
`,
|
|
81454
|
-
args: [
|
|
81455
|
-
args.operationalStatus,
|
|
81456
|
-
now,
|
|
81457
|
-
args.operationalStatus,
|
|
81458
|
-
now,
|
|
81459
|
-
args.operationalStatus,
|
|
81460
|
-
now,
|
|
81461
|
-
args.failureCode ?? null,
|
|
81462
|
-
args.retryable == null ? null : args.retryable ? 1 : 0,
|
|
81463
|
-
args.operationalStatus,
|
|
81464
|
-
args.connectionId
|
|
81465
|
-
]
|
|
81466
|
-
});
|
|
81467
|
-
await getDb().execute({
|
|
81468
|
-
sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
81469
|
-
args: [(0, import_node_crypto59.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
|
|
81470
|
-
});
|
|
81471
|
-
}
|
|
81472
|
-
async function setServiceConnectionActions(identity, connectionId, enabled) {
|
|
81473
|
-
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
81474
|
-
if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
|
|
81475
|
-
await getDb().execute({
|
|
81476
|
-
sql: `UPDATE service_connections SET actions_enabled = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
81477
|
-
args: [enabled ? 1 : 0, connectionId, connection.userId]
|
|
81478
|
-
});
|
|
81479
|
-
return enabled;
|
|
81480
|
-
}
|
|
81481
|
-
async function markServiceConnectionDisconnected(identity, connectionId) {
|
|
81482
|
-
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
81483
|
-
if (!connection) throw new Error("service_connection_not_found");
|
|
81484
|
-
await getDb().execute({
|
|
81485
|
-
sql: `UPDATE service_connections SET lifecycle_status = 'disconnected', operational_status = 'unknown', reconnect_required = 0, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
81486
|
-
args: [connectionId, connection.userId]
|
|
81487
|
-
});
|
|
81488
|
-
const remaining = await getDb().execute({
|
|
81489
|
-
sql: `SELECT COUNT(*) AS count FROM service_connections WHERE user_id = ? AND transport = 'nango' AND lifecycle_status = 'connected'`,
|
|
81490
|
-
args: [connection.userId]
|
|
81491
|
-
});
|
|
81492
|
-
return Number(remaining.rows[0]?.count ?? 0);
|
|
81493
|
-
}
|
|
81494
|
-
async function recordServiceConnectionAction(args) {
|
|
81495
|
-
await ensureServiceConnectionsSchema();
|
|
81496
|
-
const connection = await getOwnedServiceConnection(args.identity, args.connectionId);
|
|
81497
|
-
if (!connection) throw new Error("service_connection_not_found");
|
|
81498
|
-
await getDb().execute({
|
|
81499
|
-
sql: `UPDATE service_connection_action_audit SET status = ?, error_code = ?, result_json = ? WHERE user_id = ? AND request_id = ? AND connection_id = ?`,
|
|
81500
|
-
args: [args.status, args.errorCode ?? null, args.result === void 0 ? null : JSON.stringify(args.result), connection.userId, args.requestId, connection.id]
|
|
81501
|
-
});
|
|
81502
|
-
}
|
|
81503
|
-
async function claimServiceConnectionAction(args) {
|
|
81504
|
-
await ensureServiceConnectionsSchema();
|
|
81505
|
-
const connection = await getOwnedServiceConnection(args.identity, args.connectionId);
|
|
81506
|
-
if (!connection) throw new Error("service_connection_not_found");
|
|
81507
|
-
const inserted = await getDb().execute({
|
|
81508
|
-
sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
|
|
81509
|
-
args: [(0, import_node_crypto59.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
|
|
81510
|
-
});
|
|
81511
|
-
if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
|
|
81512
|
-
const existing = await getDb().execute({
|
|
81513
|
-
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`,
|
|
81514
|
-
args: [connection.userId, args.requestId]
|
|
81515
|
-
});
|
|
81516
|
-
const row = existing.rows[0];
|
|
81517
|
-
const conflict = String(row?.connection_id ?? "") !== connection.id || String(row?.tool ?? "") !== args.tool || String(row?.request_digest ?? "") !== args.requestDigest;
|
|
81518
|
-
let result = void 0;
|
|
81519
|
-
if (!conflict && typeof row?.result_json === "string") {
|
|
81520
|
-
try {
|
|
81521
|
-
result = JSON.parse(row.result_json);
|
|
81522
|
-
} catch {
|
|
81523
|
-
result = void 0;
|
|
81524
|
-
}
|
|
81525
|
-
}
|
|
81526
|
-
return {
|
|
81527
|
-
claimed: false,
|
|
81528
|
-
status: String(row?.status ?? "unknown"),
|
|
81529
|
-
errorCode: typeof row?.error_code === "string" ? row.error_code : null,
|
|
81530
|
-
conflict,
|
|
81531
|
-
...result !== void 0 ? { result } : {}
|
|
81532
|
-
};
|
|
81533
|
-
}
|
|
81534
|
-
var import_node_crypto59, schemaReady3, schemaDb6;
|
|
81535
|
-
var init_service_connections = __esm({
|
|
81536
|
-
"src/api/service-connections.ts"() {
|
|
81537
|
-
"use strict";
|
|
81538
|
-
import_node_crypto59 = require("crypto");
|
|
81539
|
-
init_db();
|
|
81540
|
-
schemaReady3 = null;
|
|
81541
|
-
schemaDb6 = null;
|
|
81542
|
-
}
|
|
81543
|
-
});
|
|
81544
|
-
|
|
81545
82042
|
// src/api/scheduler-integration-auth.ts
|
|
81546
82043
|
function signingSecret() {
|
|
81547
82044
|
const secret2 = process.env.SCHEDULER_INTEGRATION_SIGNING_SECRET?.trim();
|