mcp-scraper 0.49.0 → 0.51.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/README.md +11 -3
- package/dist/{analytics-repository-WWXVU3VW.js → analytics-repository-IKE53S3E.js} +10 -2
- package/dist/bin/api-server.cjs +840 -177
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +4 -4
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +141 -35
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +4 -4
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +3 -3
- package/dist/{chunk-MFNGUM4L.js → chunk-2XTYLJQK.js} +15 -1
- package/dist/chunk-2XTYLJQK.js.map +1 -0
- package/dist/{chunk-YW2LXLDU.js → chunk-5BEPOFKG.js} +160 -15
- package/dist/chunk-5BEPOFKG.js.map +1 -0
- package/dist/chunk-73MUQSWC.js +7 -0
- package/dist/chunk-73MUQSWC.js.map +1 -0
- package/dist/{chunk-MFE6RMIA.js → chunk-GOZIG6HD.js} +2 -2
- package/dist/{chunk-R66PJOZW.js → chunk-NNZXZTJ2.js} +2 -2
- package/dist/{chunk-64WBUDPC.js → chunk-O63VVCCE.js} +143 -37
- package/dist/chunk-O63VVCCE.js.map +1 -0
- package/dist/{chunk-6DPIE262.js → chunk-QHVFKFME.js} +2 -2
- package/dist/{chunk-PODASLGT.js → chunk-QXAY44SA.js} +2 -2
- package/dist/{chunk-ZEMHL36J.js → chunk-YMSZO62N.js} +2 -2
- package/dist/{db-A4YUPV4Z.js → db-UPSS5BGM.js} +2 -2
- package/dist/editorial-reading-room/assets/app.js +62 -0
- package/dist/editorial-reading-room/assets/index.html +8 -0
- package/dist/editorial-reading-room/assets/styles.css +39 -0
- package/dist/{extract-bundle-2HONVC5T.js → extract-bundle-RCTNANCH.js} +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/{location-data-repository-YWFJGNIP.js → location-data-repository-UUSHH5M2.js} +3 -3
- package/dist/{server-JE7SLZLJ.js → server-IGIIEHXY.js} +518 -135
- package/dist/server-IGIIEHXY.js.map +1 -0
- package/dist/{site-extract-repository-STNOI5OH.js → site-extract-repository-3VQAALNR.js} +3 -3
- package/dist/{worker-4MHZS3OB.js → worker-KQN673JF.js} +5 -5
- package/package.json +1 -1
- package/dist/chunk-64WBUDPC.js.map +0 -1
- package/dist/chunk-MFNGUM4L.js.map +0 -1
- package/dist/chunk-XLEDHV3J.js +0 -7
- package/dist/chunk-XLEDHV3J.js.map +0 -1
- package/dist/chunk-YW2LXLDU.js.map +0 -1
- package/dist/server-JE7SLZLJ.js.map +0 -1
- /package/dist/{analytics-repository-WWXVU3VW.js.map → analytics-repository-IKE53S3E.js.map} +0 -0
- /package/dist/{chunk-MFE6RMIA.js.map → chunk-GOZIG6HD.js.map} +0 -0
- /package/dist/{chunk-R66PJOZW.js.map → chunk-NNZXZTJ2.js.map} +0 -0
- /package/dist/{chunk-6DPIE262.js.map → chunk-QHVFKFME.js.map} +0 -0
- /package/dist/{chunk-PODASLGT.js.map → chunk-QXAY44SA.js.map} +0 -0
- /package/dist/{chunk-ZEMHL36J.js.map → chunk-YMSZO62N.js.map} +0 -0
- /package/dist/{db-A4YUPV4Z.js.map → db-UPSS5BGM.js.map} +0 -0
- /package/dist/{extract-bundle-2HONVC5T.js.map → extract-bundle-RCTNANCH.js.map} +0 -0
- /package/dist/{location-data-repository-YWFJGNIP.js.map → location-data-repository-UUSHH5M2.js.map} +0 -0
- /package/dist/{site-extract-repository-STNOI5OH.js.map → site-extract-repository-3VQAALNR.js.map} +0 -0
- /package/dist/{worker-4MHZS3OB.js.map → worker-KQN673JF.js.map} +0 -0
|
@@ -10,9 +10,11 @@ import {
|
|
|
10
10
|
importHostedCensusPlacesCsv,
|
|
11
11
|
importHostedZipGroupsCsv,
|
|
12
12
|
listActiveHostedLocationDatasets
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-NNZXZTJ2.js";
|
|
14
14
|
import {
|
|
15
|
+
ANALYTICS_CONTENT_SORTS,
|
|
15
16
|
AnalyticsRepositoryError,
|
|
17
|
+
MAX_ENGAGED_MS,
|
|
16
18
|
analyticsAcquisition,
|
|
17
19
|
analyticsBusinessMetrics,
|
|
18
20
|
analyticsChannelBreakdown,
|
|
@@ -64,14 +66,14 @@ import {
|
|
|
64
66
|
updateAnalyticsPixel,
|
|
65
67
|
upsertAnalyticsAdSpend,
|
|
66
68
|
verifySession
|
|
67
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-5BEPOFKG.js";
|
|
68
70
|
import {
|
|
69
71
|
classifyHarvestProblem,
|
|
70
72
|
createHarvestAttemptRecorder,
|
|
71
73
|
deliverWebhook,
|
|
72
74
|
harvestProblemResponse,
|
|
73
75
|
serializeHarvestProblem
|
|
74
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-YMSZO62N.js";
|
|
75
77
|
import {
|
|
76
78
|
AIModeSelectors,
|
|
77
79
|
AIOverviewSelectors,
|
|
@@ -98,7 +100,7 @@ import {
|
|
|
98
100
|
resolveKernelProxyId,
|
|
99
101
|
serpArrivalUrl,
|
|
100
102
|
withRuntimeUserAgent
|
|
101
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-GOZIG6HD.js";
|
|
102
104
|
import {
|
|
103
105
|
ApiErrorResponseSchema,
|
|
104
106
|
ArtifactTemplatePresetKeySchema,
|
|
@@ -156,7 +158,7 @@ import {
|
|
|
156
158
|
resolveDeploymentProfile,
|
|
157
159
|
resolveLocalSourcebookSchemaType,
|
|
158
160
|
transcribeMediaUrl
|
|
159
|
-
} from "./chunk-
|
|
161
|
+
} from "./chunk-O63VVCCE.js";
|
|
160
162
|
import {
|
|
161
163
|
auditImageUrls,
|
|
162
164
|
auditImages,
|
|
@@ -184,7 +186,7 @@ import {
|
|
|
184
186
|
recordKernelSession,
|
|
185
187
|
recordVendorUsage,
|
|
186
188
|
runWithCostContext
|
|
187
|
-
} from "./chunk-
|
|
189
|
+
} from "./chunk-QXAY44SA.js";
|
|
188
190
|
import {
|
|
189
191
|
listWorkflowDefinitions,
|
|
190
192
|
runWorkflow,
|
|
@@ -212,7 +214,7 @@ import {
|
|
|
212
214
|
} from "./chunk-3ZUBQQPQ.js";
|
|
213
215
|
import {
|
|
214
216
|
PACKAGE_VERSION
|
|
215
|
-
} from "./chunk-
|
|
217
|
+
} from "./chunk-73MUQSWC.js";
|
|
216
218
|
import {
|
|
217
219
|
abandonExtractSettlement,
|
|
218
220
|
countSuccessfulPages,
|
|
@@ -235,7 +237,7 @@ import {
|
|
|
235
237
|
setExtractJobTotal,
|
|
236
238
|
settleExtractJob,
|
|
237
239
|
terminalExtractJobStatus
|
|
238
|
-
} from "./chunk-
|
|
240
|
+
} from "./chunk-QHVFKFME.js";
|
|
239
241
|
import {
|
|
240
242
|
BROWSER_OPEN_MIN_BALANCE_MC,
|
|
241
243
|
CONCURRENCY_PRICE_ID,
|
|
@@ -392,7 +394,7 @@ import {
|
|
|
392
394
|
startHarvestAttempt,
|
|
393
395
|
upsertPageSnapshot,
|
|
394
396
|
verifyPassword
|
|
395
|
-
} from "./chunk-
|
|
397
|
+
} from "./chunk-2XTYLJQK.js";
|
|
396
398
|
|
|
397
399
|
// src/blog/registry.ts
|
|
398
400
|
var posts = [
|
|
@@ -8738,7 +8740,7 @@ var siteExtractFn = inngest.createFunction(
|
|
|
8738
8740
|
});
|
|
8739
8741
|
}) : null;
|
|
8740
8742
|
const finalized = await step.run("finalize", async () => {
|
|
8741
|
-
const { assembleExtractArtifacts } = await import("./extract-bundle-
|
|
8743
|
+
const { assembleExtractArtifacts } = await import("./extract-bundle-RCTNANCH.js");
|
|
8742
8744
|
const current = await getExtractJob(jobId2);
|
|
8743
8745
|
if (!current) throw new Error("extract job disappeared before finalization");
|
|
8744
8746
|
const stored = await assembleExtractArtifacts(current, { branding, imageAudit });
|
|
@@ -9680,7 +9682,7 @@ async function resolveUsZipsPath(requestedPath) {
|
|
|
9680
9682
|
async function loadZipGroups(stateAbbr, requestedPath, warnings, sourceMode) {
|
|
9681
9683
|
const mode = resolvedLocationDataMode({ zipSource: sourceMode });
|
|
9682
9684
|
if (mode === "hosted") {
|
|
9683
|
-
const { getHostedZipGroups } = await import("./location-data-repository-
|
|
9685
|
+
const { getHostedZipGroups } = await import("./location-data-repository-UUSHH5M2.js");
|
|
9684
9686
|
const hosted = await getHostedZipGroups(stateAbbr);
|
|
9685
9687
|
if (!hosted.dataset) {
|
|
9686
9688
|
throw new DirectoryLocationError(
|
|
@@ -9743,7 +9745,7 @@ async function resolveDirectoryMarkets(options, config = {}) {
|
|
|
9743
9745
|
const sourceUrl = censusStateUrl(state.fips);
|
|
9744
9746
|
const warnings = [];
|
|
9745
9747
|
if (resolvedLocationDataMode(config) === "hosted") {
|
|
9746
|
-
const { queryHostedLocationMarkets } = await import("./location-data-repository-
|
|
9748
|
+
const { queryHostedLocationMarkets } = await import("./location-data-repository-UUSHH5M2.js");
|
|
9747
9749
|
const hosted = await queryHostedLocationMarkets({
|
|
9748
9750
|
state: state.abbr,
|
|
9749
9751
|
minPopulation: options.minPopulation,
|
|
@@ -10732,7 +10734,6 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
10732
10734
|
var DEFAULT_LOCK_TTL_SECONDS = 15 * 60;
|
|
10733
10735
|
var DEFAULT_RETRY_AFTER_SECONDS = 30;
|
|
10734
10736
|
var MAX_LOCK_TTL_SECONDS = 24 * 60 * 60;
|
|
10735
|
-
var schemaReady = null;
|
|
10736
10737
|
function concurrencyLimitForUser(user) {
|
|
10737
10738
|
const tier = Math.max(0, Number(user.subscription_concurrency ?? 0));
|
|
10738
10739
|
return Math.max(1, tier) + Math.max(0, Number(user.extra_concurrency_slots ?? 0));
|
|
@@ -10770,40 +10771,15 @@ function safeMetadata(metadata) {
|
|
|
10770
10771
|
return null;
|
|
10771
10772
|
}
|
|
10772
10773
|
}
|
|
10773
|
-
async function ensureConcurrencyGateSchema() {
|
|
10774
|
-
if (!schemaReady) {
|
|
10775
|
-
schemaReady = (async () => {
|
|
10776
|
-
const db = getDb();
|
|
10777
|
-
await db.execute(`
|
|
10778
|
-
CREATE TABLE IF NOT EXISTS concurrency_locks (
|
|
10779
|
-
id TEXT PRIMARY KEY,
|
|
10780
|
-
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
10781
|
-
operation TEXT NOT NULL,
|
|
10782
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
10783
|
-
acquired_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
10784
|
-
expires_at TEXT NOT NULL,
|
|
10785
|
-
released_at TEXT,
|
|
10786
|
-
metadata TEXT
|
|
10787
|
-
)
|
|
10788
|
-
`);
|
|
10789
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_user_status ON concurrency_locks(user_id, status, expires_at)`);
|
|
10790
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_operation ON concurrency_locks(operation, status, expires_at)`);
|
|
10791
|
-
})();
|
|
10792
|
-
}
|
|
10793
|
-
return schemaReady;
|
|
10794
|
-
}
|
|
10795
10774
|
async function expireConcurrencyLocksForUser(userId) {
|
|
10796
|
-
await ensureConcurrencyGateSchema();
|
|
10797
10775
|
await getDb().execute({
|
|
10798
|
-
sql: `
|
|
10799
|
-
SET status = 'expired', released_at = datetime('now')
|
|
10776
|
+
sql: `DELETE FROM concurrency_locks
|
|
10800
10777
|
WHERE user_id = ? AND status = 'active' AND expires_at <= datetime('now')`,
|
|
10801
10778
|
args: [userId]
|
|
10802
10779
|
});
|
|
10803
10780
|
}
|
|
10804
|
-
async function countActiveUsageForUser(userId) {
|
|
10805
|
-
await
|
|
10806
|
-
await expireConcurrencyLocksForUser(userId);
|
|
10781
|
+
async function countActiveUsageForUser(userId, alreadyExpired = false) {
|
|
10782
|
+
if (!alreadyExpired) await expireConcurrencyLocksForUser(userId);
|
|
10807
10783
|
const res = await getDb().execute({
|
|
10808
10784
|
sql: `SELECT
|
|
10809
10785
|
(
|
|
@@ -10823,7 +10799,6 @@ async function countActiveUsageForUser(userId) {
|
|
|
10823
10799
|
}
|
|
10824
10800
|
async function reuseExistingConcurrencyGate(userId, lockId, ttlSeconds) {
|
|
10825
10801
|
if (!/^cl_[a-f0-9]{24,32}$/.test(lockId)) return false;
|
|
10826
|
-
await ensureConcurrencyGateSchema();
|
|
10827
10802
|
await expireConcurrencyLocksForUser(userId);
|
|
10828
10803
|
const res = await getDb().execute({
|
|
10829
10804
|
sql: `UPDATE concurrency_locks
|
|
@@ -10837,13 +10812,12 @@ async function reuseExistingConcurrencyGate(userId, lockId, ttlSeconds) {
|
|
|
10837
10812
|
return Number(res.rowsAffected ?? 0) > 0;
|
|
10838
10813
|
}
|
|
10839
10814
|
async function acquireConcurrencyGate(user, operation, options = {}) {
|
|
10840
|
-
await ensureConcurrencyGateSchema();
|
|
10841
10815
|
const limit = concurrencyLimitForUser(user);
|
|
10842
10816
|
if (isConcurrencyLimitBypassed(user)) {
|
|
10843
10817
|
return { ok: true, lockId: null, active: 0, limit, operation, bypassed: true };
|
|
10844
10818
|
}
|
|
10845
10819
|
if (options.reuseLockId && await reuseExistingConcurrencyGate(user.id, options.reuseLockId, options.ttlSeconds)) {
|
|
10846
|
-
return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id), limit, operation, reused: true };
|
|
10820
|
+
return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id, true), limit, operation, reused: true };
|
|
10847
10821
|
}
|
|
10848
10822
|
await expireConcurrencyLocksForUser(user.id);
|
|
10849
10823
|
const lockId = `cl_${randomUUID2().replace(/-/g, "").slice(0, 24)}`;
|
|
@@ -10874,7 +10848,7 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
|
|
|
10874
10848
|
limit
|
|
10875
10849
|
]
|
|
10876
10850
|
});
|
|
10877
|
-
const active = await countActiveUsageForUser(user.id);
|
|
10851
|
+
const active = await countActiveUsageForUser(user.id, true);
|
|
10878
10852
|
if (Number(res.rowsAffected ?? 0) > 0) {
|
|
10879
10853
|
return { ok: true, lockId, active, limit, operation };
|
|
10880
10854
|
}
|
|
@@ -10882,17 +10856,13 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
|
|
|
10882
10856
|
}
|
|
10883
10857
|
async function releaseConcurrencyGate(lockId) {
|
|
10884
10858
|
if (!lockId) return;
|
|
10885
|
-
await ensureConcurrencyGateSchema();
|
|
10886
10859
|
await getDb().execute({
|
|
10887
|
-
sql: `
|
|
10888
|
-
SET status = 'released', released_at = datetime('now')
|
|
10889
|
-
WHERE id = ? AND status = 'active'`,
|
|
10860
|
+
sql: `DELETE FROM concurrency_locks WHERE id = ?`,
|
|
10890
10861
|
args: [lockId]
|
|
10891
10862
|
});
|
|
10892
10863
|
}
|
|
10893
10864
|
async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECONDS) {
|
|
10894
10865
|
if (!lockId) return;
|
|
10895
|
-
await ensureConcurrencyGateSchema();
|
|
10896
10866
|
await getDb().execute({
|
|
10897
10867
|
sql: `UPDATE concurrency_locks
|
|
10898
10868
|
SET expires_at = datetime('now', ?)
|
|
@@ -14709,7 +14679,7 @@ async function recoverSettlement(row, user, authorization, duplicate) {
|
|
|
14709
14679
|
}
|
|
14710
14680
|
}
|
|
14711
14681
|
await getDb().execute({
|
|
14712
|
-
sql: "UPDATE billing_events SET status = 'settled', settled_at = datetime('now') WHERE id = ? AND status
|
|
14682
|
+
sql: "UPDATE billing_events SET status = 'settled', settled_at = datetime('now') WHERE id = ? AND status IN ('settling', 'cost_pending')",
|
|
14713
14683
|
args: [row.id]
|
|
14714
14684
|
});
|
|
14715
14685
|
await getDb().execute({
|
|
@@ -14754,11 +14724,13 @@ async function settleScheduledRun(args) {
|
|
|
14754
14724
|
const rawCostNanos = rawCostUsd == null ? null : Math.round(rawCostUsd * USD_NANOS);
|
|
14755
14725
|
const modelMc = rawCostUsd == null ? 0 : vendorCostUsdToMc(rawCostUsd, SCHEDULED_AGENT_LLM_MARKUP_BPS);
|
|
14756
14726
|
const maximumModelMc = Math.max(0, Number(authorization.maximum_amount_mc) - SCHEDULED_RUN_BASE_MC);
|
|
14757
|
-
const
|
|
14727
|
+
const modelCostUnreported = args.modelCostStatus === "missing";
|
|
14728
|
+
const pendingReason = modelMc > maximumModelMc ? "max_credits_per_run_exceeded" : null;
|
|
14758
14729
|
const metadata = safeMetadata2({
|
|
14759
14730
|
...args.metadata,
|
|
14760
14731
|
billingPhase: "settlement",
|
|
14761
14732
|
chargeMode: "model_delta",
|
|
14733
|
+
...modelCostUnreported ? { modelCostUnreported: true } : {},
|
|
14762
14734
|
...pendingReason ? { reason: pendingReason } : {}
|
|
14763
14735
|
});
|
|
14764
14736
|
const eventId = existing?.id ?? randomUUID4();
|
|
@@ -14802,6 +14774,72 @@ async function settleScheduledRun(args) {
|
|
|
14802
14774
|
}
|
|
14803
14775
|
return recoverSettlement(existing, user, authorization, duplicateClaim);
|
|
14804
14776
|
}
|
|
14777
|
+
async function reconcileScheduledRunSettlements(limit = 25, minimumAgeHours = 24) {
|
|
14778
|
+
const stale = await getDb().execute({
|
|
14779
|
+
sql: `SELECT id, authorization_id, status, amount_mc, raw_cost_usd_nanos, multiplier_bps, metadata
|
|
14780
|
+
FROM billing_events
|
|
14781
|
+
WHERE billing_class = ? AND status = 'cost_pending'
|
|
14782
|
+
AND created_at < datetime('now', ?)
|
|
14783
|
+
ORDER BY created_at
|
|
14784
|
+
LIMIT ?`,
|
|
14785
|
+
args: [SCHEDULED_RUN_BILLING_CLASS, `-${Math.max(1, Math.trunc(minimumAgeHours))} hours`, Math.max(1, Math.trunc(limit))]
|
|
14786
|
+
});
|
|
14787
|
+
const result = { checked: stale.rows.length, settled: 0, chargedMc: 0, failed: 0 };
|
|
14788
|
+
for (const raw of stale.rows) {
|
|
14789
|
+
const row = raw;
|
|
14790
|
+
try {
|
|
14791
|
+
const authorization = await authorizationById(row.authorization_id);
|
|
14792
|
+
if (!authorization) {
|
|
14793
|
+
result.failed += 1;
|
|
14794
|
+
continue;
|
|
14795
|
+
}
|
|
14796
|
+
const cap = Math.max(0, Number(authorization.maximum_amount_mc) - SCHEDULED_RUN_BASE_MC);
|
|
14797
|
+
const chargeMc = Math.min(Math.max(0, Math.round(Number(row.amount_mc))), cap);
|
|
14798
|
+
if (chargeMc > 0) {
|
|
14799
|
+
const debit = await debitMcIdempotent(
|
|
14800
|
+
Number(authorization.user_id),
|
|
14801
|
+
chargeMc,
|
|
14802
|
+
LedgerOperation.SCHEDULED_RUN,
|
|
14803
|
+
"scheduled-run:" + authorization.id + ":model:reconciled",
|
|
14804
|
+
authorization.id + ":model"
|
|
14805
|
+
);
|
|
14806
|
+
if (!debit.ok) {
|
|
14807
|
+
result.failed += 1;
|
|
14808
|
+
continue;
|
|
14809
|
+
}
|
|
14810
|
+
}
|
|
14811
|
+
await getDb().execute({
|
|
14812
|
+
sql: `UPDATE billing_events
|
|
14813
|
+
SET status = 'settled', amount_mc = ?, settled_at = datetime('now'), metadata = ?
|
|
14814
|
+
WHERE id = ? AND status = 'cost_pending'`,
|
|
14815
|
+
args: [
|
|
14816
|
+
chargeMc,
|
|
14817
|
+
safeMetadata2({ ...metadataReason(row) ? { reason: metadataReason(row) } : {}, autoReconciled: true, cappedAtAuthorizedMaximum: chargeMc < Number(row.amount_mc) }),
|
|
14818
|
+
row.id
|
|
14819
|
+
]
|
|
14820
|
+
});
|
|
14821
|
+
await getDb().execute({
|
|
14822
|
+
sql: "UPDATE billing_authorizations SET status = 'settled', settled_at = datetime('now') WHERE id = ? AND status IN ('started', 'cost_pending')",
|
|
14823
|
+
args: [authorization.id]
|
|
14824
|
+
});
|
|
14825
|
+
result.settled += 1;
|
|
14826
|
+
result.chargedMc += chargeMc;
|
|
14827
|
+
console.error(JSON.stringify({
|
|
14828
|
+
event: "scheduled_run_settlement_reconciled",
|
|
14829
|
+
authorization_id: authorization.id,
|
|
14830
|
+
charged_mc: chargeMc,
|
|
14831
|
+
reason: metadataReason(row) ?? "unreported_cost"
|
|
14832
|
+
}));
|
|
14833
|
+
} catch (err) {
|
|
14834
|
+
result.failed += 1;
|
|
14835
|
+
console.error(JSON.stringify({
|
|
14836
|
+
event: "scheduled_run_reconciliation_failed",
|
|
14837
|
+
message: err instanceof Error ? err.message : String(err)
|
|
14838
|
+
}));
|
|
14839
|
+
}
|
|
14840
|
+
}
|
|
14841
|
+
return result;
|
|
14842
|
+
}
|
|
14805
14843
|
async function voidScheduledRunAuthorization(args) {
|
|
14806
14844
|
const user = await resolveSchedulingUser(args.identity);
|
|
14807
14845
|
const authorization = await authorizationById(args.authorizationId);
|
|
@@ -15673,28 +15711,6 @@ internalInboxApp.post("/broadcast", async (c) => {
|
|
|
15673
15711
|
|
|
15674
15712
|
// src/api/connected-account-billing.ts
|
|
15675
15713
|
var DEFAULT_CONNECTED_ACCOUNT_PRICE_ID = "price_1TtYPAS8aAcsk3TGHOgrDZiQ";
|
|
15676
|
-
async function ensureConnectedAccountBillingSchema() {
|
|
15677
|
-
await getDb().execute(`CREATE TABLE IF NOT EXISTS connected_account_billing (
|
|
15678
|
-
user_id INTEGER PRIMARY KEY,
|
|
15679
|
-
stripe_subscription_id TEXT,
|
|
15680
|
-
stripe_subscription_item_id TEXT,
|
|
15681
|
-
price_id TEXT,
|
|
15682
|
-
quantity INTEGER NOT NULL DEFAULT 0,
|
|
15683
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
15684
|
-
last_error_code TEXT,
|
|
15685
|
-
checked_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
15686
|
-
synced_at TEXT,
|
|
15687
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
15688
|
-
)`);
|
|
15689
|
-
await getDb().execute(`CREATE TABLE IF NOT EXISTS connected_account_charges (
|
|
15690
|
-
user_id INTEGER NOT NULL,
|
|
15691
|
-
connection_id TEXT NOT NULL,
|
|
15692
|
-
period_start TEXT NOT NULL,
|
|
15693
|
-
amount_mc INTEGER NOT NULL,
|
|
15694
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
15695
|
-
PRIMARY KEY (user_id, connection_id, period_start)
|
|
15696
|
-
)`);
|
|
15697
|
-
}
|
|
15698
15714
|
function connectedBillingPeriodStart(now = /* @__PURE__ */ new Date()) {
|
|
15699
15715
|
return `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
15700
15716
|
}
|
|
@@ -15703,7 +15719,6 @@ function isBillableConnection(connection) {
|
|
|
15703
15719
|
return BILLABLE_LIFECYCLE.has(String(connection.lifecycleStatus ?? ""));
|
|
15704
15720
|
}
|
|
15705
15721
|
async function chargedConnectionIds(userId, periodStart) {
|
|
15706
|
-
await ensureConnectedAccountBillingSchema();
|
|
15707
15722
|
const result = await getDb().execute({
|
|
15708
15723
|
sql: "SELECT connection_id FROM connected_account_charges WHERE user_id = ? AND period_start = ?",
|
|
15709
15724
|
args: [Number(userId), periodStart]
|
|
@@ -15711,7 +15726,6 @@ async function chargedConnectionIds(userId, periodStart) {
|
|
|
15711
15726
|
return new Set(result.rows.map((row) => String(row.connection_id)));
|
|
15712
15727
|
}
|
|
15713
15728
|
async function getConnectedAccountBillingState(userId) {
|
|
15714
|
-
await ensureConnectedAccountBillingSchema();
|
|
15715
15729
|
const result = await getDb().execute({
|
|
15716
15730
|
sql: "SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",
|
|
15717
15731
|
args: [Number(userId)]
|
|
@@ -15719,7 +15733,6 @@ async function getConnectedAccountBillingState(userId) {
|
|
|
15719
15733
|
return result.rows[0] ? result.rows[0] : null;
|
|
15720
15734
|
}
|
|
15721
15735
|
async function setConnectedAccountBillingState(input) {
|
|
15722
|
-
await ensureConnectedAccountBillingSchema();
|
|
15723
15736
|
await getDb().execute({
|
|
15724
15737
|
sql: `INSERT INTO connected_account_billing
|
|
15725
15738
|
(user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, checked_at, synced_at, updated_at)
|
|
@@ -19987,6 +20000,34 @@ import { Hono as Hono11 } from "hono";
|
|
|
19987
20000
|
import { z as z21 } from "zod";
|
|
19988
20001
|
|
|
19989
20002
|
// src/api/reddit-trending.ts
|
|
20003
|
+
var PARSE_REDDIT_SEARCH = `(() => {
|
|
20004
|
+
const txt = el => ((el && el.innerText) || '').trim();
|
|
20005
|
+
const bodyText = (document.body && document.body.innerText) || '';
|
|
20006
|
+
const blocked = /whoa there|blocked by network|you've been blocked|network (policy|security)|log in to your reddit account/i.test(bodyText);
|
|
20007
|
+
const listing = document.querySelector('.search-result-listing');
|
|
20008
|
+
const rows = [];
|
|
20009
|
+
document.querySelectorAll('.search-result.search-result-link').forEach(r => {
|
|
20010
|
+
const titleEl = r.querySelector('header.search-result-header a.search-title');
|
|
20011
|
+
rows.push({
|
|
20012
|
+
title: txt(titleEl),
|
|
20013
|
+
url: (titleEl && titleEl.getAttribute('href')) || '',
|
|
20014
|
+
subreddit: txt(r.querySelector('a.search-subreddit-link')),
|
|
20015
|
+
scoreText: txt(r.querySelector('.search-score')),
|
|
20016
|
+
commentsText: txt(r.querySelector('a.search-comments')),
|
|
20017
|
+
ageText: txt(r.querySelector('.search-time time')),
|
|
20018
|
+
});
|
|
20019
|
+
});
|
|
20020
|
+
return { blocked: blocked, listingFound: Boolean(listing), rowCount: rows.length, rows: rows };
|
|
20021
|
+
})()`;
|
|
20022
|
+
function buildRedditSearchUrl(topic, subreddit, window2 = "month") {
|
|
20023
|
+
const base = subreddit ? `https://old.reddit.com/r/${encodeURIComponent(subreddit)}/search` : "https://old.reddit.com/search";
|
|
20024
|
+
const params = new URLSearchParams();
|
|
20025
|
+
params.set("q", topic);
|
|
20026
|
+
params.set("sort", "top");
|
|
20027
|
+
params.set("t", window2);
|
|
20028
|
+
if (subreddit) params.set("restrict_sr", "on");
|
|
20029
|
+
return `${base}?${params.toString()}`;
|
|
20030
|
+
}
|
|
19990
20031
|
function buildSerpQuery(topic, subreddit) {
|
|
19991
20032
|
const scope = subreddit ? `site:reddit.com/r/${subreddit}` : "site:reddit.com";
|
|
19992
20033
|
return `${scope} ${topic}`.trim();
|
|
@@ -20049,6 +20090,32 @@ function canonicalThreadUrl(href) {
|
|
|
20049
20090
|
function engagementScore(score, commentCount) {
|
|
20050
20091
|
return score + 2 * commentCount;
|
|
20051
20092
|
}
|
|
20093
|
+
function normalizeSearchRows(rows) {
|
|
20094
|
+
if (!Array.isArray(rows)) return [];
|
|
20095
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20096
|
+
const out = [];
|
|
20097
|
+
for (const raw of rows) {
|
|
20098
|
+
if (!raw || typeof raw !== "object") continue;
|
|
20099
|
+
const r = raw;
|
|
20100
|
+
const url = canonicalThreadUrl(r.url);
|
|
20101
|
+
const title = typeof r.title === "string" ? r.title.trim() : "";
|
|
20102
|
+
if (!url || !title || seen.has(url)) continue;
|
|
20103
|
+
seen.add(url);
|
|
20104
|
+
const score = parseCountText(r.scoreText);
|
|
20105
|
+
const commentCount = parseCountText(r.commentsText);
|
|
20106
|
+
out.push({
|
|
20107
|
+
title,
|
|
20108
|
+
url,
|
|
20109
|
+
subreddit: typeof r.subreddit === "string" ? r.subreddit.trim() : "",
|
|
20110
|
+
score,
|
|
20111
|
+
commentCount,
|
|
20112
|
+
engagementScore: engagementScore(score, commentCount),
|
|
20113
|
+
ageText: typeof r.ageText === "string" ? r.ageText.trim() : "",
|
|
20114
|
+
topQuestions: []
|
|
20115
|
+
});
|
|
20116
|
+
}
|
|
20117
|
+
return out;
|
|
20118
|
+
}
|
|
20052
20119
|
var INTERROGATIVE_STARTERS = /* @__PURE__ */ new Set([
|
|
20053
20120
|
"what",
|
|
20054
20121
|
"whats",
|
|
@@ -20200,8 +20267,8 @@ var PARSE_REDDIT = `(() => {
|
|
|
20200
20267
|
});
|
|
20201
20268
|
return { title: title, author: author, score: score, postBody: postBody, blocked: blocked, numComments: comments.length, comments: comments };
|
|
20202
20269
|
})()`;
|
|
20203
|
-
async function scrapeOldRedditWithRetries(url, script, accept) {
|
|
20204
|
-
for (let attempt = 0; attempt <
|
|
20270
|
+
async function scrapeOldRedditWithRetries(url, script, accept, maxAttempts = 4) {
|
|
20271
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
20205
20272
|
const backupAttempt = attempt === 3;
|
|
20206
20273
|
if (backupAttempt && !backupProxyAvailable()) break;
|
|
20207
20274
|
const proxyId = backupAttempt ? await createBackupProxyIdSafe(browserServiceApiKey(), attempt) : await residentialProxyId(attempt);
|
|
@@ -20317,6 +20384,9 @@ redditApp.post("/trending", createApiKeyAuth(), async (c) => {
|
|
|
20317
20384
|
if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.reddit_thread), 402);
|
|
20318
20385
|
discoveryDebited = true;
|
|
20319
20386
|
let candidates = [];
|
|
20387
|
+
let discoverySource = "google_serp";
|
|
20388
|
+
let primaryDiscoveryDegraded = false;
|
|
20389
|
+
let primaryDegradationReasons = [];
|
|
20320
20390
|
try {
|
|
20321
20391
|
const serp = await harvest({
|
|
20322
20392
|
query: serpQuery,
|
|
@@ -20330,14 +20400,59 @@ redditApp.post("/trending", createApiKeyAuth(), async (c) => {
|
|
|
20330
20400
|
softDeadlineMs
|
|
20331
20401
|
});
|
|
20332
20402
|
candidates = extractRedditThreadUrls(serp.organicResults, body.maxThreads);
|
|
20403
|
+
primaryDiscoveryDegraded = serp.diagnostics?.degradedResult === true;
|
|
20404
|
+
primaryDegradationReasons = Array.isArray(serp.diagnostics?.degradationReasons) ? serp.diagnostics.degradationReasons.filter((reason) => typeof reason === "string") : [];
|
|
20333
20405
|
} catch {
|
|
20334
20406
|
candidates = [];
|
|
20407
|
+
primaryDiscoveryDegraded = true;
|
|
20408
|
+
primaryDegradationReasons = ["primary_serp_failed"];
|
|
20409
|
+
}
|
|
20410
|
+
if (candidates.length === 0) {
|
|
20411
|
+
const fallback = await scrapeOldRedditWithRetries(
|
|
20412
|
+
buildRedditSearchUrl(body.topic, subreddit, body.window),
|
|
20413
|
+
PARSE_REDDIT_SEARCH,
|
|
20414
|
+
(data) => !data.blocked && data.listingFound,
|
|
20415
|
+
2
|
|
20416
|
+
);
|
|
20417
|
+
if (fallback) {
|
|
20418
|
+
candidates = normalizeSearchRows(fallback.data.rows).slice(0, body.maxThreads).map((thread) => ({ title: thread.title, url: thread.url, subreddit: thread.subreddit.replace(/^r\//i, "") }));
|
|
20419
|
+
discoverySource = "reddit_search_fallback";
|
|
20420
|
+
} else {
|
|
20421
|
+
discoverySource = "none";
|
|
20422
|
+
}
|
|
20335
20423
|
}
|
|
20336
20424
|
if (candidates.length === 0) {
|
|
20337
20425
|
await creditMc(user.id, MC_COSTS.reddit_thread, LedgerOperation.REDDIT_THREAD_REFUND, "no reddit threads discovered");
|
|
20338
20426
|
discoveryRefunded = true;
|
|
20339
|
-
|
|
20340
|
-
|
|
20427
|
+
const degradedResult = discoverySource === "none";
|
|
20428
|
+
const degradationReasons = degradedResult ? [.../* @__PURE__ */ new Set([...primaryDegradationReasons, "reddit_fallback_empty_or_blocked"])] : [];
|
|
20429
|
+
const result2 = {
|
|
20430
|
+
topic: body.topic,
|
|
20431
|
+
subreddit: subreddit ?? null,
|
|
20432
|
+
window: body.window === "week" ? "7d" : "30d",
|
|
20433
|
+
totals: trendingTotals([]),
|
|
20434
|
+
rankedThreads: [],
|
|
20435
|
+
questions: [],
|
|
20436
|
+
threadsScraped: 0,
|
|
20437
|
+
candidatesFound: 0,
|
|
20438
|
+
partial: false,
|
|
20439
|
+
searchQuery: serpQuery,
|
|
20440
|
+
discoverySource,
|
|
20441
|
+
resultQuality: degradedResult ? "degraded" : "complete",
|
|
20442
|
+
degradedResult,
|
|
20443
|
+
degradationReasons,
|
|
20444
|
+
retryRecommended: degradedResult,
|
|
20445
|
+
billingRefunded: true
|
|
20446
|
+
};
|
|
20447
|
+
await logRequestEvent({
|
|
20448
|
+
userId: user.id,
|
|
20449
|
+
source: "reddit_trending",
|
|
20450
|
+
status: "done",
|
|
20451
|
+
query: body.topic,
|
|
20452
|
+
resultCount: 0,
|
|
20453
|
+
result: result2
|
|
20454
|
+
});
|
|
20455
|
+
return c.json(result2);
|
|
20341
20456
|
}
|
|
20342
20457
|
let ranked;
|
|
20343
20458
|
if (!body.includeComments) {
|
|
@@ -20413,7 +20528,13 @@ redditApp.post("/trending", createApiKeyAuth(), async (c) => {
|
|
|
20413
20528
|
threadsScraped: body.includeComments ? ranked.length : 0,
|
|
20414
20529
|
candidatesFound: candidates.length,
|
|
20415
20530
|
partial: body.includeComments && ranked.length < candidates.length,
|
|
20416
|
-
searchQuery: serpQuery
|
|
20531
|
+
searchQuery: serpQuery,
|
|
20532
|
+
discoverySource,
|
|
20533
|
+
resultQuality: primaryDiscoveryDegraded ? "partial" : "complete",
|
|
20534
|
+
degradedResult: false,
|
|
20535
|
+
degradationReasons: primaryDiscoveryDegraded ? primaryDegradationReasons : [],
|
|
20536
|
+
retryRecommended: false,
|
|
20537
|
+
billingRefunded: false
|
|
20417
20538
|
};
|
|
20418
20539
|
await logRequestEvent({ userId: user.id, source: "reddit_trending", status: "done", query: body.topic, resultCount: ranked.length, result });
|
|
20419
20540
|
return c.json(result);
|
|
@@ -22185,6 +22306,82 @@ async function reconcilePaaHarvestSettlements(limit = 25) {
|
|
|
22185
22306
|
return { checked: jobs.length, staleFailed, settled, failed };
|
|
22186
22307
|
}
|
|
22187
22308
|
|
|
22309
|
+
// src/api/retention-sweeps.ts
|
|
22310
|
+
var REPORTING_WINDOW_DAYS = 30;
|
|
22311
|
+
var TELEMETRY_RETENTION_DAYS = 45;
|
|
22312
|
+
var FAILURE_RETENTION_DAYS = 90;
|
|
22313
|
+
var RETENTION_RULES = [
|
|
22314
|
+
{ table: "request_events", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS },
|
|
22315
|
+
{ table: "kernel_session_log", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS },
|
|
22316
|
+
{ table: "vendor_usage_log", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS },
|
|
22317
|
+
{ table: "harvest_attempts", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS, timestampFormat: "iso8601" },
|
|
22318
|
+
{ table: "browser_agent_actions", timestampColumn: "created_at", retentionDays: REPORTING_WINDOW_DAYS },
|
|
22319
|
+
{ table: "jobs", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS, where: "status = 'done'" },
|
|
22320
|
+
{ table: "jobs", timestampColumn: "created_at", retentionDays: FAILURE_RETENTION_DAYS, where: "status = 'failed'" }
|
|
22321
|
+
];
|
|
22322
|
+
function cutoffFor(rule) {
|
|
22323
|
+
const cutoff = new Date(Date.now() - rule.retentionDays * 24 * 60 * 60 * 1e3);
|
|
22324
|
+
if (rule.timestampFormat === "iso8601") return cutoff.toISOString();
|
|
22325
|
+
return cutoff.toISOString().replace("T", " ").slice(0, 19);
|
|
22326
|
+
}
|
|
22327
|
+
var DEFAULT_BATCH = 500;
|
|
22328
|
+
var DEFAULT_MAX_DELETES_PER_RULE = 1e3;
|
|
22329
|
+
var DEFAULT_SWEEP_DEADLINE_MS = 15e3;
|
|
22330
|
+
async function tableExists(table) {
|
|
22331
|
+
const res = await getDb().execute({
|
|
22332
|
+
sql: "SELECT 1 FROM sqlite_master WHERE type='table' AND name = ? LIMIT 1",
|
|
22333
|
+
args: [table]
|
|
22334
|
+
});
|
|
22335
|
+
return res.rows.length > 0;
|
|
22336
|
+
}
|
|
22337
|
+
async function sweepRule(rule, maxDeletes) {
|
|
22338
|
+
if (!await tableExists(rule.table)) return 0;
|
|
22339
|
+
const filter = rule.where ? ` AND ${rule.where}` : "";
|
|
22340
|
+
const cutoff = cutoffFor(rule);
|
|
22341
|
+
let deleted = 0;
|
|
22342
|
+
while (deleted < maxDeletes) {
|
|
22343
|
+
const res = await getDb().execute({
|
|
22344
|
+
sql: `DELETE FROM ${rule.table} WHERE rowid IN (
|
|
22345
|
+
SELECT rowid FROM ${rule.table}
|
|
22346
|
+
WHERE ${rule.timestampColumn} < ?${filter}
|
|
22347
|
+
LIMIT ?)`,
|
|
22348
|
+
args: [cutoff, Math.min(DEFAULT_BATCH, maxDeletes - deleted)]
|
|
22349
|
+
});
|
|
22350
|
+
const n = Number(res.rowsAffected ?? 0);
|
|
22351
|
+
deleted += n;
|
|
22352
|
+
if (n === 0) break;
|
|
22353
|
+
}
|
|
22354
|
+
return deleted;
|
|
22355
|
+
}
|
|
22356
|
+
async function sweepRetention(options = {}) {
|
|
22357
|
+
const maxDeletes = options.maxDeletesPerRule ?? DEFAULT_MAX_DELETES_PER_RULE;
|
|
22358
|
+
const deadline = Date.now() + (options.deadlineMs ?? DEFAULT_SWEEP_DEADLINE_MS);
|
|
22359
|
+
const perTable = {};
|
|
22360
|
+
let deleted = 0;
|
|
22361
|
+
let truncated = false;
|
|
22362
|
+
for (const rule of RETENTION_RULES) {
|
|
22363
|
+
const key = rule.where ? `${rule.table}:${rule.where}` : rule.table;
|
|
22364
|
+
if (Date.now() >= deadline) {
|
|
22365
|
+
truncated = true;
|
|
22366
|
+
break;
|
|
22367
|
+
}
|
|
22368
|
+
try {
|
|
22369
|
+
const n = await sweepRule(rule, maxDeletes);
|
|
22370
|
+
perTable[key] = n;
|
|
22371
|
+
deleted += n;
|
|
22372
|
+
if (n >= maxDeletes) truncated = true;
|
|
22373
|
+
} catch (err) {
|
|
22374
|
+
perTable[key] = -1;
|
|
22375
|
+
console.error(JSON.stringify({
|
|
22376
|
+
event: "retention_sweep_failed",
|
|
22377
|
+
table: rule.table,
|
|
22378
|
+
message: err instanceof Error ? err.message : String(err)
|
|
22379
|
+
}));
|
|
22380
|
+
}
|
|
22381
|
+
}
|
|
22382
|
+
return { deleted, perTable, truncated };
|
|
22383
|
+
}
|
|
22384
|
+
|
|
22188
22385
|
// src/api/directory-workflow-reconciliation.ts
|
|
22189
22386
|
async function reconcileDirectoryWorkflowSettlements(limit = 25) {
|
|
22190
22387
|
const expiredStarts = await listExpiredDirectoryWorkflowStarts(limit);
|
|
@@ -23797,6 +23994,24 @@ import { Hono as Hono21 } from "hono";
|
|
|
23797
23994
|
import { createMcpHandler } from "@modelcontextprotocol/server";
|
|
23798
23995
|
|
|
23799
23996
|
// src/mcp/memory-mcp-tool-executor.ts
|
|
23997
|
+
function folderInventory(result) {
|
|
23998
|
+
if (!Array.isArray(result.notes)) return [];
|
|
23999
|
+
const folders = /* @__PURE__ */ new Set();
|
|
24000
|
+
for (const note of result.notes) {
|
|
24001
|
+
if (!note || typeof note !== "object" || Array.isArray(note)) continue;
|
|
24002
|
+
const path5 = note.path;
|
|
24003
|
+
if (typeof path5 !== "string") continue;
|
|
24004
|
+
const segments = path5.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
24005
|
+
for (let depth = 1; depth < segments.length; depth += 1) {
|
|
24006
|
+
folders.add(segments.slice(0, depth).join("/"));
|
|
24007
|
+
}
|
|
24008
|
+
}
|
|
24009
|
+
return [...folders].sort((a, b) => a.localeCompare(b));
|
|
24010
|
+
}
|
|
24011
|
+
function enrichMemoryResult(toolName, result) {
|
|
24012
|
+
if (toolName !== "listTool" || result.ok !== true) return result;
|
|
24013
|
+
return { ...result, folders: folderInventory(result) };
|
|
24014
|
+
}
|
|
23800
24015
|
var MemoryMcpToolExecutor = class {
|
|
23801
24016
|
baseUrl;
|
|
23802
24017
|
apiKey;
|
|
@@ -23819,7 +24034,10 @@ var MemoryMcpToolExecutor = class {
|
|
|
23819
24034
|
const message = data?.error ?? `memory ${toolName} failed (HTTP ${res.status})`;
|
|
23820
24035
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
23821
24036
|
}
|
|
23822
|
-
const result =
|
|
24037
|
+
const result = enrichMemoryResult(
|
|
24038
|
+
toolName,
|
|
24039
|
+
data ?? { ok: false, error: `memory ${toolName} returned no result` }
|
|
24040
|
+
);
|
|
23823
24041
|
return {
|
|
23824
24042
|
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
23825
24043
|
structuredContent: result,
|
|
@@ -24894,6 +25112,7 @@ async function listSessionRows(userId, includeClosed = false) {
|
|
|
24894
25112
|
});
|
|
24895
25113
|
return res.rows;
|
|
24896
25114
|
}
|
|
25115
|
+
var ORPHANED_SESSION_STATUS = "runtime_close_pending";
|
|
24897
25116
|
async function listIdleOpenSessionRows(idleSeconds) {
|
|
24898
25117
|
const db = getDb();
|
|
24899
25118
|
const res = await db.execute({
|
|
@@ -24919,12 +25138,20 @@ async function setBilledMc(id, billedMc) {
|
|
|
24919
25138
|
args: [Math.round(billedMc), id]
|
|
24920
25139
|
});
|
|
24921
25140
|
}
|
|
24922
|
-
async function markSessionClosed(id) {
|
|
25141
|
+
async function markSessionClosed(id, runtimeClosed = true) {
|
|
24923
25142
|
const db = getDb();
|
|
24924
25143
|
await db.execute({
|
|
24925
|
-
sql: `UPDATE browser_agent_sessions SET status =
|
|
24926
|
-
args: [id]
|
|
25144
|
+
sql: `UPDATE browser_agent_sessions SET status = ?, closed_at = datetime('now') WHERE id = ?`,
|
|
25145
|
+
args: [runtimeClosed ? "closed" : ORPHANED_SESSION_STATUS, id]
|
|
25146
|
+
});
|
|
25147
|
+
}
|
|
25148
|
+
async function listOrphanedSessionRows(limit = 50) {
|
|
25149
|
+
const db = getDb();
|
|
25150
|
+
const res = await db.execute({
|
|
25151
|
+
sql: `SELECT * FROM browser_agent_sessions WHERE status = ? ORDER BY closed_at LIMIT ?`,
|
|
25152
|
+
args: [ORPHANED_SESSION_STATUS, Math.max(1, Math.min(200, Math.round(limit)))]
|
|
24927
25153
|
});
|
|
25154
|
+
return res.rows;
|
|
24928
25155
|
}
|
|
24929
25156
|
async function recordAction(input) {
|
|
24930
25157
|
const db = getDb();
|
|
@@ -26065,6 +26292,21 @@ async function charge(sessionId, userId, _startedAtMs) {
|
|
|
26065
26292
|
if (res.ok) await setBilledMc(sessionId, owed);
|
|
26066
26293
|
}
|
|
26067
26294
|
}
|
|
26295
|
+
async function closeRuntimeSession(runtimeSessionId, sessionId) {
|
|
26296
|
+
try {
|
|
26297
|
+
await closeSession(runtimeSessionId);
|
|
26298
|
+
return true;
|
|
26299
|
+
} catch (err) {
|
|
26300
|
+
console.error(JSON.stringify({
|
|
26301
|
+
event: "browser_runtime_close_failed",
|
|
26302
|
+
session_id: sessionId,
|
|
26303
|
+
runtime_session_id: runtimeSessionId,
|
|
26304
|
+
error_name: err instanceof Error ? err.name : typeof err,
|
|
26305
|
+
message: sanitizeVendorName(err instanceof Error ? err.message : String(err)).slice(0, 300)
|
|
26306
|
+
}));
|
|
26307
|
+
return false;
|
|
26308
|
+
}
|
|
26309
|
+
}
|
|
26068
26310
|
async function closeAndSettleSession(row) {
|
|
26069
26311
|
if (row.user_id != null) {
|
|
26070
26312
|
try {
|
|
@@ -26072,15 +26314,24 @@ async function closeAndSettleSession(row) {
|
|
|
26072
26314
|
} catch {
|
|
26073
26315
|
}
|
|
26074
26316
|
}
|
|
26075
|
-
|
|
26076
|
-
|
|
26077
|
-
} catch {
|
|
26078
|
-
}
|
|
26079
|
-
await markSessionClosed(row.id).catch(() => {
|
|
26317
|
+
const runtimeClosed = await closeRuntimeSession(row.runtime_session_id, row.id);
|
|
26318
|
+
await markSessionClosed(row.id, runtimeClosed).catch(() => {
|
|
26080
26319
|
});
|
|
26081
26320
|
await releaseConcurrencyGate(row.concurrency_lock_id).catch(() => {
|
|
26082
26321
|
});
|
|
26083
26322
|
}
|
|
26323
|
+
async function retryOrphanedBrowserSessions(limit = 50) {
|
|
26324
|
+
const rows = await listOrphanedSessionRows(limit);
|
|
26325
|
+
let recovered = 0;
|
|
26326
|
+
for (const row of rows) {
|
|
26327
|
+
if (await closeRuntimeSession(row.runtime_session_id, row.id)) {
|
|
26328
|
+
await markSessionClosed(row.id, true).catch(() => {
|
|
26329
|
+
});
|
|
26330
|
+
recovered++;
|
|
26331
|
+
}
|
|
26332
|
+
}
|
|
26333
|
+
return { recovered, stillOpen: rows.length - recovered };
|
|
26334
|
+
}
|
|
26084
26335
|
async function completeAuthConnection(connectionId) {
|
|
26085
26336
|
const connection = await getAuthConnectionRow(connectionId);
|
|
26086
26337
|
if (!connection) return { ok: false, error: "not found" };
|
|
@@ -26105,7 +26356,7 @@ async function reapIdleBrowserSessions(idleSeconds = 120) {
|
|
|
26105
26356
|
function publicSession(row) {
|
|
26106
26357
|
return {
|
|
26107
26358
|
session_id: row.id,
|
|
26108
|
-
status: row.status,
|
|
26359
|
+
status: row.status === ORPHANED_SESSION_STATUS ? "closed" : row.status,
|
|
26109
26360
|
label: row.label,
|
|
26110
26361
|
watch_url: `/console/${row.id}`,
|
|
26111
26362
|
created_at: row.created_at,
|
|
@@ -30627,6 +30878,24 @@ function safeFilename(requested, slug2) {
|
|
|
30627
30878
|
function escapedJsonForHtml(value) {
|
|
30628
30879
|
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
30629
30880
|
}
|
|
30881
|
+
function socialMeta(input) {
|
|
30882
|
+
const image = input.site.ogImage;
|
|
30883
|
+
const tags = [
|
|
30884
|
+
'<meta property="og:type" content="website">',
|
|
30885
|
+
`<meta property="og:title" content="${escapeHtml5(input.site.title)}">`,
|
|
30886
|
+
`<meta property="og:description" content="${escapeHtml5(input.deck || input.site.heroTitle)}">`,
|
|
30887
|
+
'<meta name="twitter:card" content="summary_large_image">',
|
|
30888
|
+
`<meta name="twitter:title" content="${escapeHtml5(input.site.title)}">`,
|
|
30889
|
+
`<meta name="twitter:description" content="${escapeHtml5(input.deck || input.site.heroTitle)}">`,
|
|
30890
|
+
`<meta property="og:image" content="${image ? escapeHtml5(image.url) : ""}">`,
|
|
30891
|
+
`<meta property="og:image:alt" content="${image ? escapeHtml5(image.alt) : ""}">`,
|
|
30892
|
+
`<meta property="og:image:width" content="${image?.width ?? ""}">`,
|
|
30893
|
+
`<meta property="og:image:height" content="${image?.height ?? ""}">`,
|
|
30894
|
+
`<meta name="twitter:image" content="${image ? escapeHtml5(image.url) : ""}">`,
|
|
30895
|
+
`<meta name="twitter:image:alt" content="${image ? escapeHtml5(image.alt) : ""}">`
|
|
30896
|
+
];
|
|
30897
|
+
return tags.join("\n ");
|
|
30898
|
+
}
|
|
30630
30899
|
function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
|
|
30631
30900
|
const sourceBytes = input.articles.reduce((total, article) => total + Buffer.byteLength(article.markdown), 0);
|
|
30632
30901
|
if (sourceBytes > 2e6) throw new Error("Editorial reading-room Markdown must be 2,000,000 bytes or fewer in total.");
|
|
@@ -30659,7 +30928,7 @@ function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
|
|
|
30659
30928
|
const generatedAt = now.toISOString();
|
|
30660
30929
|
const data = { ...input, generatedAt, articles };
|
|
30661
30930
|
const template = readAsset("index.html");
|
|
30662
|
-
const html = template.replace("<title>Editorial Reading Room</title>", `<title>${escapeHtml5(input.site.title)}</title>`).replace('content="A mobile-first editorial reading room."', `content="${escapeHtml5(input.deck || input.site.heroTitle)}"`).replace("/*__READING_ROOM_STYLES__*/", readAsset("styles.css")).replace("/*__READING_ROOM_DATA__*/", escapedJsonForHtml(data)).replace("/*__READING_ROOM_SCRIPT__*/", readAsset("app.js"));
|
|
30931
|
+
const html = template.replace("<title>Editorial Reading Room</title>", `<title>${escapeHtml5(input.site.title)}</title>`).replace('content="A mobile-first editorial reading room."', `content="${escapeHtml5(input.deck || input.site.heroTitle)}"`).replace("<!--__READING_ROOM_SOCIAL_META__-->", socialMeta(input)).replace("/*__READING_ROOM_STYLES__*/", readAsset("styles.css")).replace("/*__READING_ROOM_DATA__*/", escapedJsonForHtml(data)).replace("/*__READING_ROOM_SCRIPT__*/", readAsset("app.js"));
|
|
30663
30932
|
const bytes = Buffer.byteLength(html);
|
|
30664
30933
|
return {
|
|
30665
30934
|
html,
|
|
@@ -30876,6 +31145,31 @@ import { z as z32 } from "zod";
|
|
|
30876
31145
|
// src/api/commons-repository.ts
|
|
30877
31146
|
import { createHash as createHash13, randomUUID as randomUUID15 } from "crypto";
|
|
30878
31147
|
|
|
31148
|
+
// src/api/schema-presence.ts
|
|
31149
|
+
var cache = null;
|
|
31150
|
+
async function loadSchemaObjects() {
|
|
31151
|
+
const res = await getDb().execute(
|
|
31152
|
+
"SELECT name FROM sqlite_master WHERE type IN ('table', 'index')"
|
|
31153
|
+
);
|
|
31154
|
+
return new Set(res.rows.map((row) => String(row.name)));
|
|
31155
|
+
}
|
|
31156
|
+
function invalidateSchemaPresence() {
|
|
31157
|
+
cache = null;
|
|
31158
|
+
}
|
|
31159
|
+
async function schemaObjectsPresent(names) {
|
|
31160
|
+
if (names.length === 0) return true;
|
|
31161
|
+
try {
|
|
31162
|
+
if (!cache) cache = loadSchemaObjects();
|
|
31163
|
+
const present = await cache;
|
|
31164
|
+
if (names.every((name) => present.has(name))) return true;
|
|
31165
|
+
invalidateSchemaPresence();
|
|
31166
|
+
return false;
|
|
31167
|
+
} catch {
|
|
31168
|
+
invalidateSchemaPresence();
|
|
31169
|
+
return false;
|
|
31170
|
+
}
|
|
31171
|
+
}
|
|
31172
|
+
|
|
30879
31173
|
// src/api/commons-linksets.ts
|
|
30880
31174
|
import { createHash as createHash12 } from "crypto";
|
|
30881
31175
|
var COMMONS_LINKSET_MEDIA_TYPE = "application/linkset+json";
|
|
@@ -31202,9 +31496,38 @@ var COMMONS_ENTITY_PROFILES = {
|
|
|
31202
31496
|
]
|
|
31203
31497
|
}
|
|
31204
31498
|
};
|
|
31205
|
-
var
|
|
31499
|
+
var schemaReady = false;
|
|
31500
|
+
var COMMONS_SCHEMA_OBJECTS = [
|
|
31501
|
+
"schema_migrations",
|
|
31502
|
+
"commons_entities",
|
|
31503
|
+
"commons_claims",
|
|
31504
|
+
"commons_proposals",
|
|
31505
|
+
"commons_ledger",
|
|
31506
|
+
"commons_saved_filters",
|
|
31507
|
+
"commons_index_documents",
|
|
31508
|
+
"commons_publications",
|
|
31509
|
+
"commons_publication_editions",
|
|
31510
|
+
"commons_entities_status_updated",
|
|
31511
|
+
"commons_entities_type_updated",
|
|
31512
|
+
"commons_entities_source_domain",
|
|
31513
|
+
"commons_claims_subject_predicate",
|
|
31514
|
+
"commons_claims_object_entity",
|
|
31515
|
+
"commons_proposals_user_created",
|
|
31516
|
+
"commons_proposals_entity_created",
|
|
31517
|
+
"commons_ledger_entity_created",
|
|
31518
|
+
"commons_ledger_user_created",
|
|
31519
|
+
"commons_saved_filters_user_updated",
|
|
31520
|
+
"commons_index_documents_status_updated",
|
|
31521
|
+
"commons_index_documents_entity_type",
|
|
31522
|
+
"commons_publications_updated",
|
|
31523
|
+
"commons_publication_editions_publication_published"
|
|
31524
|
+
];
|
|
31206
31525
|
async function ensureCommonsSchema() {
|
|
31207
|
-
if (
|
|
31526
|
+
if (schemaReady) return;
|
|
31527
|
+
if (await schemaObjectsPresent(COMMONS_SCHEMA_OBJECTS)) {
|
|
31528
|
+
schemaReady = true;
|
|
31529
|
+
return;
|
|
31530
|
+
}
|
|
31208
31531
|
const db = getDb();
|
|
31209
31532
|
await db.batch([
|
|
31210
31533
|
{
|
|
@@ -31425,7 +31748,7 @@ async function ensureCommonsSchema() {
|
|
|
31425
31748
|
args: [COMMONS_SCHEMA_VERSION]
|
|
31426
31749
|
}
|
|
31427
31750
|
], "write");
|
|
31428
|
-
|
|
31751
|
+
schemaReady = true;
|
|
31429
31752
|
}
|
|
31430
31753
|
async function searchCommonsEntities(filters = {}, userId) {
|
|
31431
31754
|
await ensureCommonsSchema();
|
|
@@ -34336,7 +34659,9 @@ var EventSchema = z34.object({
|
|
|
34336
34659
|
campaign: z34.string().trim().max(240).optional(),
|
|
34337
34660
|
clickIds: z34.record(z34.string(), z34.string().trim().max(500)).optional(),
|
|
34338
34661
|
deviceClass: z34.enum(["desktop", "tablet", "mobile", "unknown"]).optional(),
|
|
34339
|
-
properties: z34.record(z34.string(), z34.unknown()).optional()
|
|
34662
|
+
properties: z34.record(z34.string(), z34.unknown()).optional(),
|
|
34663
|
+
engagedMs: z34.number().int().min(0).max(MAX_ENGAGED_MS).optional(),
|
|
34664
|
+
scrollDepth: z34.number().int().min(0).max(100).optional()
|
|
34340
34665
|
}).strict();
|
|
34341
34666
|
var IngestionSchema = z34.object({
|
|
34342
34667
|
pixelId: z34.string().trim().min(8).max(160),
|
|
@@ -34495,7 +34820,10 @@ analyticsApp.use("*", async (c, next) => {
|
|
|
34495
34820
|
await next();
|
|
34496
34821
|
});
|
|
34497
34822
|
function analyticsPixelSdk() {
|
|
34498
|
-
return `(()=>{const s=document.currentScript;if(!s)return;const pixel=s.dataset.pixel;if(!pixel)return;const endpoint=new URL('/analytics/v1/events',s.src).toString(),ttl=18e5;const key='mcp_analytics_visitor',sessionKey='mcp_analytics_session',touchKey='mcp_analytics_touch';const id=()=>crypto.randomUUID?crypto.randomUUID():'e_'+Date.now().toString(36)+Math.random().toString(36).slice(2);let consent=s.dataset.consent==='granted'&&!navigator.globalPrivacyControl,visitor=null,session=null;const read=k=>{try{return localStorage.getItem(k)}catch{return null}},write=(k,v)=>{try{localStorage.setItem(k,v)}catch{}};const ensure=()=>{if(!consent)return;visitor=read(key)||id();const now=Date.now();let saved={};try{saved=JSON.parse(read(sessionKey)||'{}')}catch{}session=saved.id&&now-Number(saved.seen||0)<ttl?saved.id:id();write(key,visitor);write(sessionKey,JSON.stringify({id:session,seen:now}))};const attribution=()=>{const q=new URLSearchParams(location.search),clickIds={},properties={},map={utm_term:'utm_term',utm_content:'utm_content',utm_id:'campaign_id',campaign_id:'campaign_id',adset_id:'ad_set_id',ad_set_id:'ad_set_id',adgroup_id:'ad_group_id',ad_group_id:'ad_group_id',ad_id:'ad_id',creative_id:'creative_id',placement:'placement'};for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}for(const [from,to] of Object.entries(map)){const v=q.get(from);if(v)properties[to]=v}const current={source:q.get('utm_source')||undefined,medium:q.get('utm_medium')||undefined,campaign:q.get('utm_campaign')||undefined,clickIds,properties};if(current.source||Object.keys(clickIds).length)write(touchKey,JSON.stringify(current));else{try{return JSON.parse(read(touchKey)||'null')||current}catch{}}return current};const send=(name,properties={})=>{if(!consent||navigator.globalPrivacyControl)return;ensure();const a=attribution();const body=JSON.stringify({pixelId:pixel,events:[{eventId:id(),eventName:name,occurredAt:new Date().toISOString(),visitorId:visitor,sessionId:session,path:location.pathname,canonicalUrl:location.origin+location.pathname,title:document.title,referrer:document.referrer||undefined,...a,deviceClass:innerWidth<640?'mobile':innerWidth<1024?'tablet':'desktop',properties:{...a.properties,...properties}}]});if(navigator.sendBeacon&&navigator.sendBeacon(endpoint,new Blob([body],{type:'application/json'})))return;fetch(endpoint,{method:'POST',headers:{'content-type':'application/json'},body,keepalive:true,credentials:'omit'}).catch(()=>{})};const api={page:()=>send('page_view'),track:send,consent:state=>{consent=(state==='granted'||state===true)&&!navigator.globalPrivacyControl;if(consent){ensure();api.page()}},debug:()=>({pixel,endpoint,consent,gpc:Boolean(navigator.globalPrivacyControl),visitorId:visitor,sessionId:session,hostname:location.hostname,path:location.pathname,...attribution()})};window.mcpAnalytics=api;ensure();if(consent)api.page();let current=location.href;const changed=()=>{if(location.href!==current){current=location.href;api.page()}};for(const method of ['pushState','replaceState']){const original=history[method];history[method]=function(...args){const result=original.apply(this,args);queueMicrotask(changed);return result}}addEventListener('popstate',changed)})();`;
|
|
34823
|
+
return `(()=>{const s=document.currentScript;if(!s)return;const pixel=s.dataset.pixel;if(!pixel)return;const endpoint=new URL('/analytics/v1/events',s.src).toString(),ttl=18e5;const key='mcp_analytics_visitor',sessionKey='mcp_analytics_session',touchKey='mcp_analytics_touch';const id=()=>crypto.randomUUID?crypto.randomUUID():'e_'+Date.now().toString(36)+Math.random().toString(36).slice(2);let consent=s.dataset.consent==='granted'&&!navigator.globalPrivacyControl,visitor=null,session=null;const read=k=>{try{return localStorage.getItem(k)}catch{return null}},write=(k,v)=>{try{localStorage.setItem(k,v)}catch{}};const ensure=()=>{if(!consent)return;visitor=read(key)||id();const now=Date.now();let saved={};try{saved=JSON.parse(read(sessionKey)||'{}')}catch{}session=saved.id&&now-Number(saved.seen||0)<ttl?saved.id:id();write(key,visitor);write(sessionKey,JSON.stringify({id:session,seen:now}))};const attribution=()=>{const q=new URLSearchParams(location.search),clickIds={},properties={},map={utm_term:'utm_term',utm_content:'utm_content',utm_id:'campaign_id',campaign_id:'campaign_id',adset_id:'ad_set_id',ad_set_id:'ad_set_id',adgroup_id:'ad_group_id',ad_group_id:'ad_group_id',ad_id:'ad_id',creative_id:'creative_id',placement:'placement'};for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}for(const [from,to] of Object.entries(map)){const v=q.get(from);if(v)properties[to]=v}const current={source:q.get('utm_source')||undefined,medium:q.get('utm_medium')||undefined,campaign:q.get('utm_campaign')||undefined,clickIds,properties};if(current.source||Object.keys(clickIds).length)write(touchKey,JSON.stringify(current));else{try{return JSON.parse(read(touchKey)||'null')||current}catch{}}return current};const send=(name,properties={},over={})=>{if(!consent||navigator.globalPrivacyControl)return;ensure();const a=attribution();const body=JSON.stringify({pixelId:pixel,events:[{eventId:id(),eventName:name,occurredAt:new Date().toISOString(),visitorId:visitor,sessionId:session,path:location.pathname,canonicalUrl:location.origin+location.pathname,title:document.title,referrer:document.referrer||undefined,...a,deviceClass:innerWidth<640?'mobile':innerWidth<1024?'tablet':'desktop',properties:{...a.properties,...properties},...over}]});if(navigator.sendBeacon&&navigator.sendBeacon(endpoint,new Blob([body],{type:'application/json'})))return;fetch(endpoint,{method:'POST',headers:{'content-type':'application/json'},body,keepalive:true,credentials:'omit'}).catch(()=>{})};let visibleSince=document.visibilityState==='visible'?Date.now():0,dwellMs=0,maxScroll=0,dwellPath=location.pathname;const depth=()=>{const d=document.documentElement,h=d.scrollHeight||1;return Math.max(0,Math.min(100,Math.round(((d.scrollTop||document.body.scrollTop||0)+innerHeight)/h*100)))};const accrue=()=>{if(visibleSince){dwellMs+=Date.now()-visibleSince;visibleSince=0}};const resume=()=>{if(!visibleSince)visibleSince=Date.now()};const flush=()=>{accrue();const ms=Math.min(dwellMs,18e5),sd=Math.max(maxScroll,depth());dwellMs=0;maxScroll=0;if(ms<250)return;send('page_engagement',{},{path:dwellPath,canonicalUrl:location.origin+dwellPath,engagedMs:ms,scrollDepth:sd})};addEventListener('scroll',()=>{const d=depth();if(d>maxScroll)maxScroll=d},{passive:true});document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='hidden')flush();else resume()});addEventListener('pagehide',flush);const api={page:()=>send('page_view'),track:send,consent:state=>{consent=(state==='granted'||state===true)&&!navigator.globalPrivacyControl;if(consent){ensure();resume();api.page()}},debug:()=>({pixel,endpoint,consent,gpc:Boolean(navigator.globalPrivacyControl),visitorId:visitor,sessionId:session,hostname:location.hostname,path:location.pathname,engagedMs:dwellMs+(visibleSince?Date.now()-visibleSince:0),scrollDepth:Math.max(maxScroll,depth()),...attribution()})};window.mcpAnalytics=api;ensure();if(consent)api.page();let current=location.href;const changed=()=>{if(location.href!==current){flush();current=location.href;dwellPath=location.pathname;resume();api.page()}};for(const method of ['pushState','replaceState']){const original=history[method];history[method]=function(...args){const result=original.apply(this,args);queueMicrotask(changed);return result}}addEventListener('popstate',changed)})();`;
|
|
34824
|
+
}
|
|
34825
|
+
function pagePathFilter(value) {
|
|
34826
|
+
return value && value.startsWith("/") ? value : void 0;
|
|
34499
34827
|
}
|
|
34500
34828
|
function reportFilters(url) {
|
|
34501
34829
|
const end = url.searchParams.get("end") || (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -34517,7 +34845,7 @@ function reportFilters(url) {
|
|
|
34517
34845
|
channelFamily: url.searchParams.get("channelFamily") || void 0,
|
|
34518
34846
|
platform: url.searchParams.get("platform") || void 0,
|
|
34519
34847
|
referrer: url.searchParams.get("referrer") || void 0,
|
|
34520
|
-
path: url.searchParams.get("path")
|
|
34848
|
+
path: pagePathFilter(url.searchParams.get("path")),
|
|
34521
34849
|
deviceClass: url.searchParams.get("deviceClass") || void 0,
|
|
34522
34850
|
countryCode: url.searchParams.get("countryCode") || void 0,
|
|
34523
34851
|
regionCode: url.searchParams.get("regionCode") || void 0,
|
|
@@ -35443,14 +35771,13 @@ for (const [path5, load] of [
|
|
|
35443
35771
|
["/sites/:siteId/overview", analyticsOverview],
|
|
35444
35772
|
["/sites/:siteId/timeseries", analyticsTimeseries],
|
|
35445
35773
|
["/sites/:siteId/acquisition", analyticsAcquisition],
|
|
35446
|
-
["/sites/:siteId/content", analyticsContent],
|
|
35447
35774
|
["/sites/:siteId/events", analyticsEventCounts]
|
|
35448
35775
|
]) {
|
|
35449
35776
|
analyticsApp.get(path5, auth3, async (c) => {
|
|
35450
35777
|
const url = new URL(c.req.url);
|
|
35451
35778
|
const filters = reportFilters(url);
|
|
35452
35779
|
validateReportWindow(filters);
|
|
35453
|
-
const paginated = path5.endsWith("/acquisition") || path5.endsWith("/
|
|
35780
|
+
const paginated = path5.endsWith("/acquisition") || path5.endsWith("/events");
|
|
35454
35781
|
const data = await load(
|
|
35455
35782
|
c.req.param("siteId"),
|
|
35456
35783
|
Number(c.get("user").id),
|
|
@@ -35460,6 +35787,24 @@ for (const [path5, load] of [
|
|
|
35460
35787
|
return c.json({ ok: true, data, filters });
|
|
35461
35788
|
});
|
|
35462
35789
|
}
|
|
35790
|
+
analyticsApp.get("/sites/:siteId/content", auth3, async (c) => {
|
|
35791
|
+
const url = new URL(c.req.url);
|
|
35792
|
+
const filters = reportFilters(url);
|
|
35793
|
+
validateReportWindow(filters);
|
|
35794
|
+
const requestedSort = url.searchParams.get("sort");
|
|
35795
|
+
const data = await analyticsContent(
|
|
35796
|
+
c.req.param("siteId"),
|
|
35797
|
+
Number(c.get("user").id),
|
|
35798
|
+
filters,
|
|
35799
|
+
reportPage(url),
|
|
35800
|
+
{
|
|
35801
|
+
sort: ANALYTICS_CONTENT_SORTS.includes(requestedSort) ? requestedSort : void 0,
|
|
35802
|
+
direction: url.searchParams.get("direction") === "asc" ? "asc" : "desc",
|
|
35803
|
+
search: url.searchParams.get("search") || void 0
|
|
35804
|
+
}
|
|
35805
|
+
);
|
|
35806
|
+
return c.json({ ok: true, data, filters });
|
|
35807
|
+
});
|
|
35463
35808
|
analyticsApp.get("/sites/:siteId/dashboard", auth3, async (c) => {
|
|
35464
35809
|
const url = new URL(c.req.url);
|
|
35465
35810
|
const filters = reportFilters(url);
|
|
@@ -35681,9 +36026,9 @@ function scheduledArtifactOwnerIdForApiKey(apiKey) {
|
|
|
35681
36026
|
|
|
35682
36027
|
// src/api/scheduled-run-view-links.ts
|
|
35683
36028
|
import { createHash as createHash18, randomBytes as randomBytes4, randomUUID as randomUUID17 } from "crypto";
|
|
35684
|
-
var
|
|
36029
|
+
var schemaReady2 = false;
|
|
35685
36030
|
async function ensureScheduledRunViewLinksSchema() {
|
|
35686
|
-
if (
|
|
36031
|
+
if (schemaReady2) return;
|
|
35687
36032
|
const db = getDb();
|
|
35688
36033
|
await db.execute(`
|
|
35689
36034
|
CREATE TABLE IF NOT EXISTS scheduled_run_view_links (
|
|
@@ -35731,7 +36076,7 @@ async function ensureScheduledRunViewLinksSchema() {
|
|
|
35731
36076
|
CREATE INDEX IF NOT EXISTS scheduled_run_view_links_expiry
|
|
35732
36077
|
ON scheduled_run_view_links(expires_at, revoked_at)
|
|
35733
36078
|
`);
|
|
35734
|
-
|
|
36079
|
+
schemaReady2 = true;
|
|
35735
36080
|
}
|
|
35736
36081
|
function tokenHash2(token4) {
|
|
35737
36082
|
return createHash18("sha256").update(token4).digest("hex");
|
|
@@ -36381,13 +36726,13 @@ import { createHash as createHash22, createHmac as createHmac4, timingSafeEqual
|
|
|
36381
36726
|
|
|
36382
36727
|
// src/api/service-connections.ts
|
|
36383
36728
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
36384
|
-
var
|
|
36729
|
+
var schemaReady3 = null;
|
|
36385
36730
|
var schemaDb3 = null;
|
|
36386
36731
|
function ensureServiceConnectionsSchema() {
|
|
36387
36732
|
const currentDb = getDb();
|
|
36388
|
-
if (
|
|
36733
|
+
if (schemaReady3 && schemaDb3 === currentDb) return schemaReady3;
|
|
36389
36734
|
schemaDb3 = currentDb;
|
|
36390
|
-
|
|
36735
|
+
schemaReady3 = (async () => {
|
|
36391
36736
|
const db = currentDb;
|
|
36392
36737
|
await db.execute(`
|
|
36393
36738
|
CREATE TABLE IF NOT EXISTS service_connections (
|
|
@@ -36455,11 +36800,11 @@ function ensureServiceConnectionsSchema() {
|
|
|
36455
36800
|
} catch {
|
|
36456
36801
|
}
|
|
36457
36802
|
})().catch((error) => {
|
|
36458
|
-
|
|
36803
|
+
schemaReady3 = null;
|
|
36459
36804
|
schemaDb3 = null;
|
|
36460
36805
|
throw error;
|
|
36461
36806
|
});
|
|
36462
|
-
return
|
|
36807
|
+
return schemaReady3;
|
|
36463
36808
|
}
|
|
36464
36809
|
function parseTools(value) {
|
|
36465
36810
|
if (typeof value !== "string") return [];
|
|
@@ -43890,35 +44235,73 @@ app.post("/billing/credits", auth4, async (c) => {
|
|
|
43890
44235
|
ledger
|
|
43891
44236
|
});
|
|
43892
44237
|
});
|
|
44238
|
+
var CRON_TICK_BUDGET_MS = 5e4;
|
|
44239
|
+
var CRON_TICK_DRAIN_BUDGET_MS = 4e4;
|
|
44240
|
+
function settleWithinTickBudget(label, unfinished, work, onDeadlineOrError) {
|
|
44241
|
+
return new Promise((resolve2) => {
|
|
44242
|
+
const timer = setTimeout(() => {
|
|
44243
|
+
unfinished.push(`${label}:deadline`);
|
|
44244
|
+
resolve2(onDeadlineOrError);
|
|
44245
|
+
}, CRON_TICK_BUDGET_MS);
|
|
44246
|
+
work.then(
|
|
44247
|
+
(value) => {
|
|
44248
|
+
clearTimeout(timer);
|
|
44249
|
+
resolve2(value);
|
|
44250
|
+
},
|
|
44251
|
+
(err) => {
|
|
44252
|
+
clearTimeout(timer);
|
|
44253
|
+
unfinished.push(`${label}:error`);
|
|
44254
|
+
console.error(JSON.stringify({
|
|
44255
|
+
event: "cron_tick_job_failed",
|
|
44256
|
+
job: label,
|
|
44257
|
+
message: err instanceof Error ? err.message : String(err)
|
|
44258
|
+
}));
|
|
44259
|
+
resolve2(onDeadlineOrError);
|
|
44260
|
+
}
|
|
44261
|
+
);
|
|
44262
|
+
});
|
|
44263
|
+
}
|
|
43893
44264
|
app.get("/cron/tick", async (c) => {
|
|
43894
44265
|
const secret = c.req.header("authorization");
|
|
43895
44266
|
if (!process.env.CRON_SECRET || secret !== `Bearer ${process.env.CRON_SECRET}`) {
|
|
43896
44267
|
return c.json({ error: "Unauthorized" }, 401);
|
|
43897
44268
|
}
|
|
43898
|
-
const
|
|
43899
|
-
const
|
|
43900
|
-
const
|
|
43901
|
-
const
|
|
43902
|
-
|
|
43903
|
-
|
|
43904
|
-
|
|
43905
|
-
|
|
43906
|
-
|
|
43907
|
-
|
|
43908
|
-
|
|
43909
|
-
|
|
43910
|
-
|
|
43911
|
-
|
|
43912
|
-
|
|
43913
|
-
|
|
43914
|
-
|
|
43915
|
-
|
|
43916
|
-
|
|
43917
|
-
|
|
43918
|
-
|
|
43919
|
-
|
|
44269
|
+
const startedAt = Date.now();
|
|
44270
|
+
const { drainQueue } = await import("./worker-KQN673JF.js");
|
|
44271
|
+
const budget = { maxJobs: 10, deadlineMs: startedAt + CRON_TICK_DRAIN_BUDGET_MS };
|
|
44272
|
+
const origin = `${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`;
|
|
44273
|
+
const unfinished = [];
|
|
44274
|
+
const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup, connectedAccountBilling, directoryWorkflowDispatch, directoryWorkflowReconciliation, directoryArtifactCleanup, siteExtractArtifactCleanup, siteExtractRedispatch, siteExtractReconciliation, paaHarvestRedispatch, paaHarvestReconciliation, localSourcebookRedispatch, analyticsRollup, analyticsFormDelivery, workflowDispatchResult, scheduledRunReconciliation, retentionSweep] = await Promise.all([
|
|
44275
|
+
settleWithinTickBudget("drainQueue", unfinished, drainQueue(budget), []),
|
|
44276
|
+
settleWithinTickBudget("monthlyRefreshSweep", unfinished, runMonthlyRefreshSweep(), { status: "skipped_deadline" }),
|
|
44277
|
+
settleWithinTickBudget("reapBrowserSessions", unfinished, reapIdleBrowserSessions(120), { reaped: 0 }),
|
|
44278
|
+
settleWithinTickBudget("retryOrphanedBrowserSessions", unfinished, retryOrphanedBrowserSessions(50), { recovered: 0, stillOpen: 0 }),
|
|
44279
|
+
settleWithinTickBudget("expireLots", unfinished, expireOldLots(), { expired_lots: 0, expired_mc: 0 }),
|
|
44280
|
+
settleWithinTickBudget("scrapeBlobCleanup", unfinished, cleanupExpiredScrapeBlobs(), { deleted: 0, store: "none" }),
|
|
44281
|
+
settleWithinTickBudget("connectedDataCleanup", unfinished, cleanupExpiredConnectedDataArtifacts(), { deleted: 0, store: "none" }),
|
|
44282
|
+
settleWithinTickBudget("connectedBillingSweep", unfinished, reconcileConnectedAccountBillingSweep(), { checked: 0, synced: 0, failed: 0 }),
|
|
44283
|
+
settleWithinTickBudget("directoryDispatch", unfinished, dispatchPendingDirectoryWorkflows(5), { claimed: 0, dispatched: 0, failed: 0 }),
|
|
44284
|
+
settleWithinTickBudget("directoryReconcile", unfinished, reconcileDirectoryWorkflowSettlements(5), { checked: 0, settled: 0, failed: 0 }),
|
|
44285
|
+
settleWithinTickBudget("directoryArtifactCleanup", unfinished, cleanupExpiredDirectoryArtifacts(), { deleted: 0, store: "none" }),
|
|
44286
|
+
settleWithinTickBudget("siteExtractArtifactCleanup", unfinished, cleanupExpiredSiteExtractArtifacts(), { deleted: 0, store: "none" }),
|
|
44287
|
+
settleWithinTickBudget("siteExtractRedispatch", unfinished, redispatchFundedSiteExtractJobs(10), { checked: 0, dispatched: 0, failed: 0 }),
|
|
44288
|
+
settleWithinTickBudget("siteExtractReconcile", unfinished, reconcileSiteExtractSettlements(10), { checked: 0, settled: 0, failed: 0 }),
|
|
44289
|
+
settleWithinTickBudget("paaRedispatch", unfinished, redispatchPendingPaaHarvests(10), { checked: 0, dispatched: 0, failed: 0 }),
|
|
44290
|
+
settleWithinTickBudget("paaReconcile", unfinished, reconcilePaaHarvestSettlements(10), { checked: 0, staleFailed: 0, settled: 0, failed: 0 }),
|
|
44291
|
+
settleWithinTickBudget("localSourcebookRedispatch", unfinished, redispatchQueuedLocalSourcebookAcquisitions(5), { checked: 0, dispatched: 0, failed: 0 }),
|
|
44292
|
+
settleWithinTickBudget("analyticsRollup", unfinished, refreshAnalyticsDailyRollupsIfDue(), { status: "failed", rows: 0 }),
|
|
44293
|
+
settleWithinTickBudget("analyticsForms", unfinished, deliverPendingAnalyticsForms(10), { claimed: 0, delivered: 0, deferred: 0 }),
|
|
44294
|
+
settleWithinTickBudget("workflowDispatch", unfinished, dispatchDueWorkflowSchedules(origin), { dispatched: 0, status: "skipped_deadline" }),
|
|
44295
|
+
settleWithinTickBudget("scheduledRunReconcile", unfinished, reconcileScheduledRunSettlements(25), { checked: 0, settled: 0, chargedMc: 0, failed: 0 }),
|
|
44296
|
+
settleWithinTickBudget("retentionSweep", unfinished, sweepRetention(), { deleted: 0, perTable: {}, truncated: false })
|
|
43920
44297
|
]);
|
|
43921
44298
|
return c.json({
|
|
44299
|
+
durationMs: Date.now() - startedAt,
|
|
44300
|
+
tickBudgetMs: CRON_TICK_BUDGET_MS,
|
|
44301
|
+
unfinished,
|
|
44302
|
+
complete: unfinished.length === 0,
|
|
44303
|
+
scheduledRunReconciliation,
|
|
44304
|
+
retentionSweep,
|
|
43922
44305
|
drained: results.length,
|
|
43923
44306
|
results,
|
|
43924
44307
|
sweepResult,
|
|
@@ -43954,8 +44337,8 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
|
|
|
43954
44337
|
finishExtractJob: finishExtractJob2,
|
|
43955
44338
|
terminalExtractJobStatus: terminalExtractJobStatus2,
|
|
43956
44339
|
extractJobLimitInfo: extractJobLimitInfo2
|
|
43957
|
-
} = await import("./site-extract-repository-
|
|
43958
|
-
const { assembleExtractArtifacts } = await import("./extract-bundle-
|
|
44340
|
+
} = await import("./site-extract-repository-3VQAALNR.js");
|
|
44341
|
+
const { assembleExtractArtifacts } = await import("./extract-bundle-RCTNANCH.js");
|
|
43959
44342
|
const existing = await getExtractJob2(jobId2);
|
|
43960
44343
|
if (!existing) return c.json({ error: "job not found" }, 404);
|
|
43961
44344
|
if (existing.status !== "failed") {
|
|
@@ -43982,7 +44365,7 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
|
|
|
43982
44365
|
if (!updated) return c.json({ error: "job state changed during re-finalization" }, 409);
|
|
43983
44366
|
let settlement = "already_settled_or_refunded";
|
|
43984
44367
|
if (job.billedMc == null && job.userId != null) {
|
|
43985
|
-
const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-
|
|
44368
|
+
const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-3VQAALNR.js");
|
|
43986
44369
|
const heldMc = Number(job.options.heldMc ?? 0);
|
|
43987
44370
|
const successful = await countSuccessfulPages2(jobId2);
|
|
43988
44371
|
const usedMc = Math.min(successful * MC_COSTS.page_scrape, heldMc);
|
|
@@ -44167,4 +44550,4 @@ app.get("/blog/:slug/", (c) => {
|
|
|
44167
44550
|
export {
|
|
44168
44551
|
app
|
|
44169
44552
|
};
|
|
44170
|
-
//# sourceMappingURL=server-
|
|
44553
|
+
//# sourceMappingURL=server-IGIIEHXY.js.map
|