deepline 0.3.88 → 0.3.89
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/dist/bundling-sources/sdk/src/client.ts +534 -4
- package/dist/bundling-sources/sdk/src/config.ts +77 -0
- package/dist/bundling-sources/sdk/src/http.ts +76 -30
- package/dist/bundling-sources/sdk/src/index.ts +6 -0
- package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +1 -1
- package/dist/bundling-sources/sdk/src/play.ts +15 -1
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +2 -2
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +83 -4
- package/dist/bundling-sources/shared_libs/integration-contracts/AGENTS.md +9 -0
- package/dist/bundling-sources/shared_libs/integration-contracts/README.md +3 -0
- package/dist/bundling-sources/shared_libs/integration-contracts/index.ts +2 -0
- package/dist/bundling-sources/shared_libs/monitors/analytics-install-guides.ts +77 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/charges.ts +125 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/policy-builders.ts +686 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/pricing.ts +54 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/public-pricing.ts +72 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/session.ts +45 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/state.ts +454 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/tamradar.ts +260 -0
- package/dist/bundling-sources/shared_libs/monitors/billing/types.ts +215 -0
- package/dist/bundling-sources/shared_libs/monitors/callback-security.ts +333 -0
- package/dist/bundling-sources/shared_libs/monitors/cursor-page.ts +42 -0
- package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +16 -0
- package/dist/bundling-sources/shared_libs/monitors/framework-managed-payload-fields.ts +71 -0
- package/dist/bundling-sources/shared_libs/monitors/generated/provider-monitor-callback-admission-targets.ts +16 -0
- package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +267 -0
- package/dist/bundling-sources/shared_libs/monitors/inbound-publish-key.ts +39 -0
- package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +82 -0
- package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +473 -0
- package/dist/bundling-sources/shared_libs/monitors/org-monitor-limits.ts +1 -1
- package/dist/bundling-sources/shared_libs/monitors/package.json +29 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-callback-verification.ts +1125 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +263 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-capability.ts +964 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-consumer-health.ts +56 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-errors.ts +131 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +535 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-legacy-aliases.ts +16 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-priority-execution.ts +56 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-public-aliases.ts +28 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-runtime-read-eligibility.ts +38 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-state.ts +122 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-status.ts +10 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-upstream-identity.ts +112 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-webhook-monitor-runtime.ts +300 -0
- package/dist/bundling-sources/shared_libs/monitors/quickstart-prompts.ts +22 -0
- package/dist/bundling-sources/shared_libs/monitors/snitcher-install-validation.ts +93 -0
- package/dist/bundling-sources/shared_libs/monitors/snitcher-ip2company.ts +332 -0
- package/dist/bundling-sources/shared_libs/monitors/snitcher-radar.ts +221 -0
- package/dist/bundling-sources/shared_libs/monitors/starter-templates.ts +105 -0
- package/dist/bundling-sources/shared_libs/monitors/tracking-provider-guidance.ts +73 -0
- package/dist/bundling-sources/shared_libs/monitors/tracking-provider-integrations.ts +81 -0
- package/dist/bundling-sources/shared_libs/monitors/tsconfig.json +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/activity-observation.ts +6 -11
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +90 -12
- package/dist/bundling-sources/shared_libs/play-runtime/async-play-invocation.ts +78 -0
- package/dist/bundling-sources/shared_libs/play-runtime/checkpoint-boundaries.ts +70 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +407 -53
- package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +1 -32
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +57 -0
- package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +20 -1
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-protocol.ts +7 -1
- package/dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts +189 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +28 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +23 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +18 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +68 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +10 -8
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +20 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +24 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +30 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +6 -3
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +454 -17
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +19 -4
- package/dist/bundling-sources/shared_libs/plays/contracts.ts +6 -14
- package/dist/bundling-sources/shared_libs/plays/core.ts +11 -6
- package/dist/bundling-sources/shared_libs/plays/docflow-binding-owner.ts +16 -11
- package/dist/bundling-sources/shared_libs/plays/email-status.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/resolve-static-pipeline.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/slug.ts +115 -0
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +44 -14
- package/dist/bundling-sources/shared_libs/plays/tool-category-descriptions.ts +2 -2
- package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +877 -0
- package/dist/bundling-sources/shared_libs/product-notifications/configuration-draft.ts +456 -0
- package/dist/bundling-sources/shared_libs/product-notifications/configuration.ts +560 -0
- package/dist/bundling-sources/shared_libs/product-notifications/content.ts +28 -0
- package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +59 -0
- package/dist/bundling-sources/shared_libs/product-notifications/email.ts +246 -0
- package/dist/bundling-sources/shared_libs/product-notifications/evaluation.ts +280 -0
- package/dist/bundling-sources/shared_libs/product-notifications/events.ts +433 -0
- package/dist/bundling-sources/shared_libs/product-notifications/legacy-slack.ts +80 -0
- package/dist/bundling-sources/shared_libs/product-notifications/workspace.ts +38 -0
- package/dist/bundling-sources/shared_libs/runtime-env.ts +6 -0
- package/dist/bundling-sources/shared_libs/security/header-safe-env.ts +28 -0
- package/dist/bundling-sources/shared_libs/security/internal-actor.ts +50 -0
- package/dist/bundling-sources/shared_libs/security/secret-redaction.ts +267 -0
- package/dist/cli/index.js +1070 -193
- package/dist/cli/index.mjs +1057 -180
- package/dist/{compiler-manifest-Dbov16F7.d.mts → compiler-manifest-DsLbQaew.d.mts} +132 -8
- package/dist/{compiler-manifest-Dbov16F7.d.ts → compiler-manifest-DsLbQaew.d.ts} +132 -8
- package/dist/index.d.mts +834 -12
- package/dist/index.d.ts +834 -12
- package/dist/index.js +469 -40
- package/dist/index.mjs +469 -40
- package/dist/install-integrity.json +65 -4
- package/dist/plays/bundle-play-file.d.mts +8 -3
- package/dist/plays/bundle-play-file.d.ts +8 -3
- package/dist/plays/bundle-play-file.mjs +100 -20
- package/package.json +3 -1
- /package/dist/bundling-sources/shared_libs/{integrations/bettercontact-execution-policy.ts → integration-contracts/bettercontact/execution-policy.ts} +0 -0
- /package/dist/bundling-sources/shared_libs/{integrations/theirstack-execution-policy.ts → integration-contracts/theirstack/execution-policy.ts} +0 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import {
|
|
3
|
+
PriorityMonitorExecutionLimitError,
|
|
4
|
+
ProviderMonitorCapacityBackfillInProgressError,
|
|
5
|
+
ProviderMonitorCapacityCleanupRequiredError,
|
|
6
|
+
ProviderMonitorIdentityBackfillInProgressError,
|
|
7
|
+
ProviderMonitorEntityLimitExceededError,
|
|
8
|
+
ProviderMonitorOrgLimitExceededError,
|
|
9
|
+
} from '@deepline/monitors/provider-monitor-errors';
|
|
10
|
+
import { MAX_PRIORITY_MONITORS_PER_ORG } from '@deepline/monitors/provider-monitor-priority-execution';
|
|
11
|
+
import { internal } from '@convex/_generated/api';
|
|
12
|
+
import { getRequiredInternalConvexClient } from '@/lib/convex/internal-client';
|
|
13
|
+
import {
|
|
14
|
+
providerMonitorUpstreamIdentitySignature,
|
|
15
|
+
type ProviderMonitorUpstreamIdentitySignature,
|
|
16
|
+
} from '@deepline/monitors/provider-monitor-upstream-identity';
|
|
17
|
+
|
|
18
|
+
export type ProviderMonitorIdentityClaim = {
|
|
19
|
+
tool: string;
|
|
20
|
+
identityKey: string;
|
|
21
|
+
identity: Record<string, string | null>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type OrgAuthContext = { orgId: string };
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The durable control-plane release operation needs only the opaque claim
|
|
28
|
+
* locator. Keep the display identity out of this boundary: Convex validates
|
|
29
|
+
* release arguments strictly so a rich conflict claim cannot turn a completed
|
|
30
|
+
* upstream delete into a failed local transition.
|
|
31
|
+
*/
|
|
32
|
+
export type ProviderMonitorIdentityClaimReference = Pick<
|
|
33
|
+
ProviderMonitorIdentityClaim,
|
|
34
|
+
'tool' | 'identityKey'
|
|
35
|
+
>;
|
|
36
|
+
|
|
37
|
+
export function providerMonitorIdentityClaimReference(
|
|
38
|
+
claim: ProviderMonitorIdentityClaim | null | undefined,
|
|
39
|
+
): ProviderMonitorIdentityClaimReference | null {
|
|
40
|
+
return claim ? { tool: claim.tool, identityKey: claim.identityKey } : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type ProviderMonitorIdentityReservation =
|
|
44
|
+
| {
|
|
45
|
+
kind: 'acquired';
|
|
46
|
+
claim: ProviderMonitorIdentityClaim;
|
|
47
|
+
restoredState: 'release' | 'active';
|
|
48
|
+
operationId: string;
|
|
49
|
+
}
|
|
50
|
+
| {
|
|
51
|
+
kind: 'occupied';
|
|
52
|
+
claim: ProviderMonitorIdentityClaim;
|
|
53
|
+
monitorKey: string;
|
|
54
|
+
state: 'provisioning' | 'active' | 'migration_conflict';
|
|
55
|
+
conflictingMonitorKeys?: string[];
|
|
56
|
+
}
|
|
57
|
+
| {
|
|
58
|
+
kind: 'in_progress';
|
|
59
|
+
claim: ProviderMonitorIdentityClaim;
|
|
60
|
+
monitorKey: string;
|
|
61
|
+
retryAt: number;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* An exact, per-monitor-key fence around an upstream lifecycle request.
|
|
66
|
+
* Unlike an identity claim, this applies even when a provider does not expose
|
|
67
|
+
* an account-unique identity.
|
|
68
|
+
*/
|
|
69
|
+
export type ProviderMonitorLifecycleReservation =
|
|
70
|
+
| {
|
|
71
|
+
kind: 'acquired';
|
|
72
|
+
state: 'provisioning' | 'deleting';
|
|
73
|
+
operationId: string;
|
|
74
|
+
}
|
|
75
|
+
| {
|
|
76
|
+
kind: 'in_progress';
|
|
77
|
+
state: 'provisioning' | 'deleting';
|
|
78
|
+
retryAt: number;
|
|
79
|
+
}
|
|
80
|
+
| {
|
|
81
|
+
kind: 'deleting';
|
|
82
|
+
state: 'deleting';
|
|
83
|
+
retryAt: number;
|
|
84
|
+
}
|
|
85
|
+
| {
|
|
86
|
+
kind: 'not_eligible';
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function record(value: unknown): Record<string, unknown> | null {
|
|
90
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
91
|
+
? (value as Record<string, unknown>)
|
|
92
|
+
: null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function finiteNumber(value: unknown): number | null {
|
|
96
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Convex owns lifecycle admission and validates every durable result. */
|
|
100
|
+
export function normalizeProviderMonitorLifecycleReservation(input: {
|
|
101
|
+
result: unknown;
|
|
102
|
+
state: 'provisioning' | 'deleting';
|
|
103
|
+
operationId: string;
|
|
104
|
+
}): ProviderMonitorLifecycleReservation {
|
|
105
|
+
const result = record(input.result);
|
|
106
|
+
if (!result || typeof result.kind !== 'string') {
|
|
107
|
+
throw new Error(
|
|
108
|
+
'Provider monitor lifecycle admission returned an invalid control-plane result.',
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
if (result.kind === 'acquired') {
|
|
112
|
+
return {
|
|
113
|
+
kind: 'acquired',
|
|
114
|
+
state: input.state,
|
|
115
|
+
operationId: input.operationId,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (result.kind === 'not_eligible') {
|
|
119
|
+
return { kind: 'not_eligible' };
|
|
120
|
+
}
|
|
121
|
+
if (
|
|
122
|
+
(result.kind !== 'in_progress' && result.kind !== 'deleting') ||
|
|
123
|
+
(result.state !== 'provisioning' && result.state !== 'deleting') ||
|
|
124
|
+
finiteNumber(result.retryAt) === null
|
|
125
|
+
) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
'Provider monitor lifecycle admission returned an unknown control-plane result.',
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
if (result.kind === 'deleting' && result.state !== 'deleting') {
|
|
131
|
+
throw new Error(
|
|
132
|
+
'Provider monitor lifecycle deletion admission returned an invalid operation state.',
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (result.kind === 'in_progress') {
|
|
136
|
+
return {
|
|
137
|
+
kind: 'in_progress',
|
|
138
|
+
state: result.state as 'provisioning' | 'deleting',
|
|
139
|
+
retryAt: result.retryAt as number,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
kind: 'deleting',
|
|
144
|
+
state: 'deleting',
|
|
145
|
+
retryAt: result.retryAt as number,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function claimFromSignature(
|
|
150
|
+
signature: ProviderMonitorUpstreamIdentitySignature,
|
|
151
|
+
): ProviderMonitorIdentityClaim {
|
|
152
|
+
const identity = Object.fromEntries(
|
|
153
|
+
Object.entries(signature.key).sort(([a], [b]) => a.localeCompare(b)),
|
|
154
|
+
);
|
|
155
|
+
return {
|
|
156
|
+
tool: signature.tool,
|
|
157
|
+
identityKey: JSON.stringify(identity),
|
|
158
|
+
identity,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function providerMonitorIdentityClaim(input: {
|
|
163
|
+
tool: string;
|
|
164
|
+
payload: Record<string, unknown> | null | undefined;
|
|
165
|
+
}) {
|
|
166
|
+
// Claims protect provider resources, including legacy rows whose public
|
|
167
|
+
// capability id was later retired. The Convex-safe identity contract owns
|
|
168
|
+
// those aliases; the live capability registry intentionally does not.
|
|
169
|
+
const signature = providerMonitorUpstreamIdentitySignature(
|
|
170
|
+
input.tool,
|
|
171
|
+
input.payload,
|
|
172
|
+
);
|
|
173
|
+
return signature ? claimFromSignature(signature) : null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Makes a legacy Convex org safe for indexed identity admission. A new org
|
|
178
|
+
* gets one bounded page inline so normal small workspaces stay transparent;
|
|
179
|
+
* a larger org continues in a durable, no-provider-side-effect Convex job and
|
|
180
|
+
* receives a retryable response rather than scanning tens of thousands of
|
|
181
|
+
* legacy definitions synchronously. Later calls are a single complete-marker
|
|
182
|
+
* read.
|
|
183
|
+
*/
|
|
184
|
+
export async function ensureProviderMonitorIdentityBackfill(input: {
|
|
185
|
+
auth: OrgAuthContext;
|
|
186
|
+
}) {
|
|
187
|
+
const client = getRequiredInternalConvexClient();
|
|
188
|
+
const globallyComplete = await client.query(
|
|
189
|
+
internal.providerMonitorIdentityClaims.globalBackfillComplete,
|
|
190
|
+
{},
|
|
191
|
+
);
|
|
192
|
+
if (globallyComplete) return;
|
|
193
|
+
const state = await client.query(
|
|
194
|
+
internal.providerMonitorIdentityClaims.backfillStatus,
|
|
195
|
+
{ orgId: input.auth.orgId },
|
|
196
|
+
);
|
|
197
|
+
if (state === 'complete') return;
|
|
198
|
+
|
|
199
|
+
if (state === 'not_started') {
|
|
200
|
+
// The marker/fence is committed before the first page reads the legacy
|
|
201
|
+
// index. Therefore every caller, including a rolling pre-claim app
|
|
202
|
+
// process, reaches the current Convex upsert and cannot slip an unclaimed
|
|
203
|
+
// identity behind this cursor.
|
|
204
|
+
await client.mutation(
|
|
205
|
+
internal.providerMonitorIdentityClaims.startBackfill,
|
|
206
|
+
{ orgId: input.auth.orgId, schedule: false },
|
|
207
|
+
);
|
|
208
|
+
const initialPage = await client.mutation(
|
|
209
|
+
internal.providerMonitorIdentityClaims.backfillNextPage,
|
|
210
|
+
{ orgId: input.auth.orgId },
|
|
211
|
+
);
|
|
212
|
+
if (initialPage.complete) return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const started = await client.mutation(
|
|
216
|
+
internal.providerMonitorIdentityClaims.startBackfill,
|
|
217
|
+
{ orgId: input.auth.orgId },
|
|
218
|
+
);
|
|
219
|
+
if (started.state === 'complete') return;
|
|
220
|
+
throw new ProviderMonitorIdentityBackfillInProgressError();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export async function getProviderMonitorIdentityOwner(input: {
|
|
224
|
+
auth: OrgAuthContext;
|
|
225
|
+
claim: ProviderMonitorIdentityClaim;
|
|
226
|
+
}) {
|
|
227
|
+
return getRequiredInternalConvexClient().query(
|
|
228
|
+
internal.providerMonitorIdentityClaims.get,
|
|
229
|
+
{
|
|
230
|
+
orgId: input.auth.orgId,
|
|
231
|
+
tool: input.claim.tool,
|
|
232
|
+
identityKey: input.claim.identityKey,
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function providerMonitorIdentityProvisioningForMonitor(input: {
|
|
238
|
+
auth: OrgAuthContext;
|
|
239
|
+
monitorKey: string;
|
|
240
|
+
}) {
|
|
241
|
+
return getRequiredInternalConvexClient().query(
|
|
242
|
+
internal.providerMonitorIdentityClaims.provisioningForMonitor,
|
|
243
|
+
{ orgId: input.auth.orgId, monitorKey: input.monitorKey },
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function priorityMonitorExecutionLimitFromLifecycleError(
|
|
248
|
+
error: unknown,
|
|
249
|
+
) {
|
|
250
|
+
if (
|
|
251
|
+
error instanceof Error &&
|
|
252
|
+
error.message.includes('PRIORITY_MONITOR_LIMIT_EXCEEDED')
|
|
253
|
+
) {
|
|
254
|
+
return new PriorityMonitorExecutionLimitError(
|
|
255
|
+
MAX_PRIORITY_MONITORS_PER_ORG,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Convex retries mutations itself. This is the bounded outer retry for the
|
|
262
|
+
// rare case in which that budget is exhausted while the short capacity-fold
|
|
263
|
+
// mutation is updating the same organization fence. Lifecycle admission is
|
|
264
|
+
// still pre-provider and reuses one operation id, so replaying it cannot make
|
|
265
|
+
// an upstream monitor, charge, or fence duplicate.
|
|
266
|
+
const LIFECYCLE_ADMISSION_WRITE_CONFLICT =
|
|
267
|
+
/write conflict|optimisticconcurrencycontrolfailure|documents read from or written to .* changed while this mutation was being run/i;
|
|
268
|
+
const MAX_LIFECYCLE_ADMISSION_RETRIES = 3;
|
|
269
|
+
|
|
270
|
+
function lifecycleAdmissionWriteConflict(error: unknown) {
|
|
271
|
+
const message =
|
|
272
|
+
error instanceof Error
|
|
273
|
+
? error.message
|
|
274
|
+
: typeof error === 'string'
|
|
275
|
+
? error
|
|
276
|
+
: '';
|
|
277
|
+
return LIFECYCLE_ADMISSION_WRITE_CONFLICT.test(message);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function sleep(ms: number) {
|
|
281
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export async function retryProviderMonitorLifecycleAdmission<T>(
|
|
285
|
+
operation: () => Promise<T>,
|
|
286
|
+
): Promise<T> {
|
|
287
|
+
for (
|
|
288
|
+
let attempt = 0;
|
|
289
|
+
attempt <= MAX_LIFECYCLE_ADMISSION_RETRIES;
|
|
290
|
+
attempt += 1
|
|
291
|
+
) {
|
|
292
|
+
try {
|
|
293
|
+
return await operation();
|
|
294
|
+
} catch (error) {
|
|
295
|
+
if (
|
|
296
|
+
!lifecycleAdmissionWriteConflict(error) ||
|
|
297
|
+
attempt === MAX_LIFECYCLE_ADMISSION_RETRIES
|
|
298
|
+
) {
|
|
299
|
+
throw error;
|
|
300
|
+
}
|
|
301
|
+
await sleep(25 * 2 ** attempt);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
throw new Error('Provider monitor lifecycle admission retry exhausted.');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export async function beginProviderMonitorLifecycleReservation(input: {
|
|
308
|
+
auth: OrgAuthContext;
|
|
309
|
+
monitorKey: string;
|
|
310
|
+
/**
|
|
311
|
+
* A durable bulk operation supplies its stable item attempt id. The
|
|
312
|
+
* ordinary scalar path intentionally leaves this unset and mints a fresh
|
|
313
|
+
* UUID. Reusing the same id lets a reclaimed batch refresh its own fence
|
|
314
|
+
* without ever turning an already-recorded provider intent into a second
|
|
315
|
+
* create.
|
|
316
|
+
*/
|
|
317
|
+
operationId?: string;
|
|
318
|
+
/** Tool and payload of the monitor being provisioned, so capacity can be
|
|
319
|
+
* counted against the account it watches rather than against the row. */
|
|
320
|
+
entityKey?: string | null;
|
|
321
|
+
state: 'provisioning' | 'deleting';
|
|
322
|
+
priorityExecutionSlots?: number;
|
|
323
|
+
expectedControlStateRevision?: number;
|
|
324
|
+
requiredMonitorStatus?: 'active';
|
|
325
|
+
}): Promise<ProviderMonitorLifecycleReservation> {
|
|
326
|
+
const operationId = input.operationId ?? randomUUID();
|
|
327
|
+
const action =
|
|
328
|
+
input.state === 'provisioning'
|
|
329
|
+
? internal.providerMonitorIdentityClaims.beginLifecycleProvisioning
|
|
330
|
+
: internal.providerMonitorIdentityClaims.beginLifecycleDeletion;
|
|
331
|
+
const request = () =>
|
|
332
|
+
getRequiredInternalConvexClient().mutation(action, {
|
|
333
|
+
orgId: input.auth.orgId,
|
|
334
|
+
monitorKey: input.monitorKey,
|
|
335
|
+
operationId,
|
|
336
|
+
...(input.entityKey ? { entityKey: input.entityKey } : {}),
|
|
337
|
+
...(input.priorityExecutionSlots === 1
|
|
338
|
+
? { priorityExecutionSlots: 1 }
|
|
339
|
+
: {}),
|
|
340
|
+
...(typeof input.expectedControlStateRevision === 'number'
|
|
341
|
+
? { expectedControlStateRevision: input.expectedControlStateRevision }
|
|
342
|
+
: {}),
|
|
343
|
+
...(input.requiredMonitorStatus
|
|
344
|
+
? { requiredMonitorStatus: input.requiredMonitorStatus }
|
|
345
|
+
: {}),
|
|
346
|
+
});
|
|
347
|
+
// A priority-slot refusal arrives as a thrown lifecycle error, while the
|
|
348
|
+
// capacity outcomes below arrive as a returned kind. Both paths have to work
|
|
349
|
+
// on the same call, and the backfill retry has to re-run the same request.
|
|
350
|
+
const attempt = async () => {
|
|
351
|
+
try {
|
|
352
|
+
return await request();
|
|
353
|
+
} catch (error) {
|
|
354
|
+
const priorityLimit =
|
|
355
|
+
priorityMonitorExecutionLimitFromLifecycleError(error);
|
|
356
|
+
if (priorityLimit) throw priorityLimit;
|
|
357
|
+
throw error;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
let result = await retryProviderMonitorLifecycleAdmission(attempt);
|
|
361
|
+
if (result.kind === 'backfill_required') {
|
|
362
|
+
await ensureProviderMonitorCapacityBackfill({ auth: input.auth });
|
|
363
|
+
result = await retryProviderMonitorLifecycleAdmission(attempt);
|
|
364
|
+
}
|
|
365
|
+
if (result.kind === 'limit_exceeded') {
|
|
366
|
+
throw new ProviderMonitorOrgLimitExceededError(result.capacity);
|
|
367
|
+
}
|
|
368
|
+
if (result.kind === 'entity_limit_exceeded') {
|
|
369
|
+
throw new ProviderMonitorEntityLimitExceededError(
|
|
370
|
+
result.entityKey,
|
|
371
|
+
result.capacity.monitors_per_entity,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
if (result.kind === 'cleanup_blocked') {
|
|
375
|
+
throw new ProviderMonitorCapacityCleanupRequiredError(input.monitorKey);
|
|
376
|
+
}
|
|
377
|
+
if (result.kind === 'backfill_required') {
|
|
378
|
+
throw new ProviderMonitorCapacityBackfillInProgressError();
|
|
379
|
+
}
|
|
380
|
+
return normalizeProviderMonitorLifecycleReservation({
|
|
381
|
+
result,
|
|
382
|
+
state: input.state,
|
|
383
|
+
operationId,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function ensureProviderMonitorCapacityBackfill(input: {
|
|
388
|
+
auth: OrgAuthContext;
|
|
389
|
+
}) {
|
|
390
|
+
const client = getRequiredInternalConvexClient();
|
|
391
|
+
const state = await client.query(
|
|
392
|
+
internal.providerMonitorCapacity.backfillStatus,
|
|
393
|
+
{ orgId: input.auth.orgId },
|
|
394
|
+
);
|
|
395
|
+
if (state === 'complete') return;
|
|
396
|
+
|
|
397
|
+
if (state === 'not_started') {
|
|
398
|
+
await client.mutation(internal.providerMonitorCapacity.startBackfill, {
|
|
399
|
+
orgId: input.auth.orgId,
|
|
400
|
+
schedule: false,
|
|
401
|
+
});
|
|
402
|
+
const initialPage = await client.mutation(
|
|
403
|
+
internal.providerMonitorCapacity.backfillNextPage,
|
|
404
|
+
{ orgId: input.auth.orgId },
|
|
405
|
+
);
|
|
406
|
+
if (initialPage.complete) return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const started = await client.mutation(
|
|
410
|
+
internal.providerMonitorCapacity.startBackfill,
|
|
411
|
+
{ orgId: input.auth.orgId },
|
|
412
|
+
);
|
|
413
|
+
if (started.state === 'complete') return;
|
|
414
|
+
throw new ProviderMonitorCapacityBackfillInProgressError();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function failProviderMonitorLifecycleReservation(input: {
|
|
418
|
+
auth: OrgAuthContext;
|
|
419
|
+
monitorKey: string;
|
|
420
|
+
reservation: Extract<
|
|
421
|
+
ProviderMonitorLifecycleReservation,
|
|
422
|
+
{ kind: 'acquired' }
|
|
423
|
+
>;
|
|
424
|
+
}) {
|
|
425
|
+
return getRequiredInternalConvexClient().mutation(
|
|
426
|
+
internal.providerMonitorIdentityClaims.failLifecycleOperation,
|
|
427
|
+
{
|
|
428
|
+
orgId: input.auth.orgId,
|
|
429
|
+
monitorKey: input.monitorKey,
|
|
430
|
+
state: input.reservation.state,
|
|
431
|
+
operationId: input.reservation.operationId,
|
|
432
|
+
},
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export async function beginProviderMonitorIdentityReservation(input: {
|
|
437
|
+
auth: OrgAuthContext;
|
|
438
|
+
monitorKey: string;
|
|
439
|
+
tool: string;
|
|
440
|
+
payload: Record<string, unknown> | null | undefined;
|
|
441
|
+
/**
|
|
442
|
+
* Resume rather than wait when THIS monitor key already holds a provisioning
|
|
443
|
+
* claim. Only for callers that serialise their own passes for a member -
|
|
444
|
+
* see `reclaimSelfProvisioning` on the mutation.
|
|
445
|
+
*/
|
|
446
|
+
reclaimSelfProvisioning?: boolean;
|
|
447
|
+
}): Promise<ProviderMonitorIdentityReservation | null> {
|
|
448
|
+
const claim = providerMonitorIdentityClaim(input);
|
|
449
|
+
if (!claim) return null;
|
|
450
|
+
await ensureProviderMonitorIdentityBackfill({ auth: input.auth });
|
|
451
|
+
const operationId = randomUUID();
|
|
452
|
+
const result = await getRequiredInternalConvexClient().mutation(
|
|
453
|
+
internal.providerMonitorIdentityClaims.begin,
|
|
454
|
+
{
|
|
455
|
+
orgId: input.auth.orgId,
|
|
456
|
+
tool: claim.tool,
|
|
457
|
+
identityKey: claim.identityKey,
|
|
458
|
+
monitorKey: input.monitorKey,
|
|
459
|
+
operationId,
|
|
460
|
+
...(input.reclaimSelfProvisioning
|
|
461
|
+
? { reclaimSelfProvisioning: true }
|
|
462
|
+
: {}),
|
|
463
|
+
},
|
|
464
|
+
);
|
|
465
|
+
if (result.kind === 'acquired') {
|
|
466
|
+
return {
|
|
467
|
+
kind: 'acquired',
|
|
468
|
+
claim,
|
|
469
|
+
restoredState: result.restoredState,
|
|
470
|
+
operationId,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
if (result.kind === 'occupied') {
|
|
474
|
+
return { ...result, claim };
|
|
475
|
+
}
|
|
476
|
+
return { ...result, claim };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async function mutateClaim(
|
|
480
|
+
action: 'activate' | 'release',
|
|
481
|
+
input: {
|
|
482
|
+
auth: OrgAuthContext;
|
|
483
|
+
monitorKey: string;
|
|
484
|
+
claim: ProviderMonitorIdentityClaim;
|
|
485
|
+
operationId?: string;
|
|
486
|
+
},
|
|
487
|
+
) {
|
|
488
|
+
return getRequiredInternalConvexClient().mutation(
|
|
489
|
+
internal.providerMonitorIdentityClaims[action],
|
|
490
|
+
{
|
|
491
|
+
orgId: input.auth.orgId,
|
|
492
|
+
tool: input.claim.tool,
|
|
493
|
+
identityKey: input.claim.identityKey,
|
|
494
|
+
monitorKey: input.monitorKey,
|
|
495
|
+
...(input.operationId ? { operationId: input.operationId } : {}),
|
|
496
|
+
},
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export function activateProviderMonitorIdentityReservation(input: {
|
|
501
|
+
auth: OrgAuthContext;
|
|
502
|
+
monitorKey: string;
|
|
503
|
+
claim: ProviderMonitorIdentityClaim;
|
|
504
|
+
operationId: string;
|
|
505
|
+
}) {
|
|
506
|
+
return mutateClaim('activate', input);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export function releaseProviderMonitorIdentityClaim(input: {
|
|
510
|
+
auth: OrgAuthContext;
|
|
511
|
+
monitorKey: string;
|
|
512
|
+
claim: ProviderMonitorIdentityClaim;
|
|
513
|
+
}) {
|
|
514
|
+
return mutateClaim('release', input);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export function failProviderMonitorIdentityReservation(input: {
|
|
518
|
+
auth: OrgAuthContext;
|
|
519
|
+
monitorKey: string;
|
|
520
|
+
reservation: Extract<
|
|
521
|
+
ProviderMonitorIdentityReservation,
|
|
522
|
+
{ kind: 'acquired' }
|
|
523
|
+
>;
|
|
524
|
+
}) {
|
|
525
|
+
return getRequiredInternalConvexClient().mutation(
|
|
526
|
+
internal.providerMonitorIdentityClaims.fail,
|
|
527
|
+
{
|
|
528
|
+
orgId: input.auth.orgId,
|
|
529
|
+
tool: input.reservation.claim.tool,
|
|
530
|
+
identityKey: input.reservation.claim.identityKey,
|
|
531
|
+
monitorKey: input.monitorKey,
|
|
532
|
+
operationId: input.reservation.operationId,
|
|
533
|
+
},
|
|
534
|
+
);
|
|
535
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const LEGACY_STORED_PROVIDER_MONITOR_CAPABILITY_ALIASES: Readonly<
|
|
2
|
+
Record<string, string>
|
|
3
|
+
> = {
|
|
4
|
+
'tamradar.company_radar': 'deepline_native.company_radar',
|
|
5
|
+
'tamradar.contact_radar': 'deepline_native.contact_radar',
|
|
6
|
+
'tamradar.industry_radar': 'deepline_native.industry_radar',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function canonicalizeStoredProviderMonitorCapabilityId(
|
|
10
|
+
providerCapability: string,
|
|
11
|
+
): string {
|
|
12
|
+
return (
|
|
13
|
+
LEGACY_STORED_PROVIDER_MONITOR_CAPABILITY_ALIASES[providerCapability] ??
|
|
14
|
+
providerCapability
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ProviderMonitorCapabilityDefinition } from '@deepline/monitors/provider-monitor-capability';
|
|
2
|
+
import type { ProviderMonitorStatus } from './provider-monitor-status';
|
|
3
|
+
|
|
4
|
+
export const PRIORITY_MONITOR_EXECUTION_TYPE = 'priority' as const;
|
|
5
|
+
export const MAX_PRIORITY_MONITORS_PER_ORG = 10;
|
|
6
|
+
|
|
7
|
+
export const PRIORITY_MONITOR_SLOT_STATUSES = [
|
|
8
|
+
'active',
|
|
9
|
+
// A billing suspension writes disabled before upstream deprovisioning. Keep
|
|
10
|
+
// the slot occupied until that delete confirms success and clears the
|
|
11
|
+
// projection; a failed cleanup can leave the priority radar live.
|
|
12
|
+
'disabled',
|
|
13
|
+
'deleting',
|
|
14
|
+
'error',
|
|
15
|
+
'deleted',
|
|
16
|
+
// A fleet monitor holds its priority reservation from the commit that
|
|
17
|
+
// records the desired state, not from the provider create that follows it.
|
|
18
|
+
'pending_create',
|
|
19
|
+
] as const satisfies readonly ProviderMonitorStatus[];
|
|
20
|
+
|
|
21
|
+
const PRIORITY_MONITOR_SLOT_STATUS_SET = new Set<ProviderMonitorStatus>(
|
|
22
|
+
PRIORITY_MONITOR_SLOT_STATUSES,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export function requestedPriorityMonitorExecution(definition: {
|
|
26
|
+
controls?: Record<string, unknown>;
|
|
27
|
+
}) {
|
|
28
|
+
return (
|
|
29
|
+
definition.controls?.execution_type === PRIORITY_MONITOR_EXECUTION_TYPE
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function priorityMonitorExecutionIsSupported(
|
|
34
|
+
capability: ProviderMonitorCapabilityDefinition,
|
|
35
|
+
) {
|
|
36
|
+
return capability.provider === 'deepline_native';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function priorityMonitorConsumesSlot(input: {
|
|
40
|
+
priorityExecution: boolean;
|
|
41
|
+
status: ProviderMonitorStatus;
|
|
42
|
+
}) {
|
|
43
|
+
return (
|
|
44
|
+
input.priorityExecution &&
|
|
45
|
+
PRIORITY_MONITOR_SLOT_STATUS_SET.has(input.status)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function priorityMonitorReservedSlotCount(input: {
|
|
50
|
+
priorityExecution: boolean;
|
|
51
|
+
status: ProviderMonitorStatus;
|
|
52
|
+
priorityExecutionSlots?: number;
|
|
53
|
+
}) {
|
|
54
|
+
if (!priorityMonitorConsumesSlot(input)) return 0;
|
|
55
|
+
return input.priorityExecutionSlots === 2 ? 2 : 1;
|
|
56
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ANALYTICS_INTEGRATION_SLUG,
|
|
3
|
+
ANALYTICS_PUBLIC_PROVIDER,
|
|
4
|
+
ANALYTICS_PUBLIC_TOOL,
|
|
5
|
+
} from '@/lib/analytics/public';
|
|
6
|
+
|
|
7
|
+
export const ANALYTICS_MONITOR_KEY = ANALYTICS_INTEGRATION_SLUG;
|
|
8
|
+
export const ANALYTICS_INTERNAL_MONITOR_KEY = 'snitcher-website-sessions';
|
|
9
|
+
export const ANALYTICS_INTERNAL_TOOL = 'snitcher.website_sessions';
|
|
10
|
+
|
|
11
|
+
export function canonicalProviderMonitorKey(key: string) {
|
|
12
|
+
return key === ANALYTICS_MONITOR_KEY ? ANALYTICS_INTERNAL_MONITOR_KEY : key;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Convert a customer-facing monitor tool id to its internal registry id. */
|
|
16
|
+
export function canonicalProviderMonitorTool(tool: string) {
|
|
17
|
+
return tool === ANALYTICS_PUBLIC_TOOL ? ANALYTICS_INTERNAL_TOOL : tool;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Convert an internal registry id to the stable customer-facing tool id. */
|
|
21
|
+
export function publicProviderMonitorTool(tool: string) {
|
|
22
|
+
return tool === ANALYTICS_INTERNAL_TOOL ? ANALYTICS_PUBLIC_TOOL : tool;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Convert an internal provider name to the stable customer-facing name. */
|
|
26
|
+
export function publicProviderMonitorProvider(provider: string) {
|
|
27
|
+
return provider === 'snitcher' ? ANALYTICS_PUBLIC_PROVIDER : provider;
|
|
28
|
+
}
|
package/dist/bundling-sources/shared_libs/monitors/provider-monitor-runtime-read-eligibility.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
type ProviderMonitorCallbackRuntimeRow = {
|
|
2
|
+
status: string;
|
|
3
|
+
billing_state: unknown;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
function billingStateKind(value: unknown) {
|
|
7
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
8
|
+
const state = (value as Record<string, unknown>).state;
|
|
9
|
+
return typeof state === 'string' && state.trim() ? state.trim() : null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** True when an admitted callback is allowed to write an event for this monitor. */
|
|
13
|
+
export function isProviderMonitorCallbackWriteEnabled(
|
|
14
|
+
row: Pick<ProviderMonitorCallbackRuntimeRow, 'status'>,
|
|
15
|
+
) {
|
|
16
|
+
return row.status === 'active';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Matches the Customer DB callback lookup predicate for a row that has already
|
|
21
|
+
* been selected from the Convex callback-token projection.
|
|
22
|
+
*/
|
|
23
|
+
export function isProviderMonitorCallbackRuntimeEligible(
|
|
24
|
+
row: ProviderMonitorCallbackRuntimeRow,
|
|
25
|
+
) {
|
|
26
|
+
if (isProviderMonitorCallbackWriteEnabled(row)) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return (
|
|
30
|
+
row.status === 'disabled' &&
|
|
31
|
+
(billingStateKind(row.billing_state) === 'upstream_disable_pending' ||
|
|
32
|
+
billingStateKind(row.billing_state) === 'upstream_disable_failed' ||
|
|
33
|
+
// A credit suspension turns the provider resource off but does not turn
|
|
34
|
+
// off receipt capture. The inbound processor stores raw provenance and
|
|
35
|
+
// returns its explicit billing-suspended outcome without typed writes.
|
|
36
|
+
billingStateKind(row.billing_state) === 'billing_suspended')
|
|
37
|
+
);
|
|
38
|
+
}
|