deepline 0.3.87 → 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 +532 -2
- 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/play.ts +15 -1
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
- 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/monitors/deactivation-reasons.ts +16 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +263 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +9 -1
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-state.ts +122 -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-scheduler-topology.ts +24 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +30 -1
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +6 -3
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +152 -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/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/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/security/internal-actor.ts +50 -0
- package/dist/cli/index.js +1065 -190
- package/dist/cli/index.mjs +1052 -177
- 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 +833 -11
- package/dist/index.d.ts +833 -11
- package/dist/index.js +467 -38
- package/dist/index.mjs +467 -38
- package/dist/install-integrity.json +20 -2
- 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 +1 -1
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, provider-agnostic vocabulary for a durable monitor operation.
|
|
3
|
+
*
|
|
4
|
+
* A batch operation is deliberately a small control document. It names a
|
|
5
|
+
* stable scope and a retry key; monitor definitions remain the source of
|
|
6
|
+
* truth for every individual item and the retained operation evidence is only
|
|
7
|
+
* a log of what the runner observed or did. Keeping this vocabulary pure
|
|
8
|
+
* makes CLI/API parsing, Convex admission and the background runner agree on
|
|
9
|
+
* the exact same request identity.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { requireMonitorDeactivationReason } from './deactivation-reasons';
|
|
13
|
+
|
|
14
|
+
export const PROVIDER_MONITOR_BATCH_OPERATION_KINDS = [
|
|
15
|
+
'deploy',
|
|
16
|
+
'deactivate',
|
|
17
|
+
'reactivate',
|
|
18
|
+
'audit',
|
|
19
|
+
'repair',
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
export type ProviderMonitorBatchOperationKind =
|
|
23
|
+
(typeof PROVIDER_MONITOR_BATCH_OPERATION_KINDS)[number];
|
|
24
|
+
|
|
25
|
+
export const PROVIDER_MONITOR_BATCH_TARGETS = [
|
|
26
|
+
'all',
|
|
27
|
+
'all-active',
|
|
28
|
+
'billing-suspended',
|
|
29
|
+
'needs-repair',
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
export type ProviderMonitorBatchTarget =
|
|
33
|
+
(typeof PROVIDER_MONITOR_BATCH_TARGETS)[number];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A manifest lives in durable blob storage. The runner reads it in bounded
|
|
37
|
+
* pages; never embed its definitions or a growing result array in a run
|
|
38
|
+
* record. `count` is declared at upload time and is used for honest progress
|
|
39
|
+
* before the first item has been processed.
|
|
40
|
+
*/
|
|
41
|
+
export type ProviderMonitorBatchManifestScope = {
|
|
42
|
+
kind: 'manifest';
|
|
43
|
+
storageId: string;
|
|
44
|
+
count: number;
|
|
45
|
+
/** Content address used for idempotent retries; storage ids are ephemeral. */
|
|
46
|
+
sha256?: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type ProviderMonitorBatchScope =
|
|
50
|
+
| { kind: 'keys'; monitorKeys: readonly string[] }
|
|
51
|
+
| { kind: 'target'; target: ProviderMonitorBatchTarget }
|
|
52
|
+
| { kind: 'fleet'; fleetId: string }
|
|
53
|
+
| ProviderMonitorBatchManifestScope;
|
|
54
|
+
|
|
55
|
+
export type ProviderMonitorBatchOperationRequest = {
|
|
56
|
+
kind: ProviderMonitorBatchOperationKind;
|
|
57
|
+
scope: ProviderMonitorBatchScope;
|
|
58
|
+
dryRun: boolean;
|
|
59
|
+
/** Required for deactivate; absent for every other operation. */
|
|
60
|
+
deactivationReason?: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type ProviderMonitorBatchOperationState =
|
|
64
|
+
| 'running'
|
|
65
|
+
| 'retry_scheduled'
|
|
66
|
+
| 'completed'
|
|
67
|
+
| 'failed';
|
|
68
|
+
|
|
69
|
+
const IDEMPOTENCY_KEY = /^[A-Za-z0-9][A-Za-z0-9._-]{0,95}$/;
|
|
70
|
+
|
|
71
|
+
function canonicalJson(value: unknown): string {
|
|
72
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
|
|
73
|
+
if (value && typeof value === 'object') {
|
|
74
|
+
return `{${Object.entries(value as Record<string, unknown>)
|
|
75
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
76
|
+
.map(([key, child]) => `${JSON.stringify(key)}:${canonicalJson(child)}`)
|
|
77
|
+
.join(',')}}`;
|
|
78
|
+
}
|
|
79
|
+
return JSON.stringify(value);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A job id is deterministic on purpose. Convex's existing `by_run` point
|
|
84
|
+
* lookup is then the idempotency index: the same org/key can only own one run,
|
|
85
|
+
* and its canonical request below decides whether it is a rejoin or conflict.
|
|
86
|
+
*/
|
|
87
|
+
export function providerMonitorBatchOperationId(input: {
|
|
88
|
+
orgId: string;
|
|
89
|
+
idempotencyKey: string;
|
|
90
|
+
}): string {
|
|
91
|
+
const orgId = input.orgId.trim();
|
|
92
|
+
const idempotencyKey = input.idempotencyKey.trim();
|
|
93
|
+
if (!orgId)
|
|
94
|
+
throw new Error('A monitor batch operation requires an organization.');
|
|
95
|
+
if (!IDEMPOTENCY_KEY.test(idempotencyKey)) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
'idempotencyKey must be 1-96 characters of letters, numbers, dot, underscore, or hyphen.',
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return `monitor-batch:${orgId}:${idempotencyKey}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function isProviderMonitorBatchOperationKind(
|
|
104
|
+
value: string,
|
|
105
|
+
): value is ProviderMonitorBatchOperationKind {
|
|
106
|
+
return (PROVIDER_MONITOR_BATCH_OPERATION_KINDS as readonly string[]).includes(
|
|
107
|
+
value,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function isProviderMonitorBatchTarget(
|
|
112
|
+
value: string,
|
|
113
|
+
): value is ProviderMonitorBatchTarget {
|
|
114
|
+
return (PROVIDER_MONITOR_BATCH_TARGETS as readonly string[]).includes(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Validate the representation shared by all entry points before any state is
|
|
119
|
+
* mutated. A request chooses exactly one selection mechanism. Key lists are
|
|
120
|
+
* kept intentionally small because a large explicit selection is a manifest,
|
|
121
|
+
* not an unbounded mutation argument.
|
|
122
|
+
*/
|
|
123
|
+
export function validateProviderMonitorBatchOperationRequest(
|
|
124
|
+
request: ProviderMonitorBatchOperationRequest,
|
|
125
|
+
): ProviderMonitorBatchOperationRequest {
|
|
126
|
+
if (!isProviderMonitorBatchOperationKind(request.kind)) {
|
|
127
|
+
throw new Error(`Unknown monitor batch operation '${request.kind}'.`);
|
|
128
|
+
}
|
|
129
|
+
if (!request.scope || typeof request.scope !== 'object') {
|
|
130
|
+
throw new Error('A monitor batch operation requires exactly one scope.');
|
|
131
|
+
}
|
|
132
|
+
const scope = request.scope;
|
|
133
|
+
if (scope.kind === 'keys') {
|
|
134
|
+
const keys = [...new Set(scope.monitorKeys.map((key) => key.trim()))];
|
|
135
|
+
if (
|
|
136
|
+
keys.length === 0 ||
|
|
137
|
+
keys.length !== scope.monitorKeys.length ||
|
|
138
|
+
keys.some((key) => key.length === 0)
|
|
139
|
+
) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
'Monitor key scopes must contain unique, non-empty keys.',
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (keys.length > 100) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
'An explicit monitor batch contains at most 100 keys. Upload a manifest for larger selections.',
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (request.kind === 'deploy') {
|
|
150
|
+
throw new Error(
|
|
151
|
+
'Batch deploy requires a durable manifest of complete definitions.',
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
if (request.kind === 'deactivate' && !request.deactivationReason) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
'Batch deactivate requires a declared deactivation reason.',
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (request.kind === 'deactivate') {
|
|
160
|
+
requireMonitorDeactivationReason(request.deactivationReason!);
|
|
161
|
+
}
|
|
162
|
+
return { ...request, scope: { kind: 'keys', monitorKeys: keys } };
|
|
163
|
+
}
|
|
164
|
+
if (scope.kind === 'target') {
|
|
165
|
+
if (request.kind === 'deploy') {
|
|
166
|
+
throw new Error(
|
|
167
|
+
'Batch deploy requires a durable manifest of complete definitions.',
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (!isProviderMonitorBatchTarget(scope.target)) {
|
|
171
|
+
throw new Error(`Unknown monitor batch target '${scope.target}'.`);
|
|
172
|
+
}
|
|
173
|
+
if (request.kind === 'deactivate' && !request.deactivationReason) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
'Batch deactivate requires a declared deactivation reason.',
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
if (request.kind === 'deactivate') {
|
|
179
|
+
requireMonitorDeactivationReason(request.deactivationReason!);
|
|
180
|
+
}
|
|
181
|
+
return request;
|
|
182
|
+
}
|
|
183
|
+
if (scope.kind === 'fleet') {
|
|
184
|
+
if (request.kind === 'deploy') {
|
|
185
|
+
throw new Error(
|
|
186
|
+
'Batch deploy requires a durable manifest of complete definitions.',
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
if (!scope.fleetId.trim())
|
|
190
|
+
throw new Error('A Fleet scope requires fleetId.');
|
|
191
|
+
if (request.kind === 'deactivate' && !request.deactivationReason) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
'Batch deactivate requires a declared deactivation reason.',
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
if (request.kind === 'deactivate') {
|
|
197
|
+
requireMonitorDeactivationReason(request.deactivationReason!);
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
...request,
|
|
201
|
+
scope: { kind: 'fleet', fleetId: scope.fleetId.trim() },
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (scope.kind === 'manifest') {
|
|
205
|
+
if (
|
|
206
|
+
!scope.storageId.trim() ||
|
|
207
|
+
!Number.isSafeInteger(scope.count) ||
|
|
208
|
+
scope.count < 1
|
|
209
|
+
) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
'A manifest scope requires a storage id and a positive item count.',
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
if (request.kind !== 'deploy') {
|
|
215
|
+
throw new Error('Only batch deploy accepts a manifest scope.');
|
|
216
|
+
}
|
|
217
|
+
if (
|
|
218
|
+
scope.sha256 !== undefined &&
|
|
219
|
+
!/^[a-f0-9]{64}$/i.test(scope.sha256.trim())
|
|
220
|
+
) {
|
|
221
|
+
throw new Error('A manifest sha256 must be a 64-character hex digest.');
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
...request,
|
|
225
|
+
scope: {
|
|
226
|
+
kind: 'manifest',
|
|
227
|
+
storageId: scope.storageId.trim(),
|
|
228
|
+
count: scope.count,
|
|
229
|
+
...(scope.sha256 === undefined
|
|
230
|
+
? {}
|
|
231
|
+
: { sha256: scope.sha256.trim().toLowerCase() }),
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
throw new Error(
|
|
236
|
+
'A monitor batch operation requires exactly one supported scope.',
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* The persisted fingerprint is an equality proof, not a display hash. It is
|
|
242
|
+
* compact for normal target/key requests, collision-free, and avoids a Node
|
|
243
|
+
* crypto dependency in code shared with Convex.
|
|
244
|
+
*/
|
|
245
|
+
export function providerMonitorBatchOperationRequestFingerprint(
|
|
246
|
+
request: ProviderMonitorBatchOperationRequest,
|
|
247
|
+
): string {
|
|
248
|
+
const normalized = validateProviderMonitorBatchOperationRequest(request);
|
|
249
|
+
// A storage id is an implementation detail of an uploaded immutable file.
|
|
250
|
+
// Re-uploading identical bytes after a lost HTTP response must rejoin the
|
|
251
|
+
// same operation, not conflict merely because storage issued another id.
|
|
252
|
+
if (normalized.scope.kind === 'manifest' && normalized.scope.sha256) {
|
|
253
|
+
return canonicalJson({
|
|
254
|
+
...normalized,
|
|
255
|
+
scope: {
|
|
256
|
+
kind: 'manifest',
|
|
257
|
+
count: normalized.scope.count,
|
|
258
|
+
sha256: normalized.scope.sha256,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return canonicalJson(normalized);
|
|
263
|
+
}
|
|
@@ -307,6 +307,14 @@ export async function retryProviderMonitorLifecycleAdmission<T>(
|
|
|
307
307
|
export async function beginProviderMonitorLifecycleReservation(input: {
|
|
308
308
|
auth: OrgAuthContext;
|
|
309
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;
|
|
310
318
|
/** Tool and payload of the monitor being provisioned, so capacity can be
|
|
311
319
|
* counted against the account it watches rather than against the row. */
|
|
312
320
|
entityKey?: string | null;
|
|
@@ -315,7 +323,7 @@ export async function beginProviderMonitorLifecycleReservation(input: {
|
|
|
315
323
|
expectedControlStateRevision?: number;
|
|
316
324
|
requiredMonitorStatus?: 'active';
|
|
317
325
|
}): Promise<ProviderMonitorLifecycleReservation> {
|
|
318
|
-
const operationId = randomUUID();
|
|
326
|
+
const operationId = input.operationId ?? randomUUID();
|
|
319
327
|
const action =
|
|
320
328
|
input.state === 'provisioning'
|
|
321
329
|
? internal.providerMonitorIdentityClaims.beginLifecycleProvisioning
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The customer-visible state of a monitor. Provider work and audit evidence
|
|
3
|
+
* intentionally live on separate axes; neither may add a third value here.
|
|
4
|
+
*/
|
|
5
|
+
export type ProviderMonitorState = 'active' | 'inactive';
|
|
6
|
+
|
|
7
|
+
declare const providerMonitorResourceIdBrand: unique symbol;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A non-empty provider-owned resource handle. This deliberately says
|
|
11
|
+
* nothing about the provider or its wire format: an adapter is the only
|
|
12
|
+
* layer that knows whether this happens to be a radar, subscription, etc.
|
|
13
|
+
*/
|
|
14
|
+
export type ProviderMonitorResourceId = string & {
|
|
15
|
+
readonly [providerMonitorResourceIdBrand]: 'ProviderMonitorResourceId';
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Turn untrusted storage/API input into the proof used by active state. */
|
|
19
|
+
export function providerMonitorResourceId(
|
|
20
|
+
value: string | null | undefined,
|
|
21
|
+
): ProviderMonitorResourceId | null {
|
|
22
|
+
const normalized = typeof value === 'string' ? value.trim() : '';
|
|
23
|
+
return normalized === '' ? null : (normalized as ProviderMonitorResourceId);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A provider-neutral proof that this control record has an upstream
|
|
28
|
+
* counterpart. The identifier might be a radar id today and a different
|
|
29
|
+
* provider's resource id tomorrow; callers outside an adapter must never need
|
|
30
|
+
* to know which.
|
|
31
|
+
*/
|
|
32
|
+
export type ProviderMonitorResourceBinding =
|
|
33
|
+
| { kind: 'bound'; providerResourceId: ProviderMonitorResourceId }
|
|
34
|
+
| { kind: 'unbound'; providerResourceId: null };
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The state safe to show as a monitor's current operational state.
|
|
38
|
+
*
|
|
39
|
+
* An active intent without a resource is valid *work* (a create is pending or
|
|
40
|
+
* a historical record needs repair), but it is not an active monitor. The
|
|
41
|
+
* discriminated `active` branch therefore proves a non-empty provider id to
|
|
42
|
+
* TypeScript at every API/worker boundary that administers a live resource.
|
|
43
|
+
*/
|
|
44
|
+
export type OperationalProviderMonitorState =
|
|
45
|
+
| {
|
|
46
|
+
state: 'active';
|
|
47
|
+
desired: 'active';
|
|
48
|
+
providerResourceId: ProviderMonitorResourceId;
|
|
49
|
+
binding: 'bound';
|
|
50
|
+
}
|
|
51
|
+
| {
|
|
52
|
+
state: 'inactive';
|
|
53
|
+
desired: ProviderMonitorState;
|
|
54
|
+
providerResourceId: string | null;
|
|
55
|
+
binding: 'bound' | 'unbound';
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type ActiveOperationalProviderMonitorState = Extract<
|
|
59
|
+
OperationalProviderMonitorState,
|
|
60
|
+
{ state: 'active' }
|
|
61
|
+
>;
|
|
62
|
+
|
|
63
|
+
export function providerMonitorOperationalState(input: {
|
|
64
|
+
desired: ProviderMonitorState;
|
|
65
|
+
providerResourceId: string | null | undefined;
|
|
66
|
+
}): OperationalProviderMonitorState {
|
|
67
|
+
const providerResourceId = providerMonitorResourceId(input.providerResourceId);
|
|
68
|
+
if (input.desired === 'active' && providerResourceId !== null) {
|
|
69
|
+
return {
|
|
70
|
+
state: 'active',
|
|
71
|
+
desired: 'active',
|
|
72
|
+
providerResourceId,
|
|
73
|
+
binding: 'bound',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
state: 'inactive',
|
|
78
|
+
desired: input.desired,
|
|
79
|
+
providerResourceId,
|
|
80
|
+
binding: providerResourceId === null ? 'unbound' : 'bound',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Runtime boundary for code that is about to publish or administer an active
|
|
86
|
+
* resource. Its return type is the same proof exposed to callers: an active
|
|
87
|
+
* record necessarily has a non-empty, provider-neutral resource id.
|
|
88
|
+
*/
|
|
89
|
+
export function requireActiveProviderMonitorBinding(input: {
|
|
90
|
+
providerResourceId: string | null | undefined;
|
|
91
|
+
}): ActiveOperationalProviderMonitorState {
|
|
92
|
+
const operational = providerMonitorOperationalState({
|
|
93
|
+
desired: 'active',
|
|
94
|
+
providerResourceId: input.providerResourceId,
|
|
95
|
+
});
|
|
96
|
+
if (operational.state !== 'active') {
|
|
97
|
+
throw new Error(
|
|
98
|
+
'An active provider monitor requires a non-empty provider resource id. Keep it pending until upstream provisioning returns one.',
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return operational;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Translate the historical control-record lifecycle label at the sole
|
|
106
|
+
* compatibility boundary. New code persists `desired` and returns this type;
|
|
107
|
+
* this mapping exists only while the online backfill drains existing rows.
|
|
108
|
+
*/
|
|
109
|
+
export function providerMonitorStateForLegacyStatus(
|
|
110
|
+
status: string | undefined,
|
|
111
|
+
): ProviderMonitorState {
|
|
112
|
+
switch (status) {
|
|
113
|
+
case 'active':
|
|
114
|
+
case 'pending_create':
|
|
115
|
+
case 'replacement_pending':
|
|
116
|
+
case 'replacement_active_old_cleanup_pending':
|
|
117
|
+
case 'replacement_cleanup_failed':
|
|
118
|
+
return 'active';
|
|
119
|
+
default:
|
|
120
|
+
return 'inactive';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -101,7 +101,11 @@ export type PlayActivityState =
|
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
export type PlayActivityObservation = {
|
|
104
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Retired marker. Producers stopped writing it; envelopes still in flight
|
|
106
|
+
* from an older producer carry `1`, so readers tolerate both shapes.
|
|
107
|
+
*/
|
|
108
|
+
schemaVersion?: 1;
|
|
105
109
|
/** Stable within one run, for example `tool:discover_ctos` or `dataset:leads`. */
|
|
106
110
|
activityId: string;
|
|
107
111
|
/** Optional owning play step. */
|
|
@@ -126,7 +130,7 @@ export function isPlayActivityObservation(
|
|
|
126
130
|
): value is PlayActivityObservation {
|
|
127
131
|
if (
|
|
128
132
|
!isRecord(value) ||
|
|
129
|
-
value.schemaVersion !== 1 ||
|
|
133
|
+
(value.schemaVersion !== undefined && value.schemaVersion !== 1) ||
|
|
130
134
|
typeof value.activityId !== 'string' ||
|
|
131
135
|
!value.activityId.trim() ||
|
|
132
136
|
typeof value.observedAt !== 'number' ||
|
|
@@ -316,7 +320,6 @@ export function createPlayActivityReporter(input: {
|
|
|
316
320
|
type: 'activity.observed',
|
|
317
321
|
observation: {
|
|
318
322
|
...observation,
|
|
319
|
-
schemaVersion: 1,
|
|
320
323
|
observedAt: observation.observedAt ?? input.now?.() ?? Date.now(),
|
|
321
324
|
},
|
|
322
325
|
});
|
|
@@ -402,7 +405,6 @@ export function projectPlayRunActivity(input: {
|
|
|
402
405
|
if (input.waitKind === 'detached_runner') {
|
|
403
406
|
return projection(
|
|
404
407
|
{
|
|
405
|
-
schemaVersion: 1,
|
|
406
408
|
activityId: 'runner:managed',
|
|
407
409
|
target: {
|
|
408
410
|
kind: 'runner',
|
|
@@ -418,7 +420,6 @@ export function projectPlayRunActivity(input: {
|
|
|
418
420
|
if (input.waitKind === 'sleep') {
|
|
419
421
|
return projection(
|
|
420
422
|
{
|
|
421
|
-
schemaVersion: 1,
|
|
422
423
|
activityId: 'run:sleep',
|
|
423
424
|
target: {
|
|
424
425
|
kind: 'step',
|
|
@@ -441,7 +442,6 @@ export function projectPlayRunActivity(input: {
|
|
|
441
442
|
) {
|
|
442
443
|
return projection(
|
|
443
444
|
{
|
|
444
|
-
schemaVersion: 1,
|
|
445
445
|
activityId: `event:${input.activeNodeId ?? 'run'}`,
|
|
446
446
|
stepId: input.activeNodeId ?? undefined,
|
|
447
447
|
target: {
|
|
@@ -468,7 +468,6 @@ export function projectPlayRunActivity(input: {
|
|
|
468
468
|
if (normalizedStatus === 'waiting') {
|
|
469
469
|
return projection(
|
|
470
470
|
{
|
|
471
|
-
schemaVersion: 1,
|
|
472
471
|
activityId: 'run:legacy-wait',
|
|
473
472
|
target: {
|
|
474
473
|
kind: 'step',
|
|
@@ -506,7 +505,6 @@ export function projectPlayRunActivity(input: {
|
|
|
506
505
|
const datasetProgress = datasetStep?.progress ?? null;
|
|
507
506
|
return projection(
|
|
508
507
|
{
|
|
509
|
-
schemaVersion: 1,
|
|
510
508
|
activityId: `dataset:${pendingDataset.datasetId ?? pendingDataset.tableNamespace}`,
|
|
511
509
|
target: {
|
|
512
510
|
kind: 'dataset',
|
|
@@ -548,7 +546,6 @@ export function projectPlayRunActivity(input: {
|
|
|
548
546
|
const provider = toolId?.split(/[._]/)[0]?.trim() || null;
|
|
549
547
|
return projection(
|
|
550
548
|
{
|
|
551
|
-
schemaVersion: 1,
|
|
552
549
|
activityId: `step:${activeStep.nodeId}`,
|
|
553
550
|
stepId: activeStep.nodeId,
|
|
554
551
|
target:
|
|
@@ -577,7 +574,6 @@ export function projectPlayRunActivity(input: {
|
|
|
577
574
|
if (normalizedStatus === 'queued') {
|
|
578
575
|
return projection(
|
|
579
576
|
{
|
|
580
|
-
schemaVersion: 1,
|
|
581
577
|
activityId: 'capacity:worker',
|
|
582
578
|
target: { kind: 'capacity', pool: 'worker' },
|
|
583
579
|
state: { kind: 'queued', reason: 'capacity' },
|
|
@@ -588,7 +584,6 @@ export function projectPlayRunActivity(input: {
|
|
|
588
584
|
}
|
|
589
585
|
|
|
590
586
|
const fallback: PlayActivityObservation = {
|
|
591
|
-
schemaVersion: 1,
|
|
592
587
|
activityId: 'run:legacy',
|
|
593
588
|
target: {
|
|
594
589
|
kind: 'step',
|
|
@@ -4,6 +4,7 @@ import { stat } from 'node:fs/promises';
|
|
|
4
4
|
import { Readable } from 'node:stream';
|
|
5
5
|
import { pipeline } from 'node:stream/promises';
|
|
6
6
|
import type { PlaySheetContract } from '../plays/static-pipeline';
|
|
7
|
+
import type { AsyncPlayRunRef } from '../plays/authoring-contract';
|
|
7
8
|
import type { PlayRowUpdate } from './/ctx-types';
|
|
8
9
|
import type { PlayRunTimelineEntry } from './/live-events';
|
|
9
10
|
import type { PlayDatasetBornFrom } from './/cell-provenance';
|
|
@@ -179,6 +180,51 @@ type RuntimeApiRequest =
|
|
|
179
180
|
runId: string;
|
|
180
181
|
firstClaimLatencyMs: number;
|
|
181
182
|
}
|
|
183
|
+
| {
|
|
184
|
+
action: 'launch_play_async';
|
|
185
|
+
key: string;
|
|
186
|
+
invocationKey: string;
|
|
187
|
+
playRef: string;
|
|
188
|
+
input: Record<string, unknown>;
|
|
189
|
+
description: string;
|
|
190
|
+
schedulerSchema?: string | null;
|
|
191
|
+
ancestryPlayIds: string[];
|
|
192
|
+
}
|
|
193
|
+
| {
|
|
194
|
+
action: 'project_run_created';
|
|
195
|
+
idempotencyKey: string;
|
|
196
|
+
playName: string;
|
|
197
|
+
playReference?: string | null;
|
|
198
|
+
workflowId: string;
|
|
199
|
+
runId: string;
|
|
200
|
+
workflowFamilyKey: string;
|
|
201
|
+
definitionScope?: 'org' | 'system';
|
|
202
|
+
revisionId?: string | null;
|
|
203
|
+
artifactStorageKey?: string | null;
|
|
204
|
+
artifactHash?: string | null;
|
|
205
|
+
graphHash?: string | null;
|
|
206
|
+
runtimeBackend?: string | null;
|
|
207
|
+
schedulerBackend?: string | null;
|
|
208
|
+
schedulerSchema?: string | null;
|
|
209
|
+
executionProfile?: string | null;
|
|
210
|
+
runtimeReleaseId?: string | null;
|
|
211
|
+
runtimeDeployVersion?: string | null;
|
|
212
|
+
runtimeCallbackBaseUrl?: string | null;
|
|
213
|
+
coordinatorUrl?: string | null;
|
|
214
|
+
coordinatorWorkerName?: string | null;
|
|
215
|
+
runtimeWorkflowName?: string | null;
|
|
216
|
+
runtimeHarnessWorkerName?: string | null;
|
|
217
|
+
maxCreditsPerRun?: number | null;
|
|
218
|
+
staticPipeline?: unknown;
|
|
219
|
+
source?: 'published' | 'ad_hoc' | 'draft';
|
|
220
|
+
triggerSource?: 'webhook' | 'cron' | 'sql_listener' | 'api';
|
|
221
|
+
inputFileId?: string;
|
|
222
|
+
inputBytes?: number;
|
|
223
|
+
inputSha256?: string;
|
|
224
|
+
replayedFromRunId?: string | null;
|
|
225
|
+
secretRefs?: unknown[];
|
|
226
|
+
inputSummary?: Record<string, unknown>;
|
|
227
|
+
}
|
|
182
228
|
| ({
|
|
183
229
|
action: 'save_results';
|
|
184
230
|
} & RuntimeSaveResults)
|
|
@@ -719,7 +765,8 @@ function isRetryableAppRuntimeAction(
|
|
|
719
765
|
action === 'release_runtime_step_receipt' ||
|
|
720
766
|
action === 'save_results' ||
|
|
721
767
|
action === 'skip_runtime_step_receipt' ||
|
|
722
|
-
action === 'start_run'
|
|
768
|
+
action === 'start_run' ||
|
|
769
|
+
action === 'launch_play_async'
|
|
723
770
|
);
|
|
724
771
|
}
|
|
725
772
|
|
|
@@ -1073,7 +1120,8 @@ export class AppRuntimeApiResponseError extends Error {
|
|
|
1073
1120
|
this.requestId = input.requestId?.trim() || null;
|
|
1074
1121
|
this.retryable = input.retryable;
|
|
1075
1122
|
this.retryAfterMs =
|
|
1076
|
-
typeof input.retryAfterMs === 'number' &&
|
|
1123
|
+
typeof input.retryAfterMs === 'number' &&
|
|
1124
|
+
Number.isFinite(input.retryAfterMs)
|
|
1077
1125
|
? Math.max(0, Math.floor(input.retryAfterMs))
|
|
1078
1126
|
: null;
|
|
1079
1127
|
this.detail = input.detail;
|
|
@@ -1963,16 +2011,14 @@ export async function appendRunEventsViaAppRuntime(
|
|
|
1963
2011
|
let terminalBatchIndex = -1;
|
|
1964
2012
|
for (let index = batches.length - 1; index >= 0; index -= 1) {
|
|
1965
2013
|
if (
|
|
1966
|
-
batches[index]!.some(
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
},
|
|
1975
|
-
)
|
|
2014
|
+
batches[index]!.some((event) => {
|
|
2015
|
+
const key = event.key ?? event.type;
|
|
2016
|
+
return (
|
|
2017
|
+
key === 'run.completed' ||
|
|
2018
|
+
key === 'run.failed' ||
|
|
2019
|
+
key === 'run.cancelled'
|
|
2020
|
+
);
|
|
2021
|
+
})
|
|
1976
2022
|
) {
|
|
1977
2023
|
terminalBatchIndex = index;
|
|
1978
2024
|
break;
|
|
@@ -2021,6 +2067,19 @@ export async function startRunViaAppRuntime(
|
|
|
2021
2067
|
});
|
|
2022
2068
|
}
|
|
2023
2069
|
|
|
2070
|
+
export async function projectRunCreatedViaAppRuntime(
|
|
2071
|
+
context: WorkerRuntimeApiContext,
|
|
2072
|
+
input: Omit<
|
|
2073
|
+
Extract<RuntimeApiRequest, { action: 'project_run_created' }>,
|
|
2074
|
+
'action'
|
|
2075
|
+
>,
|
|
2076
|
+
): Promise<void> {
|
|
2077
|
+
await postAppRuntimeApi<{ ok: true; status: 'queued' }>(context, {
|
|
2078
|
+
action: 'project_run_created',
|
|
2079
|
+
...input,
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2024
2083
|
export async function recordFirstClaimLatencyViaAppRuntime(
|
|
2025
2084
|
context: WorkerRuntimeApiContext,
|
|
2026
2085
|
input: { runId: string; firstClaimLatencyMs: number },
|
|
@@ -2031,6 +2090,25 @@ export async function recordFirstClaimLatencyViaAppRuntime(
|
|
|
2031
2090
|
});
|
|
2032
2091
|
}
|
|
2033
2092
|
|
|
2093
|
+
export async function launchPlayAsyncViaAppRuntime(
|
|
2094
|
+
context: WorkerRuntimeApiContext,
|
|
2095
|
+
input: {
|
|
2096
|
+
key: string;
|
|
2097
|
+
invocationKey: string;
|
|
2098
|
+
playRef: string;
|
|
2099
|
+
input: Record<string, unknown>;
|
|
2100
|
+
description: string;
|
|
2101
|
+
schedulerSchema?: string | null;
|
|
2102
|
+
ancestryPlayIds: readonly string[];
|
|
2103
|
+
},
|
|
2104
|
+
): Promise<AsyncPlayRunRef> {
|
|
2105
|
+
return await postAppRuntimeApi<AsyncPlayRunRef>(context, {
|
|
2106
|
+
action: 'launch_play_async',
|
|
2107
|
+
...input,
|
|
2108
|
+
ancestryPlayIds: [...input.ancestryPlayIds],
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2034
2112
|
export async function saveResultsViaAppRuntime(
|
|
2035
2113
|
context: WorkerRuntimeApiContext,
|
|
2036
2114
|
input: RuntimeSaveResults,
|