deepline 0.3.89 → 0.3.90
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/contracts/install-commands.json +29 -0
- package/dist/bundling-sources/sdk/src/client.ts +120 -37
- package/dist/bundling-sources/sdk/src/index.ts +4 -0
- package/dist/bundling-sources/sdk/src/pagination.ts +25 -0
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
- package/dist/bundling-sources/sdk/src/release.ts +16 -9
- package/dist/bundling-sources/sdk/src/types.ts +23 -0
- package/dist/bundling-sources/shared_libs/monitors/analytics-public.ts +10 -0
- package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +3 -0
- package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +4 -4
- package/dist/bundling-sources/shared_libs/monitors/inventory-reconciliation.ts +59 -0
- package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +7 -9
- package/dist/bundling-sources/shared_libs/monitors/package.json +7 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +44 -2
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-consumer-health.ts +2 -2
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +2 -2
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-public-aliases.ts +1 -1
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-dead-letter.ts +245 -0
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-message.ts +38 -0
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue-ack-probe.ts +94 -0
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue-delivery.ts +60 -0
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue.ts +60 -0
- package/dist/bundling-sources/shared_libs/monitors/snitcher-install-validation.ts +1 -1
- package/dist/bundling-sources/shared_libs/monitors/snitcher-ip2company.ts +2 -2
- package/dist/bundling-sources/shared_libs/monitors/snitcher-radar.ts +1 -1
- package/dist/bundling-sources/shared_libs/monitors/tracking-provider-integrations.ts +1 -1
- package/dist/bundling-sources/shared_libs/observability/build-context.ts +13 -0
- package/dist/bundling-sources/shared_libs/observability/log-metadata.ts +160 -0
- package/dist/bundling-sources/shared_libs/observability/request-context.ts +68 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +654 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-work-manual.ts +35 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-work.ts +5 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/neon-constants.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +272 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +45 -0
- package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +66 -1
- package/dist/bundling-sources/shared_libs/play-runtime/async-play-invocation.ts +0 -1
- package/dist/bundling-sources/shared_libs/play-runtime/bounceban-batching.ts +137 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +779 -140
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +46 -0
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-payload-contract.ts +68 -0
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-janitor.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-reaper.ts +725 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +29 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +23 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-invocation-fence.ts +442 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-protocol.ts +3 -3
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +18 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/contracts.ts +66 -0
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/job.ts +145 -0
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/ports.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/postgres-store.ts +170 -0
- package/dist/bundling-sources/shared_libs/play-runtime/internal-bootstrap-token.ts +1322 -0
- package/dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +112 -0
- package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +8 -0
- package/dist/bundling-sources/shared_libs/play-runtime/perf-trace.ts +127 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-artifact-runtime-contract.ts +5 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +24 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/contracts.ts +56 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/index.ts +13 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/job.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/ports.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/postgres-adapter.ts +54 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/contracts.ts +114 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/engine-suspension.ts +108 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/index.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/job.ts +97 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/ports.ts +104 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/postgres-fence-adapter.ts +263 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +64 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/contracts.ts +29 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/gateway-adapter.ts +56 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/index.ts +15 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/job.ts +65 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/ports.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/contracts.ts +42 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/index.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/job.ts +142 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/ports.ts +42 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/postgres-adapter.ts +102 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/contracts.ts +60 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/index.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +78 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +54 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/postgres-adapter.ts +71 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +5 -8
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +3 -5
- package/dist/bundling-sources/shared_libs/play-runtime/run-identity.ts +168 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +72 -9
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +6 -12
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +19 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/bundle.ts +0 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +14 -44
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +57 -205
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-authority.ts +201 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +67 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-release-environment.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-contract.ts +68 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +14 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-telemetry.ts +108 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-deadline-reconciliation.ts +88 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +124 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-run-task.ts +68 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool.ts +193 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-queue-lifecycle.ts +269 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-queue-suffix.ts +73 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +6642 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +897 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-metrics.ts +120 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +76 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd.ts +476 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-heavy-request-admission.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-progress-registry.ts +147 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/index.ts +27 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/lazy.ts +50 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/play-run-recovery.ts +98 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-budget-state.ts +260 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +1416 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +1255 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +942 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +14636 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +888 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +355 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts +46 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runner-push.ts +60 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-api-origin.ts +33 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-gateway-health.ts +64 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +835 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-ledger-reconciliation.ts +1218 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-operation.ts +207 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-scheduler-schema.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/scheduler-capacity.ts +123 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/scheduler-launch-read-observability.ts +78 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/submit-admission.ts +169 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/terminal-contract.ts +20 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-queue-metrics.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts +249 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +296 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/native-batch.ts +3 -3
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +20 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +60 -15
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execution-outcome.ts +20 -0
- package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +12 -0
- package/dist/bundling-sources/shared_libs/play-runtime/vercel-protection.ts +14 -1
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-storage-contract.ts +165 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +15 -23
- package/dist/bundling-sources/shared_libs/play-runtime/workflow-executor-auth.ts +169 -0
- package/dist/bundling-sources/shared_libs/play-runtime/zerobounce-batching.ts +122 -0
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/contracts.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/deployment-status.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/docflow.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/play-grep-search.ts +39 -0
- package/dist/bundling-sources/shared_libs/plays/share/copy.ts +271 -0
- package/dist/bundling-sources/shared_libs/plays/share/metrics.ts +140 -0
- package/dist/bundling-sources/shared_libs/plays/share/public-read-model.ts +86 -0
- package/dist/bundling-sources/shared_libs/plays/share/seo-quality.ts +181 -0
- package/dist/bundling-sources/shared_libs/plays/share/static-pipeline-summary.ts +658 -0
- package/dist/bundling-sources/shared_libs/plays/share/types.ts +197 -0
- package/dist/bundling-sources/shared_libs/plays/slack-hitl-timeout-error.ts +37 -0
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +6 -1
- package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +1 -1
- package/dist/bundling-sources/shared_libs/product-notifications/configuration.ts +7 -1
- package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +3 -2
- package/dist/bundling-sources/shared_libs/product-notifications/overflow.ts +18 -0
- package/dist/bundling-sources/shared_libs/product-notifications/rolling-limit.ts +24 -0
- package/dist/bundling-sources/shared_libs/product-notifications/slack-delivery.ts +624 -0
- package/dist/bundling-sources/shared_libs/product-notifications/slack-payload.ts +140 -0
- package/dist/bundling-sources/shared_libs/product-notifications/slack-transport.ts +75 -0
- package/dist/bundling-sources/shared_libs/runtime-env.ts +6 -0
- package/dist/bundling-sources/shared_libs/security/email-blacklist.ts +42 -0
- package/dist/bundling-sources/shared_libs/security/header-safe-env.ts +10 -3
- package/dist/bundling-sources/shared_libs/security/slack-sanitize.ts +133 -0
- package/dist/cli/getting-started.d.mts +52 -0
- package/dist/cli/getting-started.d.ts +52 -0
- package/dist/cli/getting-started.js +65 -0
- package/dist/cli/getting-started.mjs +36 -0
- package/dist/cli/index.d.mts +9 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +545 -264
- package/dist/cli/index.mjs +543 -271
- package/dist/{compiler-manifest-DsLbQaew.d.mts → compiler-manifest-BaJfE-5H.d.mts} +5 -3
- package/dist/{compiler-manifest-DsLbQaew.d.ts → compiler-manifest-BaJfE-5H.d.ts} +5 -3
- package/dist/index.d.mts +81 -148
- package/dist/index.d.ts +81 -148
- package/dist/index.js +83 -26
- package/dist/index.mjs +83 -26
- package/dist/install-integrity.json +130 -2
- package/dist/monitor-fleet-contract.d.mts +138 -0
- package/dist/monitor-fleet-contract.d.ts +138 -0
- package/dist/monitor-fleet-contract.js +404 -0
- package/dist/monitor-fleet-contract.mjs +367 -0
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/release.d.mts +255 -0
- package/dist/release.d.ts +255 -0
- package/dist/release.js +215 -0
- package/dist/release.mjs +188 -0
- package/package.json +21 -4
|
@@ -56,6 +56,12 @@ export type ProviderMonitorBatchOperationRequest = {
|
|
|
56
56
|
kind: ProviderMonitorBatchOperationKind;
|
|
57
57
|
scope: ProviderMonitorBatchScope;
|
|
58
58
|
dryRun: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The application deployment that accepted this operation. A provider
|
|
61
|
+
* callback is part of the create side effect, so a continuation must retain
|
|
62
|
+
* this origin instead of recomputing it from a later deployment.
|
|
63
|
+
*/
|
|
64
|
+
callbackBaseUrl?: string;
|
|
59
65
|
/** Required for deactivate; absent for every other operation. */
|
|
60
66
|
deactivationReason?: string;
|
|
61
67
|
};
|
|
@@ -79,6 +85,29 @@ function canonicalJson(value: unknown): string {
|
|
|
79
85
|
return JSON.stringify(value);
|
|
80
86
|
}
|
|
81
87
|
|
|
88
|
+
function normalizeCallbackBaseUrl(
|
|
89
|
+
value: string | undefined,
|
|
90
|
+
): string | undefined {
|
|
91
|
+
if (value === undefined) return undefined;
|
|
92
|
+
let parsed: URL;
|
|
93
|
+
try {
|
|
94
|
+
parsed = new URL(value);
|
|
95
|
+
} catch {
|
|
96
|
+
throw new Error('callbackBaseUrl must be an absolute HTTP(S) origin.');
|
|
97
|
+
}
|
|
98
|
+
if (
|
|
99
|
+
(parsed.protocol !== 'http:' && parsed.protocol !== 'https:') ||
|
|
100
|
+
parsed.username ||
|
|
101
|
+
parsed.password ||
|
|
102
|
+
parsed.search ||
|
|
103
|
+
parsed.hash ||
|
|
104
|
+
(parsed.pathname !== '' && parsed.pathname !== '/')
|
|
105
|
+
) {
|
|
106
|
+
throw new Error('callbackBaseUrl must be an absolute HTTP(S) origin.');
|
|
107
|
+
}
|
|
108
|
+
return parsed.origin;
|
|
109
|
+
}
|
|
110
|
+
|
|
82
111
|
/**
|
|
83
112
|
* A job id is deterministic on purpose. Convex's existing `by_run` point
|
|
84
113
|
* lookup is then the idempotency index: the same org/key can only own one run,
|
|
@@ -126,6 +155,10 @@ export function validateProviderMonitorBatchOperationRequest(
|
|
|
126
155
|
if (!isProviderMonitorBatchOperationKind(request.kind)) {
|
|
127
156
|
throw new Error(`Unknown monitor batch operation '${request.kind}'.`);
|
|
128
157
|
}
|
|
158
|
+
const callbackBaseUrl = normalizeCallbackBaseUrl(request.callbackBaseUrl);
|
|
159
|
+
if (callbackBaseUrl !== undefined && request.kind !== 'deploy') {
|
|
160
|
+
throw new Error('callbackBaseUrl is only valid for a batch deploy.');
|
|
161
|
+
}
|
|
129
162
|
if (!request.scope || typeof request.scope !== 'object') {
|
|
130
163
|
throw new Error('A monitor batch operation requires exactly one scope.');
|
|
131
164
|
}
|
|
@@ -159,7 +192,11 @@ export function validateProviderMonitorBatchOperationRequest(
|
|
|
159
192
|
if (request.kind === 'deactivate') {
|
|
160
193
|
requireMonitorDeactivationReason(request.deactivationReason!);
|
|
161
194
|
}
|
|
162
|
-
return {
|
|
195
|
+
return {
|
|
196
|
+
...request,
|
|
197
|
+
scope: { kind: 'keys', monitorKeys: keys },
|
|
198
|
+
...(callbackBaseUrl === undefined ? {} : { callbackBaseUrl }),
|
|
199
|
+
};
|
|
163
200
|
}
|
|
164
201
|
if (scope.kind === 'target') {
|
|
165
202
|
if (request.kind === 'deploy') {
|
|
@@ -178,7 +215,10 @@ export function validateProviderMonitorBatchOperationRequest(
|
|
|
178
215
|
if (request.kind === 'deactivate') {
|
|
179
216
|
requireMonitorDeactivationReason(request.deactivationReason!);
|
|
180
217
|
}
|
|
181
|
-
return
|
|
218
|
+
return {
|
|
219
|
+
...request,
|
|
220
|
+
...(callbackBaseUrl === undefined ? {} : { callbackBaseUrl }),
|
|
221
|
+
};
|
|
182
222
|
}
|
|
183
223
|
if (scope.kind === 'fleet') {
|
|
184
224
|
if (request.kind === 'deploy') {
|
|
@@ -199,6 +239,7 @@ export function validateProviderMonitorBatchOperationRequest(
|
|
|
199
239
|
return {
|
|
200
240
|
...request,
|
|
201
241
|
scope: { kind: 'fleet', fleetId: scope.fleetId.trim() },
|
|
242
|
+
...(callbackBaseUrl === undefined ? {} : { callbackBaseUrl }),
|
|
202
243
|
};
|
|
203
244
|
}
|
|
204
245
|
if (scope.kind === 'manifest') {
|
|
@@ -230,6 +271,7 @@ export function validateProviderMonitorBatchOperationRequest(
|
|
|
230
271
|
? {}
|
|
231
272
|
: { sha256: scope.sha256.trim().toLowerCase() }),
|
|
232
273
|
},
|
|
274
|
+
...(callbackBaseUrl === undefined ? {} : { callbackBaseUrl }),
|
|
233
275
|
};
|
|
234
276
|
}
|
|
235
277
|
throw new Error(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { internal } from '@convex/
|
|
2
|
-
import { getRequiredInternalConvexClient } from '
|
|
1
|
+
import { internal } from '@deepline/convex-client/function-references';
|
|
2
|
+
import { getRequiredInternalConvexClient } from '@deepline/convex-client/internal-client';
|
|
3
3
|
|
|
4
4
|
type MonitorStreamResource = {
|
|
5
5
|
key: string;
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
ProviderMonitorOrgLimitExceededError,
|
|
9
9
|
} from '@deepline/monitors/provider-monitor-errors';
|
|
10
10
|
import { MAX_PRIORITY_MONITORS_PER_ORG } from '@deepline/monitors/provider-monitor-priority-execution';
|
|
11
|
-
import { internal } from '@convex/
|
|
12
|
-
import { getRequiredInternalConvexClient } from '
|
|
11
|
+
import { internal } from '@deepline/convex-client/function-references';
|
|
12
|
+
import { getRequiredInternalConvexClient } from '@deepline/convex-client/internal-client';
|
|
13
13
|
import {
|
|
14
14
|
providerMonitorUpstreamIdentitySignature,
|
|
15
15
|
type ProviderMonitorUpstreamIdentitySignature,
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
ANALYTICS_INTEGRATION_SLUG,
|
|
3
3
|
ANALYTICS_PUBLIC_PROVIDER,
|
|
4
4
|
ANALYTICS_PUBLIC_TOOL,
|
|
5
|
-
} from '
|
|
5
|
+
} from '@deepline/monitors/analytics-public';
|
|
6
6
|
|
|
7
7
|
export const ANALYTICS_MONITOR_KEY = ANALYTICS_INTEGRATION_SLUG;
|
|
8
8
|
export const ANALYTICS_INTERNAL_MONITOR_KEY = 'snitcher-website-sessions';
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { isRepairableTenantDataPlaneError } from '@deepline/ingestion-v2/tenant-data-plane-errors';
|
|
3
|
+
import type { SignalRadarInboundMessage } from '@deepline/monitors/signal-radars/inbound-message';
|
|
4
|
+
|
|
5
|
+
// Minimal pg client seam shared with the ingestion stack: a `query(sql, args)`
|
|
6
|
+
// that returns `{ rows }`. Deliberately structural so PGlite (tests) and the
|
|
7
|
+
// Neon pooled client (prod) both satisfy it without adapters.
|
|
8
|
+
export type InboundPgClient = {
|
|
9
|
+
query: (
|
|
10
|
+
sql: string,
|
|
11
|
+
args?: unknown[],
|
|
12
|
+
) => Promise<{ rows?: unknown[]; rowCount?: number }>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type InboundFailureClass = 'transient' | 'permanent';
|
|
16
|
+
|
|
17
|
+
function errorMessage(error: unknown): string {
|
|
18
|
+
if (error instanceof Error && error.message.trim()) return error.message;
|
|
19
|
+
if (typeof error === 'string' && error.trim()) return error;
|
|
20
|
+
return String(error ?? 'unknown error');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// A TRANSIENT failure is an infra outage the event can survive: keep retrying
|
|
24
|
+
// via backoff so a long Neon/Convex flap drains on recovery instead of being
|
|
25
|
+
// dead-lettered (which would drop a real customer signal). A PERMANENT failure
|
|
26
|
+
// is a poison message — an unrecognized/invalid payload shape that will never
|
|
27
|
+
// succeed no matter how many times we redeliver — so it must give up + record.
|
|
28
|
+
//
|
|
29
|
+
// The transient side reuses the SAME classification the rest of the ingestion
|
|
30
|
+
// stack already treats as repairable (permission-denied / role drift / conn
|
|
31
|
+
// resets), so the worker never disagrees with the route on "transient".
|
|
32
|
+
export function classifyInboundFailure(error: unknown): InboundFailureClass {
|
|
33
|
+
if (isRepairableTenantDataPlaneError(error)) return 'transient';
|
|
34
|
+
|
|
35
|
+
const code =
|
|
36
|
+
error && typeof error === 'object'
|
|
37
|
+
? String((error as { code?: unknown }).code ?? '')
|
|
38
|
+
: '';
|
|
39
|
+
const transientCodes = new Set([
|
|
40
|
+
'tenant_data_plane_repair_required',
|
|
41
|
+
'provider_monitor_convex_sync_pending',
|
|
42
|
+
// The worker emits this after capture-first receipt persistence cannot be
|
|
43
|
+
// completed. The event has not entered durable storage, so it must remain
|
|
44
|
+
// retryable even after the ordinary poison-message delivery budget.
|
|
45
|
+
'provider_callback_raw_capture_unavailable',
|
|
46
|
+
// A concurrent worker still owns the durable receipt. Acknowledging this
|
|
47
|
+
// redelivery as poison could dead-letter a live callback and clear the
|
|
48
|
+
// owner's receipt before its lease completes.
|
|
49
|
+
'provider_callback_raw_receipt_in_progress',
|
|
50
|
+
'ECONNRESET',
|
|
51
|
+
'ECONNREFUSED',
|
|
52
|
+
'ETIMEDOUT',
|
|
53
|
+
'EPIPE',
|
|
54
|
+
'ENOTFOUND',
|
|
55
|
+
'EAI_AGAIN',
|
|
56
|
+
'snitcher_ip2company_retryable',
|
|
57
|
+
'57P01', // admin shutdown
|
|
58
|
+
'57P02', // crash shutdown
|
|
59
|
+
'57P03', // cannot connect now
|
|
60
|
+
'08000', // connection exception
|
|
61
|
+
'08003', // connection does not exist
|
|
62
|
+
'08006', // connection failure
|
|
63
|
+
'53300', // too many connections
|
|
64
|
+
]);
|
|
65
|
+
if (code && transientCodes.has(code)) return 'transient';
|
|
66
|
+
|
|
67
|
+
const message = errorMessage(error);
|
|
68
|
+
const transientPatterns = [
|
|
69
|
+
/connection (?:terminated|refused|reset|closed|failure)/i,
|
|
70
|
+
/could not connect to server/i,
|
|
71
|
+
/connection to server .* failed/i,
|
|
72
|
+
/the database system is (?:starting up|shutting down|in recovery)/i,
|
|
73
|
+
/timeout(?: exceeded)?(?: when trying)? to connect/i,
|
|
74
|
+
/connect(?:ion)? timeout/i,
|
|
75
|
+
/terminating connection due to/i,
|
|
76
|
+
/too many connections/i,
|
|
77
|
+
/ECONNRESET|ECONNREFUSED|ETIMEDOUT/i,
|
|
78
|
+
/convex.*(?:unavailable|timeout|network)/i,
|
|
79
|
+
/fetch failed/i,
|
|
80
|
+
/socket hang up/i,
|
|
81
|
+
];
|
|
82
|
+
if (transientPatterns.some((pattern) => pattern.test(message))) {
|
|
83
|
+
return 'transient';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Default to permanent: an unclassified failure is treated as poison so it is
|
|
87
|
+
// recorded for human review rather than retried forever. Transient outages are
|
|
88
|
+
// the ones we can positively identify (above); everything else is give-up-able.
|
|
89
|
+
return 'permanent';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type DeadLetterMessageInput = Pick<
|
|
93
|
+
SignalRadarInboundMessage,
|
|
94
|
+
'provider' | 'orgId' | 'token' | 'headers' | 'receivedAt'
|
|
95
|
+
> & {
|
|
96
|
+
admission?: SignalRadarInboundMessage['admission'];
|
|
97
|
+
radarKey?: string | null;
|
|
98
|
+
eventIdempotencyKey?: string | null;
|
|
99
|
+
rawBody?: string;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
async function markCapturedRawWriteFailed(
|
|
103
|
+
client: InboundPgClient,
|
|
104
|
+
eventIdempotencyKey: string,
|
|
105
|
+
lastError: string,
|
|
106
|
+
) {
|
|
107
|
+
// The capture-first raw row (dl_meta.monitor_inbound_raw) may already exist for
|
|
108
|
+
// this event. Flip it to write_failed so on-call sees "captured, dead-lettered,
|
|
109
|
+
// replayable" in one place. Best-effort: a missing table/row must not block the
|
|
110
|
+
// dead-letter record itself.
|
|
111
|
+
try {
|
|
112
|
+
await client.query(
|
|
113
|
+
`UPDATE dl_meta.monitor_inbound_raw
|
|
114
|
+
SET status = 'write_failed', error = $2, updated_at = now()
|
|
115
|
+
WHERE event_idempotency_key = $1`,
|
|
116
|
+
[eventIdempotencyKey, lastError.slice(0, 4000)],
|
|
117
|
+
);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.warn('[signal-radar.dead_letter.raw_mark_failed]', {
|
|
120
|
+
event_idempotency_key: eventIdempotencyKey,
|
|
121
|
+
error: error instanceof Error ? error.message : String(error),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Idempotent dead-letter writer. Keyed on the provider event idempotency key via
|
|
127
|
+
// the partial-unique index, so the SAME poison event redelivered N times yields
|
|
128
|
+
// exactly ONE dead letter (attempts/last_error bumped), while two distinct
|
|
129
|
+
// null-keyed poison events stay distinct (the partial index only enforces
|
|
130
|
+
// uniqueness WHERE event_idempotency_key IS NOT NULL). Also marks the captured
|
|
131
|
+
// raw row write_failed when one exists for the key.
|
|
132
|
+
export async function recordSignalRadarDeadLetter(input: {
|
|
133
|
+
client: InboundPgClient;
|
|
134
|
+
message: DeadLetterMessageInput;
|
|
135
|
+
error: unknown;
|
|
136
|
+
attempts: number;
|
|
137
|
+
}): Promise<void> {
|
|
138
|
+
const { client, message } = input;
|
|
139
|
+
const lastError = errorMessage(input.error);
|
|
140
|
+
const eventIdempotencyKey = message.eventIdempotencyKey ?? null;
|
|
141
|
+
const payload = {
|
|
142
|
+
provider: message.provider,
|
|
143
|
+
orgId: message.orgId,
|
|
144
|
+
token: message.token,
|
|
145
|
+
radarKey: message.radarKey ?? null,
|
|
146
|
+
eventIdempotencyKey,
|
|
147
|
+
rawBody: message.rawBody ?? null,
|
|
148
|
+
headers: message.headers ?? {},
|
|
149
|
+
receivedAt: message.receivedAt,
|
|
150
|
+
admission: message.admission ?? null,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (eventIdempotencyKey) {
|
|
154
|
+
await client.query(
|
|
155
|
+
`INSERT INTO dl_meta.signal_radar_inbound_dead_letter
|
|
156
|
+
(dead_letter_id, provider, radar_key, org_id, event_idempotency_key,
|
|
157
|
+
payload, headers, attempts, last_error, status)
|
|
158
|
+
VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7::jsonb, $8, $9, 'open')
|
|
159
|
+
ON CONFLICT (event_idempotency_key)
|
|
160
|
+
WHERE event_idempotency_key IS NOT NULL
|
|
161
|
+
DO UPDATE SET
|
|
162
|
+
attempts = EXCLUDED.attempts,
|
|
163
|
+
last_error = EXCLUDED.last_error,
|
|
164
|
+
payload = EXCLUDED.payload,
|
|
165
|
+
headers = EXCLUDED.headers,
|
|
166
|
+
updated_at = now()`,
|
|
167
|
+
[
|
|
168
|
+
randomUUID(),
|
|
169
|
+
message.provider,
|
|
170
|
+
message.radarKey ?? null,
|
|
171
|
+
message.orgId ?? null,
|
|
172
|
+
eventIdempotencyKey,
|
|
173
|
+
JSON.stringify(payload),
|
|
174
|
+
JSON.stringify(message.headers ?? {}),
|
|
175
|
+
input.attempts,
|
|
176
|
+
lastError.slice(0, 8000),
|
|
177
|
+
],
|
|
178
|
+
);
|
|
179
|
+
await markCapturedRawWriteFailed(client, eventIdempotencyKey, lastError);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// No idempotency key -> the partial index does not apply, so each null-keyed
|
|
184
|
+
// poison event is its own dead letter (never merged).
|
|
185
|
+
await client.query(
|
|
186
|
+
`INSERT INTO dl_meta.signal_radar_inbound_dead_letter
|
|
187
|
+
(dead_letter_id, provider, radar_key, org_id, event_idempotency_key,
|
|
188
|
+
payload, headers, attempts, last_error, status)
|
|
189
|
+
VALUES ($1, $2, $3, $4, NULL, $5::jsonb, $6::jsonb, $7, $8, 'open')`,
|
|
190
|
+
[
|
|
191
|
+
randomUUID(),
|
|
192
|
+
message.provider,
|
|
193
|
+
message.radarKey ?? null,
|
|
194
|
+
message.orgId ?? null,
|
|
195
|
+
JSON.stringify(payload),
|
|
196
|
+
JSON.stringify(message.headers ?? {}),
|
|
197
|
+
input.attempts,
|
|
198
|
+
lastError.slice(0, 8000),
|
|
199
|
+
],
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// The load-bearing retry-callback decision (plan §4a). Runs on every handler
|
|
204
|
+
// throw:
|
|
205
|
+
// - transient -> back off + redeliver, NEVER acknowledge, regardless of how
|
|
206
|
+
// far past maxDeliveries we are. A long Neon outage must drain on recovery.
|
|
207
|
+
// - permanent, still within maxDeliveries -> back off (give it its allotted
|
|
208
|
+
// retries; a "permanent" verdict can be a transient mislabel early on).
|
|
209
|
+
// - permanent, past maxDeliveries -> record the dead letter + acknowledge
|
|
210
|
+
// (stop retrying; it is now durably captured for human review + replay).
|
|
211
|
+
export type InboundRetryDecision =
|
|
212
|
+
| { afterSeconds: number }
|
|
213
|
+
| { acknowledge: true };
|
|
214
|
+
|
|
215
|
+
export function inboundBackoffSeconds(deliveryCount: number): number {
|
|
216
|
+
// Mirror the plan's bounded exponential backoff: 2^n * 5, capped at 300s.
|
|
217
|
+
const exponent = Math.max(1, deliveryCount);
|
|
218
|
+
return Math.min(300, 2 ** exponent * 5);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export async function resolveInboundRetryDecision(input: {
|
|
222
|
+
client: InboundPgClient;
|
|
223
|
+
message: DeadLetterMessageInput;
|
|
224
|
+
error: unknown;
|
|
225
|
+
deliveryCount: number;
|
|
226
|
+
maxDeliveries: number;
|
|
227
|
+
}): Promise<InboundRetryDecision> {
|
|
228
|
+
const failureClass = classifyInboundFailure(input.error);
|
|
229
|
+
|
|
230
|
+
if (failureClass === 'transient') {
|
|
231
|
+
return { afterSeconds: inboundBackoffSeconds(input.deliveryCount) };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (input.deliveryCount <= input.maxDeliveries) {
|
|
235
|
+
return { afterSeconds: inboundBackoffSeconds(input.deliveryCount) };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
await recordSignalRadarDeadLetter({
|
|
239
|
+
client: input.client,
|
|
240
|
+
message: input.message,
|
|
241
|
+
error: input.error,
|
|
242
|
+
attempts: input.deliveryCount,
|
|
243
|
+
});
|
|
244
|
+
return { acknowledge: true };
|
|
245
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// The durable message the thin Monitor inbound receiver enqueues onto the
|
|
2
|
+
// Vercel Queues front-queue. It carries ONLY what the worker needs that is not
|
|
3
|
+
// derivable without the provider's HTTP request — nothing Neon-derived. The
|
|
4
|
+
// worker re-runs the full ingestion machinery from this envelope alone (resolve
|
|
5
|
+
// the radar binding from Neon, capture-first raw, typed write, inline dispatch),
|
|
6
|
+
// so the receiver never has to touch Neon before returning 200.
|
|
7
|
+
|
|
8
|
+
export type SignalRadarInboundMessage = {
|
|
9
|
+
provider: string;
|
|
10
|
+
orgId: string;
|
|
11
|
+
token: string;
|
|
12
|
+
rawBody: string;
|
|
13
|
+
headers: Record<string, string>;
|
|
14
|
+
receivedAt: string;
|
|
15
|
+
admission: {
|
|
16
|
+
id: string;
|
|
17
|
+
bindingVersion: number;
|
|
18
|
+
/** A batch callback is resolved by its provider resource ID in Tier 2. */
|
|
19
|
+
routingMode?: 'single_monitor' | 'batch_by_upstream_resource';
|
|
20
|
+
verificationMode:
|
|
21
|
+
| 'hmac_signature'
|
|
22
|
+
| 'shared_secret_body'
|
|
23
|
+
| 'secret_header'
|
|
24
|
+
| 'callback_token_only';
|
|
25
|
+
};
|
|
26
|
+
// The Neon-free provider-event publish idempotency key, computed by the
|
|
27
|
+
// receiver from what is on the wire. Carried so the worker and dead-letter
|
|
28
|
+
// writer can key on the same grain the publish deduped on.
|
|
29
|
+
eventIdempotencyKey?: string | null;
|
|
30
|
+
// A preview monitor callback is received by Vercel but its run executes on
|
|
31
|
+
// an isolated scheduler lane. Preserve that request-scoped context across
|
|
32
|
+
// the durable queue boundary.
|
|
33
|
+
postgresSchedulerSchema?: string | null;
|
|
34
|
+
runtimeApiBaseUrl?: string | null;
|
|
35
|
+
// Optional radar hint the worker resolves durably from Neon; present only on
|
|
36
|
+
// dead-letter/replay envelopes where it was already known.
|
|
37
|
+
radarKey?: string | null;
|
|
38
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { getVercelOidcToken } from '@vercel/oidc';
|
|
2
|
+
|
|
3
|
+
import { SIGNAL_RADAR_INBOUND_TOPIC } from '@deepline/monitors/signal-radars/inbound-queue';
|
|
4
|
+
|
|
5
|
+
// Must match the function key in apps/deepline-api/vercel.json. Vercel Queue's push-trigger
|
|
6
|
+
// adapter derives the consumer group from this path. Deriving rather than
|
|
7
|
+
// copying the encoded value makes the transformation reviewable and testable.
|
|
8
|
+
export const SIGNAL_RADAR_INBOUND_TRIGGER_PATH =
|
|
9
|
+
'src/app/api/queues/signal-radar-inbound/route.ts';
|
|
10
|
+
|
|
11
|
+
const DEEPLINE_API_PROJECT_ROOT = 'apps/deepline-api/';
|
|
12
|
+
|
|
13
|
+
export function vercelQueueConsumerGroupForFunctionPath(functionPath: string) {
|
|
14
|
+
return functionPath
|
|
15
|
+
.replaceAll('_', '__')
|
|
16
|
+
.replaceAll('/', '_S')
|
|
17
|
+
.replaceAll('.', '_D');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const SIGNAL_RADAR_INBOUND_CONSUMER_GROUP =
|
|
21
|
+
vercelQueueConsumerGroupForFunctionPath(
|
|
22
|
+
SIGNAL_RADAR_INBOUND_TRIGGER_PATH.replace(DEEPLINE_API_PROJECT_ROOT, ''),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export type SignalRadarInboundQueueMessageState =
|
|
26
|
+
| 'acknowledged'
|
|
27
|
+
| 'available'
|
|
28
|
+
| 'in_flight'
|
|
29
|
+
| 'not_found';
|
|
30
|
+
|
|
31
|
+
type ProbeDependencies = {
|
|
32
|
+
fetch?: typeof fetch;
|
|
33
|
+
getOidcToken?: () => Promise<string>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type ProbeInput = {
|
|
37
|
+
messageId: string;
|
|
38
|
+
region: string | undefined;
|
|
39
|
+
deploymentId: string | undefined;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function requiredRuntimeValue(name: string, value: string | undefined) {
|
|
43
|
+
if (value?.trim()) return value.trim();
|
|
44
|
+
throw new Error(
|
|
45
|
+
`${name} is required for the Vercel Queue acknowledgement probe.`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Peeks at the exact message in Vercel's push-consumer group without leasing,
|
|
51
|
+
* acknowledging, or otherwise changing it. A 410 is the direct proof that
|
|
52
|
+
* the consumer successfully acknowledged this message.
|
|
53
|
+
*/
|
|
54
|
+
export async function signalRadarInboundQueueMessageState(
|
|
55
|
+
input: ProbeInput,
|
|
56
|
+
dependencies: ProbeDependencies = {},
|
|
57
|
+
): Promise<SignalRadarInboundQueueMessageState> {
|
|
58
|
+
const region = requiredRuntimeValue('VERCEL_REGION', input.region);
|
|
59
|
+
const deploymentId = requiredRuntimeValue(
|
|
60
|
+
'VERCEL_DEPLOYMENT_ID',
|
|
61
|
+
input.deploymentId,
|
|
62
|
+
);
|
|
63
|
+
const requestFetch = dependencies.fetch ?? fetch;
|
|
64
|
+
const getToken = dependencies.getOidcToken ?? getVercelOidcToken;
|
|
65
|
+
const response = await requestFetch(
|
|
66
|
+
`https://${region}.vercel-queue.com/api/v3/topic/${encodeURIComponent(
|
|
67
|
+
SIGNAL_RADAR_INBOUND_TOPIC,
|
|
68
|
+
)}/consumer/${encodeURIComponent(
|
|
69
|
+
SIGNAL_RADAR_INBOUND_CONSUMER_GROUP,
|
|
70
|
+
)}/id/${encodeURIComponent(input.messageId)}`,
|
|
71
|
+
{
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: {
|
|
74
|
+
authorization: `Bearer ${await getToken()}`,
|
|
75
|
+
accept: 'multipart/mixed',
|
|
76
|
+
// Vercel documents zero as a non-leasing peek. This probe must never
|
|
77
|
+
// become a competing consumer or acknowledge an unprocessed message.
|
|
78
|
+
'vqs-visibility-timeout-seconds': '0',
|
|
79
|
+
'vqs-deployment-id': deploymentId,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (response.status === 410) return 'acknowledged';
|
|
85
|
+
if (response.status === 409) return 'in_flight';
|
|
86
|
+
if (response.status === 404) return 'not_found';
|
|
87
|
+
if (response.status === 200) return 'available';
|
|
88
|
+
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Vercel Queue acknowledgement probe failed with HTTP ${response.status}: ${(
|
|
91
|
+
await response.text()
|
|
92
|
+
).slice(0, 500)}`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifyInboundFailure,
|
|
3
|
+
inboundBackoffSeconds,
|
|
4
|
+
} from '@deepline/monitors/signal-radars/inbound-dead-letter';
|
|
5
|
+
import type { SignalRadarInboundMessage } from '@deepline/monitors/signal-radars/inbound-message';
|
|
6
|
+
|
|
7
|
+
export class SignalRadarInboundDeliveryError extends Error {
|
|
8
|
+
readonly cause_error: unknown;
|
|
9
|
+
|
|
10
|
+
constructor(cause: unknown) {
|
|
11
|
+
super(cause instanceof Error ? cause.message : String(cause));
|
|
12
|
+
this.name = 'SignalRadarInboundDeliveryError';
|
|
13
|
+
this.cause_error = cause;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function handleSignalRadarInboundDelivery(input: {
|
|
18
|
+
message: SignalRadarInboundMessage;
|
|
19
|
+
metadata: { deliveryCount: number; messageId?: string };
|
|
20
|
+
maxDeliveries: number;
|
|
21
|
+
runWorker: (
|
|
22
|
+
message: SignalRadarInboundMessage,
|
|
23
|
+
metadata: { deliveryCount: number; messageId?: string },
|
|
24
|
+
) => Promise<unknown>;
|
|
25
|
+
recordDeadLetter: (input: {
|
|
26
|
+
message: SignalRadarInboundMessage;
|
|
27
|
+
error: unknown;
|
|
28
|
+
deliveryCount: number;
|
|
29
|
+
}) => Promise<void>;
|
|
30
|
+
}) {
|
|
31
|
+
try {
|
|
32
|
+
await input.runWorker(input.message, input.metadata);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (
|
|
35
|
+
classifyInboundFailure(error) === 'permanent' &&
|
|
36
|
+
input.metadata.deliveryCount > input.maxDeliveries
|
|
37
|
+
) {
|
|
38
|
+
try {
|
|
39
|
+
await input.recordDeadLetter({
|
|
40
|
+
message: input.message,
|
|
41
|
+
error,
|
|
42
|
+
deliveryCount: input.metadata.deliveryCount,
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
} catch (recordError) {
|
|
46
|
+
throw new SignalRadarInboundDeliveryError(recordError);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw new SignalRadarInboundDeliveryError(error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function signalRadarInboundRetry(input: {
|
|
54
|
+
error: unknown;
|
|
55
|
+
deliveryCount: number;
|
|
56
|
+
}) {
|
|
57
|
+
// This function intentionally does no I/O. Vercel's retry callback is
|
|
58
|
+
// synchronous; terminal persistence belongs in the awaited handler above.
|
|
59
|
+
return { afterSeconds: inboundBackoffSeconds(input.deliveryCount) };
|
|
60
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { send as vercelQueueSend } from '@vercel/queue';
|
|
2
|
+
import type { SignalRadarInboundMessage } from '@deepline/monitors/signal-radars/inbound-message';
|
|
3
|
+
import { isTestOnlyRuntime } from '../../play-runtime/runtime-env';
|
|
4
|
+
|
|
5
|
+
// Durable Vercel Queues front-queue for Monitor inbound webhook events.
|
|
6
|
+
// The receiver produces onto this topic (auth -> enqueue -> 200); the air-gapped
|
|
7
|
+
// worker at apps/deepline-api/src/app/api/queues/signal-radar-inbound/route.ts consumes it and runs
|
|
8
|
+
// the full ingestion machinery. See docs/provider-monitor-playbook.md.
|
|
9
|
+
export const SIGNAL_RADAR_INBOUND_TOPIC = 'signal-radar-inbound';
|
|
10
|
+
|
|
11
|
+
// Retention set to the Vercel Queues maximum (7 days). This is the ceiling on
|
|
12
|
+
// the worst Neon/Convex outage the buffer can survive: while downstream is down,
|
|
13
|
+
// the receiver still returns 200 and the message sits here, redelivering until it
|
|
14
|
+
// drains or expires. Sized to exceed any tolerable Neon outage window.
|
|
15
|
+
export const SIGNAL_RADAR_INBOUND_RETENTION_SECONDS = 604_800;
|
|
16
|
+
|
|
17
|
+
export type SignalRadarInboundSendOptions = {
|
|
18
|
+
idempotencyKey?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Test-runtime delivery driver. The public receiver may set this only after
|
|
21
|
+
* proving the request is synthetic and test-authorized. It exercises the
|
|
22
|
+
* same queue worker locally, where Vercel Queues cannot mint its OIDC token.
|
|
23
|
+
*/
|
|
24
|
+
inlineTestWorker?: boolean;
|
|
25
|
+
runInlineTestWorker?: (message: SignalRadarInboundMessage) => Promise<unknown>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Publish onto the inbound topic. `idempotencyKey` is the Neon-free provider
|
|
29
|
+
// event key (belt-and-suspenders publish-side dedupe within the message TTL;
|
|
30
|
+
// the real guarantee is the worker's consumer-side dedupe on the same grain).
|
|
31
|
+
export async function send(
|
|
32
|
+
message: SignalRadarInboundMessage,
|
|
33
|
+
options?: SignalRadarInboundSendOptions,
|
|
34
|
+
) {
|
|
35
|
+
if (options?.inlineTestWorker) {
|
|
36
|
+
if (!isTestOnlyRuntime()) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
'The synthetic inbound queue driver is unavailable outside a test runtime.',
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
// The owning app supplies its real worker; packages cannot import app routes.
|
|
42
|
+
if (!options.runInlineTestWorker) {
|
|
43
|
+
throw new Error('The synthetic inbound queue driver requires an app worker.');
|
|
44
|
+
}
|
|
45
|
+
const result = await options.runInlineTestWorker(message);
|
|
46
|
+
return {
|
|
47
|
+
messageId: `test-inline:${options.idempotencyKey ?? crypto.randomUUID()}`,
|
|
48
|
+
// The test-only driver executes Tier 2 synchronously. Preserve its
|
|
49
|
+
// outcome for the synthetic receiver so a Gauntlet can assert a billing
|
|
50
|
+
// suspension without changing the real provider-facing queue contract.
|
|
51
|
+
result,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return vercelQueueSend(SIGNAL_RADAR_INBOUND_TOPIC, message, {
|
|
55
|
+
retentionSeconds: SIGNAL_RADAR_INBOUND_RETENTION_SECONDS,
|
|
56
|
+
...(options?.idempotencyKey
|
|
57
|
+
? { idempotencyKey: options.idempotencyKey }
|
|
58
|
+
: {}),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { lookup } from 'node:dns/promises';
|
|
2
2
|
import { isIP } from 'node:net';
|
|
3
|
-
import { DEEPLINE_ANALYTICS_BROWSER_OBJECT } from '
|
|
3
|
+
import { DEEPLINE_ANALYTICS_BROWSER_OBJECT } from '@deepline/monitors/analytics-public';
|
|
4
4
|
|
|
5
5
|
export type SnitcherInstallationCheck = {
|
|
6
6
|
domain: string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHmac, randomUUID } from 'node:crypto';
|
|
2
2
|
import { isIP } from 'node:net';
|
|
3
|
-
import { internal } from '@convex/
|
|
3
|
+
import { internal } from '@deepline/convex-client/function-references';
|
|
4
4
|
import { hasSnitcherSessionIdentity } from '@deepline/ingestion-v2/snitcher-session-identity';
|
|
5
|
-
import { getRequiredInternalConvexClient } from '
|
|
5
|
+
import { getRequiredInternalConvexClient } from '@deepline/convex-client/internal-client';
|
|
6
6
|
|
|
7
7
|
export { hasSnitcherSessionIdentity };
|
|
8
8
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildProviderWebhookCallbackUrl } from '@deepline/monitors/provider-webhook-monitor-runtime';
|
|
2
|
-
import { DEEPLINE_ANALYTICS_BROWSER_OBJECT } from '
|
|
2
|
+
import { DEEPLINE_ANALYTICS_BROWSER_OBJECT } from '@deepline/monitors/analytics-public';
|
|
3
3
|
|
|
4
4
|
const SNITCHER_RADAR_API_URL = 'https://api.snitcher.com/radar/operator/v1';
|
|
5
5
|
const SNITCHER_INTERNAL_IDENTIFIER_PREFIX = 'dlr1_';
|
|
@@ -2,7 +2,7 @@ import { TRACKING_PROVIDER_POST_DEPLOY_GUIDANCE } from '@deepline/monitors/track
|
|
|
2
2
|
import {
|
|
3
3
|
ANALYTICS_INTEGRATION_SLUG,
|
|
4
4
|
ANALYTICS_PUBLIC_TOOL,
|
|
5
|
-
} from '
|
|
5
|
+
} from '@deepline/monitors/analytics-public';
|
|
6
6
|
|
|
7
7
|
export const TRACKING_PROVIDER_INTEGRATIONS = {
|
|
8
8
|
vector: {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True while running inside a production build — Vercel/CI `next build` (where
|
|
3
|
+
* `CI=1`) and the prebuilt-artifact scripts, plus the prerender phase (where
|
|
4
|
+
* `NEXT_PHASE` is set). Use it to silence high-volume per-operation diagnostics
|
|
5
|
+
* that flood the build log with no build-time signal; they still emit at runtime
|
|
6
|
+
* and in local dev for Axiom/perf debugging.
|
|
7
|
+
*/
|
|
8
|
+
export function isBuildContext(): boolean {
|
|
9
|
+
return (
|
|
10
|
+
process.env.CI === '1' ||
|
|
11
|
+
process.env.NEXT_PHASE === 'phase-production-build'
|
|
12
|
+
);
|
|
13
|
+
}
|