deepline 0.1.239 → 0.1.241
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/release.ts +6 -3
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +27 -7
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +8 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +12 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +5 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +30 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +5 -1
- package/dist/bundling-sources/shared_libs/plays/artifact-kind-guard.ts +21 -2
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +11 -0
- package/dist/bundling-sources/shared_libs/plays/secret-guardrails.ts +14 -0
- package/dist/bundling-sources/shared_libs/plays/source-metadata.ts +239 -0
- package/dist/cli/index.js +16 -4
- package/dist/cli/index.mjs +16 -4
- package/dist/index.js +7 -4
- package/dist/index.mjs +7 -4
- package/dist/plays/bundle-play-file.mjs +9 -0
- package/package.json +2 -2
|
@@ -110,10 +110,13 @@ export const SDK_RELEASE = {
|
|
|
110
110
|
// silently materializing them as unmatched results.
|
|
111
111
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
112
112
|
// automatically without blocking their current command.
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
// 0.1.241 hard-cuts published and launched plays to immutable cjs_node20
|
|
114
|
+
// artifacts. Older clients can submit esm_workers artifacts that the Absurd
|
|
115
|
+
// runtime intentionally no longer compiles at publish or launch time.
|
|
116
|
+
version: '0.1.241',
|
|
117
|
+
apiContract: '2026-07-immutable-cjs-play-artifacts-hard-cutover',
|
|
115
118
|
supportPolicy: {
|
|
116
|
-
latest: '0.1.
|
|
119
|
+
latest: '0.1.241',
|
|
117
120
|
minimumSupported: '0.1.53',
|
|
118
121
|
deprecatedBelow: '0.1.219',
|
|
119
122
|
commandMinimumSupported: [
|
|
@@ -5852,6 +5852,10 @@ export class PlayContextImpl {
|
|
|
5852
5852
|
for (const wake of waiters) wake();
|
|
5853
5853
|
}
|
|
5854
5854
|
|
|
5855
|
+
private toolBatchDispatcherNowMs(): number {
|
|
5856
|
+
return this.#options.toolBatchDispatcherClock?.nowMs() ?? Date.now();
|
|
5857
|
+
}
|
|
5858
|
+
|
|
5855
5859
|
private enqueueToolCall(request: ToolCallRequest): void {
|
|
5856
5860
|
if (this.toolDispatcherFailure != null) {
|
|
5857
5861
|
const resolver = this.toolCallResolvers.get(request.callId);
|
|
@@ -5864,7 +5868,10 @@ export class PlayContextImpl {
|
|
|
5864
5868
|
this.toolCallQueue.push(request);
|
|
5865
5869
|
const batchKey = this.toolBatchCoalesceKey(request);
|
|
5866
5870
|
if (batchKey && !this.toolBatchQueuedAtByKey.has(batchKey)) {
|
|
5867
|
-
this.toolBatchQueuedAtByKey.set(
|
|
5871
|
+
this.toolBatchQueuedAtByKey.set(
|
|
5872
|
+
batchKey,
|
|
5873
|
+
this.toolBatchDispatcherNowMs(),
|
|
5874
|
+
);
|
|
5868
5875
|
}
|
|
5869
5876
|
this.wakeToolDispatcher();
|
|
5870
5877
|
}
|
|
@@ -5941,15 +5948,26 @@ export class PlayContextImpl {
|
|
|
5941
5948
|
|
|
5942
5949
|
private waitForToolDispatcherWake(deadlineMs: number | null): Promise<void> {
|
|
5943
5950
|
return new Promise((resolve) => {
|
|
5944
|
-
let
|
|
5951
|
+
let cancelDeadline: (() => void) | null = null;
|
|
5945
5952
|
const wake = () => {
|
|
5946
|
-
|
|
5953
|
+
cancelDeadline?.();
|
|
5954
|
+
cancelDeadline = null;
|
|
5947
5955
|
this.toolDispatcherWakeWaiters.delete(wake);
|
|
5948
5956
|
resolve();
|
|
5949
5957
|
};
|
|
5950
5958
|
this.toolDispatcherWakeWaiters.add(wake);
|
|
5951
5959
|
if (deadlineMs != null) {
|
|
5952
|
-
|
|
5960
|
+
const delayMs = Math.max(
|
|
5961
|
+
0,
|
|
5962
|
+
deadlineMs - this.toolBatchDispatcherNowMs(),
|
|
5963
|
+
);
|
|
5964
|
+
const clock = this.#options.toolBatchDispatcherClock;
|
|
5965
|
+
if (clock) {
|
|
5966
|
+
cancelDeadline = clock.schedule(delayMs, wake);
|
|
5967
|
+
} else {
|
|
5968
|
+
const timer = setTimeout(wake, delayMs);
|
|
5969
|
+
cancelDeadline = () => clearTimeout(timer);
|
|
5970
|
+
}
|
|
5953
5971
|
}
|
|
5954
5972
|
});
|
|
5955
5973
|
}
|
|
@@ -5977,7 +5995,9 @@ export class PlayContextImpl {
|
|
|
5977
5995
|
throw this.toolDispatcherFailure;
|
|
5978
5996
|
}
|
|
5979
5997
|
|
|
5980
|
-
const dispatchable = this.takeDispatchableToolCalls(
|
|
5998
|
+
const dispatchable = this.takeDispatchableToolCalls(
|
|
5999
|
+
this.toolBatchDispatcherNowMs(),
|
|
6000
|
+
);
|
|
5981
6001
|
if (dispatchable.requests.length > 0) {
|
|
5982
6002
|
pass += 1;
|
|
5983
6003
|
this.log(` Batch pass ${pass}`);
|
|
@@ -6688,8 +6708,8 @@ export class PlayContextImpl {
|
|
|
6688
6708
|
rowScopeKey ? `@${rowScopeKey}` : ''
|
|
6689
6709
|
}`;
|
|
6690
6710
|
if (rowScope) {
|
|
6691
|
-
const namespaces =
|
|
6692
|
-
|
|
6711
|
+
const namespaces = (rowScope.inlineChildInvocationNamespaces ??=
|
|
6712
|
+
new Set<string>());
|
|
6693
6713
|
namespaces.add(namespace);
|
|
6694
6714
|
if (namespaces.size > MAX_INLINE_CHILD_INVOCATIONS_PER_ROW) {
|
|
6695
6715
|
throw new Error(
|
|
@@ -680,6 +680,14 @@ export interface ContextOptions {
|
|
|
680
680
|
getBatchOperationStrategy?: (
|
|
681
681
|
operation: string,
|
|
682
682
|
) => AnyBatchOperationStrategy | null;
|
|
683
|
+
/**
|
|
684
|
+
* Internal deterministic clock for fixed-window dispatcher tests. Runtime
|
|
685
|
+
* adapters omit this and use the system clock.
|
|
686
|
+
*/
|
|
687
|
+
toolBatchDispatcherClock?: {
|
|
688
|
+
nowMs: () => number;
|
|
689
|
+
schedule: (delayMs: number, wake: () => void) => () => void;
|
|
690
|
+
};
|
|
683
691
|
// How long batchable calls wait for same-key row continuations before
|
|
684
692
|
// dispatching, measured from the first queued item. Defaults to 5ms in
|
|
685
693
|
// production; tests override it to a large value to make coalescing
|
|
@@ -1055,7 +1055,12 @@ export function reducePlayRunLedgerEvent(
|
|
|
1055
1055
|
if (ignoreNonTerminalAfterStickyTerminal) {
|
|
1056
1056
|
return withTiming(base);
|
|
1057
1057
|
}
|
|
1058
|
-
|
|
1058
|
+
// Scheduler wait/resume events own the active lifecycle once a run is
|
|
1059
|
+
// parked. Worker registration can arrive later through an independent
|
|
1060
|
+
// ledger append lane; replaying that older `run.created` fact must not
|
|
1061
|
+
// make an externally-waiting run look active again.
|
|
1062
|
+
const createdStatus =
|
|
1063
|
+
base.status === 'waiting' ? 'waiting' : (event.status ?? base.status);
|
|
1059
1064
|
return withTiming({
|
|
1060
1065
|
...base,
|
|
1061
1066
|
playName: event.playName ?? base.playName ?? null,
|
|
@@ -1075,7 +1080,9 @@ export function reducePlayRunLedgerEvent(
|
|
|
1075
1080
|
playName: event.playName ?? base.playName ?? null,
|
|
1076
1081
|
status: isTerminalPlayRunLedgerStatus(base.status)
|
|
1077
1082
|
? base.status
|
|
1078
|
-
: '
|
|
1083
|
+
: base.status === 'waiting'
|
|
1084
|
+
? 'waiting'
|
|
1085
|
+
: 'running',
|
|
1079
1086
|
startedAt: base.startedAt ?? occurredAt,
|
|
1080
1087
|
});
|
|
1081
1088
|
case 'run.waiting':
|
|
@@ -1200,7 +1207,9 @@ export function reducePlayRunLedgerEvent(
|
|
|
1200
1207
|
...base,
|
|
1201
1208
|
status: isTerminalPlayRunLedgerStatus(base.status)
|
|
1202
1209
|
? base.status
|
|
1203
|
-
: '
|
|
1210
|
+
: base.status === 'waiting'
|
|
1211
|
+
? 'waiting'
|
|
1212
|
+
: 'running',
|
|
1204
1213
|
startedAt: base.startedAt ?? occurredAt,
|
|
1205
1214
|
orderedStepIds: appendOrderedStepId(base, event.stepId),
|
|
1206
1215
|
stepsById: { ...base.stepsById, [event.stepId]: nextStep },
|
|
@@ -32,6 +32,7 @@ export type StagedDaytonaPayload = {
|
|
|
32
32
|
workDir: string;
|
|
33
33
|
command: string;
|
|
34
34
|
readyPath: string;
|
|
35
|
+
startAckPath: string;
|
|
35
36
|
outputPath: string;
|
|
36
37
|
exitCodePath: string;
|
|
37
38
|
progressEventPath: string;
|
|
@@ -389,6 +390,7 @@ export async function stageDaytonaRunnerPayload(input: {
|
|
|
389
390
|
const outputPath = `${workDir}/deepline-play-output-${randomUUID()}.log`;
|
|
390
391
|
const exitCodePath = `${workDir}/deepline-play-exit-${randomUUID()}.txt`;
|
|
391
392
|
const readyPath = `${workDir}/deepline-play-ready-${randomUUID()}.json`;
|
|
393
|
+
const startAckPath = `${workDir}/deepline-play-start-ack-${randomUUID()}.json`;
|
|
392
394
|
const progressEventPath = `${workDir}/deepline-play-progress-${randomUUID()}.jsonl`;
|
|
393
395
|
const runnerTraceEnv =
|
|
394
396
|
process.env.DEEPLINE_RUNTIME_RECEIPT_TRACE === '1'
|
|
@@ -401,18 +403,19 @@ export async function stageDaytonaRunnerPayload(input: {
|
|
|
401
403
|
artifactCodePath,
|
|
402
404
|
artifactSourceMapPath,
|
|
403
405
|
crashPusherPath,
|
|
404
|
-
})} && DEEPLINE_PLAY_RUNNER_READY_PATH=${shellQuote(readyPath)} DEEPLINE_PLAY_RUNNER_PROGRESS_EVENT_PATH=${shellQuote(progressEventPath)} DEEPLINE_PLAY_RUNNER_STDOUT_EVENT_MODE=compact ${runnerTraceEnv}node ${shellQuote(runnerPath)} ${shellQuote(configPath)}`;
|
|
406
|
+
})} && DEEPLINE_PLAY_RUNNER_READY_PATH=${shellQuote(readyPath)} DEEPLINE_PLAY_RUNNER_START_ACK_PATH=${shellQuote(startAckPath)} DEEPLINE_PLAY_RUNNER_PROGRESS_EVENT_PATH=${shellQuote(progressEventPath)} DEEPLINE_PLAY_RUNNER_STDOUT_EVENT_MODE=compact ${runnerTraceEnv}node ${shellQuote(runnerPath)} ${shellQuote(configPath)}`;
|
|
405
407
|
// Crash-containment epilogue: runs UNCONDITIONALLY after the runner exits and
|
|
406
408
|
// pushes the parsed (or synthesized) terminal to the gateway so the parked
|
|
407
409
|
// worker wakes within seconds of ANY runner death — process.exit abuse, OOM
|
|
408
410
|
// SIGKILL, or a crash before the in-process push. Idempotent against a
|
|
409
411
|
// successful in-runner push (terminals are first-write-wins).
|
|
410
|
-
const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(exitCodePath)} ${shellQuote(readyPath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; node ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} >> ${shellQuote(outputPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
|
|
412
|
+
const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(exitCodePath)} ${shellQuote(readyPath)} ${shellQuote(startAckPath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; node ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} >> ${shellQuote(outputPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
|
|
411
413
|
|
|
412
414
|
return {
|
|
413
415
|
workDir: input.workDir,
|
|
414
416
|
command,
|
|
415
417
|
readyPath,
|
|
418
|
+
startAckPath,
|
|
416
419
|
outputPath,
|
|
417
420
|
exitCodePath,
|
|
418
421
|
progressEventPath,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DaytonaSandbox } from './daytona-lifecycle';
|
|
2
2
|
|
|
3
3
|
export type DetachedDaytonaStartupDiagnostic = {
|
|
4
|
+
readyMarkerObserved: boolean;
|
|
5
|
+
startAcknowledgementAttempted: boolean;
|
|
4
6
|
commandFound: boolean;
|
|
5
7
|
exitCode: number | null;
|
|
6
8
|
sessionFound: boolean;
|
|
@@ -51,6 +53,8 @@ export async function inspectDetachedDaytonaStartup(input: {
|
|
|
51
53
|
}): Promise<DetachedDaytonaStartupDiagnostic> {
|
|
52
54
|
const errors: string[] = [];
|
|
53
55
|
const diagnostic: DetachedDaytonaStartupDiagnostic = {
|
|
56
|
+
readyMarkerObserved: false,
|
|
57
|
+
startAcknowledgementAttempted: false,
|
|
54
58
|
commandFound: false,
|
|
55
59
|
exitCode: null,
|
|
56
60
|
sessionFound: false,
|
|
@@ -119,7 +123,10 @@ export const DAYTONA_RUNNER_READY_TIMEOUT_MS = 60_000;
|
|
|
119
123
|
const DAYTONA_RUNNER_READY_POLL_MS = 250;
|
|
120
124
|
|
|
121
125
|
export class DaytonaRunnerInitializationError extends Error {
|
|
122
|
-
constructor(
|
|
126
|
+
constructor(
|
|
127
|
+
message: string,
|
|
128
|
+
readonly startupDiagnostic: DetachedDaytonaStartupDiagnostic,
|
|
129
|
+
) {
|
|
123
130
|
super(message);
|
|
124
131
|
this.name = 'DaytonaRunnerInitializationError';
|
|
125
132
|
}
|
|
@@ -163,6 +170,7 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
|
163
170
|
sessionId: string;
|
|
164
171
|
cmdId: string;
|
|
165
172
|
readyPath: string;
|
|
173
|
+
startAckPath: string;
|
|
166
174
|
exitCodePath: string;
|
|
167
175
|
timeoutMs?: number;
|
|
168
176
|
pollMs?: number;
|
|
@@ -180,6 +188,8 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
|
180
188
|
const pollMs = Math.max(1, input.pollMs ?? DAYTONA_RUNNER_READY_POLL_MS);
|
|
181
189
|
const deadline = now() + timeoutMs;
|
|
182
190
|
let markerDiagnosis = 'ready_marker_unavailable';
|
|
191
|
+
let readyMarkerObserved = false;
|
|
192
|
+
let startAcknowledgementAttempted = false;
|
|
183
193
|
|
|
184
194
|
while (now() < deadline) {
|
|
185
195
|
try {
|
|
@@ -187,8 +197,21 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
|
187
197
|
const parsed = JSON.parse(marker.toString('utf-8')) as {
|
|
188
198
|
ready?: unknown;
|
|
189
199
|
};
|
|
190
|
-
if (parsed.ready === true)
|
|
191
|
-
|
|
200
|
+
if (parsed.ready === true) {
|
|
201
|
+
readyMarkerObserved = true;
|
|
202
|
+
startAcknowledgementAttempted = true;
|
|
203
|
+
try {
|
|
204
|
+
await input.sandbox.fs.uploadFile(
|
|
205
|
+
Buffer.from('{"start":true}', 'utf-8'),
|
|
206
|
+
input.startAckPath,
|
|
207
|
+
);
|
|
208
|
+
return;
|
|
209
|
+
} catch {
|
|
210
|
+
markerDiagnosis = 'start_ack_unavailable';
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
markerDiagnosis = 'ready_marker_invalid';
|
|
214
|
+
}
|
|
192
215
|
} catch {
|
|
193
216
|
markerDiagnosis = 'ready_marker_unavailable';
|
|
194
217
|
}
|
|
@@ -201,9 +224,12 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
|
201
224
|
cmdId: input.cmdId,
|
|
202
225
|
exitCodePath: input.exitCodePath,
|
|
203
226
|
});
|
|
227
|
+
diagnostic.readyMarkerObserved = readyMarkerObserved;
|
|
228
|
+
diagnostic.startAcknowledgementAttempted = startAcknowledgementAttempted;
|
|
204
229
|
const oomIndicated =
|
|
205
230
|
diagnostic.exitCode === 137 || diagnostic.exitCodeFile === 137;
|
|
206
231
|
throw new DaytonaRunnerInitializationError(
|
|
207
|
-
`RUNTIME_SANDBOX_START_FAILED: Daytona accepted detached command ${input.cmdId} in sandbox ${input.sandbox.id}, but the play runner did not materialize and establish receipt-gateway liveness within ${timeoutMs}ms. Marker diagnosis: ${markerDiagnosis}. Daytona diagnosis: ${JSON.stringify(diagnostic)}. OOM is ${oomIndicated ? 'indicated by exit code 137' : 'not confirmed by the available Daytona evidence'}. The
|
|
232
|
+
`RUNTIME_SANDBOX_START_FAILED: Daytona accepted detached command ${input.cmdId} in sandbox ${input.sandbox.id}, but the play runner did not materialize and establish receipt-gateway liveness within ${timeoutMs}ms. Marker diagnosis: ${markerDiagnosis}. Daytona diagnosis: ${JSON.stringify(diagnostic)}. OOM is ${oomIndicated ? 'indicated by exit code 137' : 'not confirmed by the available Daytona evidence'}. The worker will not park this runner; the failed runner was stopped and was not retried in place.`,
|
|
233
|
+
diagnostic,
|
|
208
234
|
);
|
|
209
235
|
}
|
|
@@ -474,6 +474,27 @@ function isRetryableDaytonaInfrastructureFailure(error: unknown): boolean {
|
|
|
474
474
|
return DAYTONA_INFRASTRUCTURE_RETRY_PATTERN.test(message);
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
+
function isRetryableUnstartedDaytonaRunner(
|
|
478
|
+
error: unknown,
|
|
479
|
+
): error is DaytonaRunnerInitializationError {
|
|
480
|
+
if (!(error instanceof DaytonaRunnerInitializationError)) return false;
|
|
481
|
+
const diagnostic = error.startupDiagnostic;
|
|
482
|
+
// The ready fence runs before any customer play code. If Daytona accepted
|
|
483
|
+
// the command but the worker never observed its ready marker, the runner
|
|
484
|
+
// cannot pass the reverse start-ack fence and customer code cannot begin.
|
|
485
|
+
// Terminate that sandbox and retry once on a fresh one even when its Node
|
|
486
|
+
// process is still alive. Do not retry after a start acknowledgement was
|
|
487
|
+
// attempted (its write outcome may be ambiguous), or after a runner exit.
|
|
488
|
+
return (
|
|
489
|
+
diagnostic.commandFound &&
|
|
490
|
+
diagnostic.sessionFound &&
|
|
491
|
+
diagnostic.exitCode === null &&
|
|
492
|
+
diagnostic.exitCodeFile === null &&
|
|
493
|
+
!diagnostic.readyMarkerObserved &&
|
|
494
|
+
!diagnostic.startAcknowledgementAttempted
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
477
498
|
function prepareDaytonaExecution(
|
|
478
499
|
input: PlayRunnerPrepareInput,
|
|
479
500
|
callbacks: Parameters<PlayRunnerBackend['execute']>[1],
|
|
@@ -819,6 +840,7 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
819
840
|
sessionId: start.sessionId,
|
|
820
841
|
cmdId: start.cmdId,
|
|
821
842
|
readyPath: stagedPayload.readyPath,
|
|
843
|
+
startAckPath: stagedPayload.startAckPath,
|
|
822
844
|
exitCodePath: stagedPayload.exitCodePath,
|
|
823
845
|
}),
|
|
824
846
|
cancellationPromise,
|
|
@@ -868,6 +890,25 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
868
890
|
onCancel();
|
|
869
891
|
throw new Error(DAYTONA_CANCELLED_ERROR);
|
|
870
892
|
}
|
|
893
|
+
if (
|
|
894
|
+
executionAttempt < DAYTONA_RUNTIME_INITIALIZATION_MAX_ATTEMPTS &&
|
|
895
|
+
isRetryableUnstartedDaytonaRunner(error)
|
|
896
|
+
) {
|
|
897
|
+
emitDaytonaStage(callbacks, config.context, 'execute:retry', {
|
|
898
|
+
sandboxId: sandboxForAttempt?.id ?? null,
|
|
899
|
+
attempt: executionAttempt + 1,
|
|
900
|
+
reason: 'runner_startup_not_live',
|
|
901
|
+
error: error.message,
|
|
902
|
+
elapsedMs: Date.now() - startedAt,
|
|
903
|
+
});
|
|
904
|
+
if (sandboxForAttempt) {
|
|
905
|
+
activeAcquiredResource!.billingEndedAt = Date.now();
|
|
906
|
+
await reportRetiringRuntimeResource(activeAcquiredResource!);
|
|
907
|
+
sandboxCleanup.stashActiveSandboxForRetry();
|
|
908
|
+
}
|
|
909
|
+
acquiredSandboxPromise = sandboxLifecycle.createFreshSandbox();
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
871
912
|
if (
|
|
872
913
|
executionAttempt < DAYTONA_INFRASTRUCTURE_MAX_ATTEMPTS &&
|
|
873
914
|
isRetryableDaytonaInfrastructureFailure(error)
|
|
@@ -2,7 +2,11 @@ export const SECRET_REDACTION_PLACEHOLDER = '[REDACTED_SECRET]';
|
|
|
2
2
|
|
|
3
3
|
const BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/g;
|
|
4
4
|
const BASIC_CREDENTIAL_RE = /\bBasic\s+[A-Za-z0-9+/]{4,}={0,2}\b/g;
|
|
5
|
-
|
|
5
|
+
// Bound each segment so V8 cannot recursively backtrack across a multi-megabyte
|
|
6
|
+
// opaque receipt value that contains no dots. Real executor/provider JWT
|
|
7
|
+
// segments are far smaller; 16 KiB per segment still leaves ample headroom.
|
|
8
|
+
const JWT_RE =
|
|
9
|
+
/\b[A-Za-z0-9_-]{8,16384}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{8,16384}\b/g;
|
|
6
10
|
const PRIVATE_KEY_RE =
|
|
7
11
|
/-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
|
|
8
12
|
const SENSITIVE_FIELD_NAME =
|
|
@@ -42,12 +42,18 @@ export function bundledCodeHasCloudflareSchemeImport(
|
|
|
42
42
|
export class PlayArtifactKindMismatchError extends Error {
|
|
43
43
|
readonly expectedKind: PlayArtifactKind;
|
|
44
44
|
readonly actualKind: PlayArtifactKind;
|
|
45
|
-
readonly reason:
|
|
45
|
+
readonly reason:
|
|
46
|
+
| 'kind_mismatch'
|
|
47
|
+
| 'code_format_mismatch'
|
|
48
|
+
| 'cloudflare_scheme_import';
|
|
46
49
|
|
|
47
50
|
constructor(input: {
|
|
48
51
|
expectedKind: PlayArtifactKind;
|
|
49
52
|
actualKind: PlayArtifactKind;
|
|
50
|
-
reason:
|
|
53
|
+
reason:
|
|
54
|
+
| 'kind_mismatch'
|
|
55
|
+
| 'code_format_mismatch'
|
|
56
|
+
| 'cloudflare_scheme_import';
|
|
51
57
|
message: string;
|
|
52
58
|
}) {
|
|
53
59
|
super(input.message);
|
|
@@ -96,6 +102,19 @@ export function assertNodeRunnableArtifact(input: {
|
|
|
96
102
|
});
|
|
97
103
|
}
|
|
98
104
|
|
|
105
|
+
if (artifact.codeFormat !== 'cjs_module') {
|
|
106
|
+
throw new PlayArtifactKindMismatchError({
|
|
107
|
+
expectedKind,
|
|
108
|
+
actualKind,
|
|
109
|
+
reason: 'code_format_mismatch',
|
|
110
|
+
message:
|
|
111
|
+
`Node runner (${context}) received a "${actualKind}" play artifact ` +
|
|
112
|
+
`with codeFormat "${artifact.codeFormat}" but requires "cjs_module". ` +
|
|
113
|
+
`The artifact metadata is inconsistent; rebuild it for the node runner ` +
|
|
114
|
+
`before launch.`,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
99
118
|
if (bundledCodeHasCloudflareSchemeImport(artifact.bundledCode)) {
|
|
100
119
|
throw new PlayArtifactKindMismatchError({
|
|
101
120
|
expectedKind,
|
|
@@ -440,6 +440,17 @@ function findSourceImportReferences(
|
|
|
440
440
|
);
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
+
/**
|
|
444
|
+
* Reports local runtime edges using the same parser that drives graph analysis.
|
|
445
|
+
* Migration code uses this to distinguish truly single-file source from stored
|
|
446
|
+
* graph metadata without maintaining a second import parser.
|
|
447
|
+
*/
|
|
448
|
+
export function countLocalRuntimeImportReferences(sourceCode: string): number {
|
|
449
|
+
return findSourceImportReferences(sourceCode).filter((reference) =>
|
|
450
|
+
isLocalSpecifier(reference.specifier),
|
|
451
|
+
).length;
|
|
452
|
+
}
|
|
453
|
+
|
|
443
454
|
function findMatchingBrace(source: string, openIndex: number): number {
|
|
444
455
|
const structuralSource = maskSourceForStructure(source);
|
|
445
456
|
let depth = 0;
|
|
@@ -6,6 +6,10 @@ const BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
|
|
|
6
6
|
const ASSIGNMENT_SECRET_LITERAL_PATTERN =
|
|
7
7
|
/\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
|
|
8
8
|
const HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
|
|
9
|
+
const UUID_IDENTIFIER_PATTERN =
|
|
10
|
+
/^((?:[A-Za-z0-9]+[-_])*)?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
11
|
+
const SECRET_LABEL_PATTERN =
|
|
12
|
+
/(?:^|[-_])(?:api|auth|access|secret|token|key|password|credential|bearer|sk|pk|live)(?:[-_]|$)/i;
|
|
9
13
|
|
|
10
14
|
function shannonEntropy(value: string): number {
|
|
11
15
|
const counts = new Map<string, number>();
|
|
@@ -16,6 +20,13 @@ function shannonEntropy(value: string): number {
|
|
|
16
20
|
}, 0);
|
|
17
21
|
}
|
|
18
22
|
|
|
23
|
+
function isNonSecretUuidIdentifier(value: string): boolean {
|
|
24
|
+
const match = UUID_IDENTIFIER_PATTERN.exec(value);
|
|
25
|
+
if (!match) return false;
|
|
26
|
+
const label = match[1] ?? '';
|
|
27
|
+
return !SECRET_LABEL_PATTERN.test(label);
|
|
28
|
+
}
|
|
29
|
+
|
|
19
30
|
/**
|
|
20
31
|
* Returns the inline-secret findings in a string (empty if none). The throwing
|
|
21
32
|
* validator below and the workflows→plays migration validator both call this so
|
|
@@ -35,6 +46,9 @@ export function collectInlineSecretFindings(sourceCode: string): string[] {
|
|
|
35
46
|
}
|
|
36
47
|
for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
|
|
37
48
|
const literal = match[1] ?? '';
|
|
49
|
+
// UUID-bearing resource names are structured identifiers, not opaque
|
|
50
|
+
// credentials. Keep secret-looking labels on the conservative path.
|
|
51
|
+
if (isNonSecretUuidIdentifier(literal)) continue;
|
|
38
52
|
if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
|
|
39
53
|
findings.push('high-entropy string literal');
|
|
40
54
|
break;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
type SourceMetadata = {
|
|
4
|
+
name: string | null;
|
|
5
|
+
description: string | null;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type SourceContext = {
|
|
9
|
+
declarations: Map<string, ts.Expression>;
|
|
10
|
+
namedExports: Map<string, string>;
|
|
11
|
+
defaultExport: ts.Expression | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function unwrap(expression: ts.Expression | null): ts.Expression | null {
|
|
15
|
+
let current = expression;
|
|
16
|
+
while (current) {
|
|
17
|
+
if (
|
|
18
|
+
ts.isAsExpression(current) ||
|
|
19
|
+
ts.isSatisfiesExpression(current) ||
|
|
20
|
+
ts.isTypeAssertionExpression(current) ||
|
|
21
|
+
ts.isNonNullExpression(current) ||
|
|
22
|
+
ts.isParenthesizedExpression(current)
|
|
23
|
+
) {
|
|
24
|
+
current = current.expression;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
return current;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function buildContext(sourceFile: ts.SourceFile): SourceContext {
|
|
33
|
+
const declarations = new Map<string, ts.Expression>();
|
|
34
|
+
const namedExports = new Map<string, string>();
|
|
35
|
+
let defaultExport: ts.Expression | null = null;
|
|
36
|
+
|
|
37
|
+
for (const statement of sourceFile.statements) {
|
|
38
|
+
if (ts.isVariableStatement(statement)) {
|
|
39
|
+
const exported = statement.modifiers?.some(
|
|
40
|
+
(modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword,
|
|
41
|
+
);
|
|
42
|
+
const immutable = Boolean(
|
|
43
|
+
statement.declarationList.flags & ts.NodeFlags.Const,
|
|
44
|
+
);
|
|
45
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
46
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer)
|
|
47
|
+
continue;
|
|
48
|
+
if (immutable) {
|
|
49
|
+
declarations.set(declaration.name.text, declaration.initializer);
|
|
50
|
+
}
|
|
51
|
+
if (exported) {
|
|
52
|
+
namedExports.set(declaration.name.text, declaration.name.text);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (ts.isExportAssignment(statement) && !statement.isExportEquals) {
|
|
58
|
+
defaultExport = statement.expression;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (!ts.isExportDeclaration(statement) || !statement.exportClause) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (ts.isNamedExports(statement.exportClause)) {
|
|
65
|
+
for (const element of statement.exportClause.elements) {
|
|
66
|
+
namedExports.set(
|
|
67
|
+
element.name.text,
|
|
68
|
+
element.propertyName?.text ?? element.name.text,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { declarations, namedExports, defaultExport };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function resolveExpression(
|
|
77
|
+
expression: ts.Expression | null,
|
|
78
|
+
context: SourceContext,
|
|
79
|
+
seen = new Set<string>(),
|
|
80
|
+
): ts.Expression | null {
|
|
81
|
+
const unwrapped = unwrap(expression);
|
|
82
|
+
if (!unwrapped || !ts.isIdentifier(unwrapped)) return unwrapped;
|
|
83
|
+
if (seen.has(unwrapped.text)) return null;
|
|
84
|
+
seen.add(unwrapped.text);
|
|
85
|
+
return resolveExpression(
|
|
86
|
+
context.declarations.get(unwrapped.text) ?? null,
|
|
87
|
+
context,
|
|
88
|
+
seen,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function staticString(
|
|
93
|
+
expression: ts.Expression | undefined,
|
|
94
|
+
context: SourceContext,
|
|
95
|
+
seen = new Set<string>(),
|
|
96
|
+
trimResult = true,
|
|
97
|
+
): string | null {
|
|
98
|
+
const resolved = unwrap(expression ?? null);
|
|
99
|
+
if (
|
|
100
|
+
resolved &&
|
|
101
|
+
(ts.isStringLiteral(resolved) ||
|
|
102
|
+
ts.isNoSubstitutionTemplateLiteral(resolved))
|
|
103
|
+
) {
|
|
104
|
+
if (!resolved.text.trim()) return null;
|
|
105
|
+
return trimResult ? resolved.text.trim() : resolved.text;
|
|
106
|
+
}
|
|
107
|
+
if (
|
|
108
|
+
resolved &&
|
|
109
|
+
ts.isBinaryExpression(resolved) &&
|
|
110
|
+
resolved.operatorToken.kind === ts.SyntaxKind.PlusToken
|
|
111
|
+
) {
|
|
112
|
+
const left = staticString(resolved.left, context, new Set(seen), false);
|
|
113
|
+
const right = staticString(resolved.right, context, new Set(seen), false);
|
|
114
|
+
const value = left !== null && right !== null ? `${left}${right}` : null;
|
|
115
|
+
if (!value?.trim()) return null;
|
|
116
|
+
return trimResult ? value.trim() : value;
|
|
117
|
+
}
|
|
118
|
+
if (!resolved || !ts.isIdentifier(resolved) || seen.has(resolved.text)) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
seen.add(resolved.text);
|
|
122
|
+
return staticString(
|
|
123
|
+
context.declarations.get(resolved.text),
|
|
124
|
+
context,
|
|
125
|
+
seen,
|
|
126
|
+
trimResult,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function propertyName(name: ts.PropertyName): string | null {
|
|
131
|
+
return ts.isIdentifier(name) ||
|
|
132
|
+
ts.isStringLiteral(name) ||
|
|
133
|
+
ts.isNumericLiteral(name)
|
|
134
|
+
? name.text
|
|
135
|
+
: null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function objectStringProperty(
|
|
139
|
+
expression: ts.Expression | undefined,
|
|
140
|
+
key: string,
|
|
141
|
+
context: SourceContext,
|
|
142
|
+
): string | null {
|
|
143
|
+
const resolved = resolveExpression(expression ?? null, context);
|
|
144
|
+
if (!resolved || !ts.isObjectLiteralExpression(resolved)) return null;
|
|
145
|
+
for (const property of resolved.properties) {
|
|
146
|
+
if (
|
|
147
|
+
ts.isPropertyAssignment(property) &&
|
|
148
|
+
propertyName(property.name) === key
|
|
149
|
+
) {
|
|
150
|
+
return staticString(property.initializer, context);
|
|
151
|
+
}
|
|
152
|
+
if (
|
|
153
|
+
ts.isShorthandPropertyAssignment(property) &&
|
|
154
|
+
property.name.text === key
|
|
155
|
+
) {
|
|
156
|
+
return staticString(property.name, context);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isDefinePlayCall(call: ts.CallExpression): boolean {
|
|
163
|
+
const callee = unwrap(call.expression);
|
|
164
|
+
if (callee && ts.isIdentifier(callee)) {
|
|
165
|
+
return callee.text === 'definePlay' || callee.text === 'defineWorkflow';
|
|
166
|
+
}
|
|
167
|
+
return Boolean(
|
|
168
|
+
callee &&
|
|
169
|
+
ts.isPropertyAccessExpression(callee) &&
|
|
170
|
+
(callee.name.text === 'definePlay' ||
|
|
171
|
+
callee.name.text === 'defineWorkflow'),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function metadataFromExpression(
|
|
176
|
+
expression: ts.Expression | null,
|
|
177
|
+
context: SourceContext,
|
|
178
|
+
): SourceMetadata | null {
|
|
179
|
+
const resolved = resolveExpression(expression, context);
|
|
180
|
+
if (
|
|
181
|
+
!resolved ||
|
|
182
|
+
!ts.isCallExpression(resolved) ||
|
|
183
|
+
!isDefinePlayCall(resolved)
|
|
184
|
+
) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const [first] = resolved.arguments;
|
|
188
|
+
const name =
|
|
189
|
+
objectStringProperty(first, 'id', context) ?? staticString(first, context);
|
|
190
|
+
let description = objectStringProperty(first, 'description', context);
|
|
191
|
+
for (
|
|
192
|
+
let index = resolved.arguments.length - 1;
|
|
193
|
+
!description && index >= 2;
|
|
194
|
+
index -= 1
|
|
195
|
+
) {
|
|
196
|
+
description = objectStringProperty(
|
|
197
|
+
resolved.arguments[index],
|
|
198
|
+
'description',
|
|
199
|
+
context,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
return name || description ? { name, description } : null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function extractMetadata(sourceCode: string, exportName: string | null) {
|
|
206
|
+
const sourceFile = ts.createSourceFile(
|
|
207
|
+
'play.ts',
|
|
208
|
+
sourceCode,
|
|
209
|
+
ts.ScriptTarget.Latest,
|
|
210
|
+
true,
|
|
211
|
+
ts.ScriptKind.TS,
|
|
212
|
+
);
|
|
213
|
+
const context = buildContext(sourceFile);
|
|
214
|
+
const exportedExpression = exportName
|
|
215
|
+
? exportName === 'default'
|
|
216
|
+
? context.defaultExport
|
|
217
|
+
: (context.declarations.get(
|
|
218
|
+
context.namedExports.get(exportName) ?? exportName,
|
|
219
|
+
) ?? null)
|
|
220
|
+
: context.defaultExport;
|
|
221
|
+
const exported = metadataFromExpression(exportedExpression, context);
|
|
222
|
+
if (exported || exportName) return exported;
|
|
223
|
+
for (const expression of context.declarations.values()) {
|
|
224
|
+
const metadata = metadataFromExpression(expression, context);
|
|
225
|
+
if (metadata) return metadata;
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function extractDefinedPlayName(sourceCode: string): string | null {
|
|
231
|
+
return extractMetadata(sourceCode, null)?.name ?? null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function extractDefinedPlayDescriptionForExport(
|
|
235
|
+
sourceCode: string,
|
|
236
|
+
exportName: string,
|
|
237
|
+
): string | null {
|
|
238
|
+
return extractMetadata(sourceCode, exportName)?.description ?? null;
|
|
239
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -627,10 +627,13 @@ var SDK_RELEASE = {
|
|
|
627
627
|
// silently materializing them as unmatched results.
|
|
628
628
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
629
629
|
// automatically without blocking their current command.
|
|
630
|
-
|
|
631
|
-
|
|
630
|
+
// 0.1.241 hard-cuts published and launched plays to immutable cjs_node20
|
|
631
|
+
// artifacts. Older clients can submit esm_workers artifacts that the Absurd
|
|
632
|
+
// runtime intentionally no longer compiles at publish or launch time.
|
|
633
|
+
version: "0.1.241",
|
|
634
|
+
apiContract: "2026-07-immutable-cjs-play-artifacts-hard-cutover",
|
|
632
635
|
supportPolicy: {
|
|
633
|
-
latest: "0.1.
|
|
636
|
+
latest: "0.1.241",
|
|
634
637
|
minimumSupported: "0.1.53",
|
|
635
638
|
deprecatedBelow: "0.1.219",
|
|
636
639
|
commandMinimumSupported: [
|
|
@@ -1371,7 +1374,7 @@ function normalizePlayRunLedgerTerminalSource(value) {
|
|
|
1371
1374
|
var SECRET_REDACTION_PLACEHOLDER = "[REDACTED_SECRET]";
|
|
1372
1375
|
var BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/g;
|
|
1373
1376
|
var BASIC_CREDENTIAL_RE = /\bBasic\s+[A-Za-z0-9+/]{4,}={0,2}\b/g;
|
|
1374
|
-
var JWT_RE = /\b[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{1,}\.[A-Za-z0-9_-]{8,}\b/g;
|
|
1377
|
+
var JWT_RE = /\b[A-Za-z0-9_-]{8,16384}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{8,16384}\b/g;
|
|
1375
1378
|
var PRIVATE_KEY_RE = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
|
|
1376
1379
|
var SENSITIVE_FIELD_NAME = "authorization|cookie|password|(?:client[_-]?)?secret|(?:access|refresh)[_-]?token|(?:x[_-]?)?api[_-]?key|private[_-]?key|token|credentials";
|
|
1377
1380
|
var COMMON_SECRET_RE = /\b(?:[prs]k_(?:live|test)_|(?:pat|ghp|github_pat)_|xox[baprs]-)[A-Za-z0-9_./+=:-]{12,}\b/gi;
|
|
@@ -28040,6 +28043,8 @@ var PRIVATE_KEY_PATTERN = /-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY----
|
|
|
28040
28043
|
var BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
|
|
28041
28044
|
var ASSIGNMENT_SECRET_LITERAL_PATTERN = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
|
|
28042
28045
|
var HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
|
|
28046
|
+
var UUID_IDENTIFIER_PATTERN = /^((?:[A-Za-z0-9]+[-_])*)?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
28047
|
+
var SECRET_LABEL_PATTERN = /(?:^|[-_])(?:api|auth|access|secret|token|key|password|credential|bearer|sk|pk|live)(?:[-_]|$)/i;
|
|
28043
28048
|
function shannonEntropy(value) {
|
|
28044
28049
|
const counts = /* @__PURE__ */ new Map();
|
|
28045
28050
|
for (const char of value) counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
@@ -28048,6 +28053,12 @@ function shannonEntropy(value) {
|
|
|
28048
28053
|
return entropy - p * Math.log2(p);
|
|
28049
28054
|
}, 0);
|
|
28050
28055
|
}
|
|
28056
|
+
function isNonSecretUuidIdentifier(value) {
|
|
28057
|
+
const match = UUID_IDENTIFIER_PATTERN.exec(value);
|
|
28058
|
+
if (!match) return false;
|
|
28059
|
+
const label = match[1] ?? "";
|
|
28060
|
+
return !SECRET_LABEL_PATTERN.test(label);
|
|
28061
|
+
}
|
|
28051
28062
|
function collectInlineSecretFindings(sourceCode) {
|
|
28052
28063
|
const findings = [];
|
|
28053
28064
|
for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
|
|
@@ -28061,6 +28072,7 @@ function collectInlineSecretFindings(sourceCode) {
|
|
|
28061
28072
|
}
|
|
28062
28073
|
for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
|
|
28063
28074
|
const literal = match[1] ?? "";
|
|
28075
|
+
if (isNonSecretUuidIdentifier(literal)) continue;
|
|
28064
28076
|
if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
|
|
28065
28077
|
findings.push("high-entropy string literal");
|
|
28066
28078
|
break;
|
package/dist/cli/index.mjs
CHANGED
|
@@ -612,10 +612,13 @@ var SDK_RELEASE = {
|
|
|
612
612
|
// silently materializing them as unmatched results.
|
|
613
613
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
614
614
|
// automatically without blocking their current command.
|
|
615
|
-
|
|
616
|
-
|
|
615
|
+
// 0.1.241 hard-cuts published and launched plays to immutable cjs_node20
|
|
616
|
+
// artifacts. Older clients can submit esm_workers artifacts that the Absurd
|
|
617
|
+
// runtime intentionally no longer compiles at publish or launch time.
|
|
618
|
+
version: "0.1.241",
|
|
619
|
+
apiContract: "2026-07-immutable-cjs-play-artifacts-hard-cutover",
|
|
617
620
|
supportPolicy: {
|
|
618
|
-
latest: "0.1.
|
|
621
|
+
latest: "0.1.241",
|
|
619
622
|
minimumSupported: "0.1.53",
|
|
620
623
|
deprecatedBelow: "0.1.219",
|
|
621
624
|
commandMinimumSupported: [
|
|
@@ -1356,7 +1359,7 @@ function normalizePlayRunLedgerTerminalSource(value) {
|
|
|
1356
1359
|
var SECRET_REDACTION_PLACEHOLDER = "[REDACTED_SECRET]";
|
|
1357
1360
|
var BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/g;
|
|
1358
1361
|
var BASIC_CREDENTIAL_RE = /\bBasic\s+[A-Za-z0-9+/]{4,}={0,2}\b/g;
|
|
1359
|
-
var JWT_RE = /\b[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{1,}\.[A-Za-z0-9_-]{8,}\b/g;
|
|
1362
|
+
var JWT_RE = /\b[A-Za-z0-9_-]{8,16384}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{8,16384}\b/g;
|
|
1360
1363
|
var PRIVATE_KEY_RE = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
|
|
1361
1364
|
var SENSITIVE_FIELD_NAME = "authorization|cookie|password|(?:client[_-]?)?secret|(?:access|refresh)[_-]?token|(?:x[_-]?)?api[_-]?key|private[_-]?key|token|credentials";
|
|
1362
1365
|
var COMMON_SECRET_RE = /\b(?:[prs]k_(?:live|test)_|(?:pat|ghp|github_pat)_|xox[baprs]-)[A-Za-z0-9_./+=:-]{12,}\b/gi;
|
|
@@ -28088,6 +28091,8 @@ var PRIVATE_KEY_PATTERN = /-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY----
|
|
|
28088
28091
|
var BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
|
|
28089
28092
|
var ASSIGNMENT_SECRET_LITERAL_PATTERN = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
|
|
28090
28093
|
var HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
|
|
28094
|
+
var UUID_IDENTIFIER_PATTERN = /^((?:[A-Za-z0-9]+[-_])*)?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
28095
|
+
var SECRET_LABEL_PATTERN = /(?:^|[-_])(?:api|auth|access|secret|token|key|password|credential|bearer|sk|pk|live)(?:[-_]|$)/i;
|
|
28091
28096
|
function shannonEntropy(value) {
|
|
28092
28097
|
const counts = /* @__PURE__ */ new Map();
|
|
28093
28098
|
for (const char of value) counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
@@ -28096,6 +28101,12 @@ function shannonEntropy(value) {
|
|
|
28096
28101
|
return entropy - p * Math.log2(p);
|
|
28097
28102
|
}, 0);
|
|
28098
28103
|
}
|
|
28104
|
+
function isNonSecretUuidIdentifier(value) {
|
|
28105
|
+
const match = UUID_IDENTIFIER_PATTERN.exec(value);
|
|
28106
|
+
if (!match) return false;
|
|
28107
|
+
const label = match[1] ?? "";
|
|
28108
|
+
return !SECRET_LABEL_PATTERN.test(label);
|
|
28109
|
+
}
|
|
28099
28110
|
function collectInlineSecretFindings(sourceCode) {
|
|
28100
28111
|
const findings = [];
|
|
28101
28112
|
for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
|
|
@@ -28109,6 +28120,7 @@ function collectInlineSecretFindings(sourceCode) {
|
|
|
28109
28120
|
}
|
|
28110
28121
|
for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
|
|
28111
28122
|
const literal = match[1] ?? "";
|
|
28123
|
+
if (isNonSecretUuidIdentifier(literal)) continue;
|
|
28112
28124
|
if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
|
|
28113
28125
|
findings.push("high-entropy string literal");
|
|
28114
28126
|
break;
|
package/dist/index.js
CHANGED
|
@@ -426,10 +426,13 @@ var SDK_RELEASE = {
|
|
|
426
426
|
// silently materializing them as unmatched results.
|
|
427
427
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
428
428
|
// automatically without blocking their current command.
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
// 0.1.241 hard-cuts published and launched plays to immutable cjs_node20
|
|
430
|
+
// artifacts. Older clients can submit esm_workers artifacts that the Absurd
|
|
431
|
+
// runtime intentionally no longer compiles at publish or launch time.
|
|
432
|
+
version: "0.1.241",
|
|
433
|
+
apiContract: "2026-07-immutable-cjs-play-artifacts-hard-cutover",
|
|
431
434
|
supportPolicy: {
|
|
432
|
-
latest: "0.1.
|
|
435
|
+
latest: "0.1.241",
|
|
433
436
|
minimumSupported: "0.1.53",
|
|
434
437
|
deprecatedBelow: "0.1.219",
|
|
435
438
|
commandMinimumSupported: [
|
|
@@ -1170,7 +1173,7 @@ function normalizePlayRunLedgerTerminalSource(value) {
|
|
|
1170
1173
|
var SECRET_REDACTION_PLACEHOLDER = "[REDACTED_SECRET]";
|
|
1171
1174
|
var BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/g;
|
|
1172
1175
|
var BASIC_CREDENTIAL_RE = /\bBasic\s+[A-Za-z0-9+/]{4,}={0,2}\b/g;
|
|
1173
|
-
var JWT_RE = /\b[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{1,}\.[A-Za-z0-9_-]{8,}\b/g;
|
|
1176
|
+
var JWT_RE = /\b[A-Za-z0-9_-]{8,16384}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{8,16384}\b/g;
|
|
1174
1177
|
var PRIVATE_KEY_RE = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
|
|
1175
1178
|
var SENSITIVE_FIELD_NAME = "authorization|cookie|password|(?:client[_-]?)?secret|(?:access|refresh)[_-]?token|(?:x[_-]?)?api[_-]?key|private[_-]?key|token|credentials";
|
|
1176
1179
|
var COMMON_SECRET_RE = /\b(?:[prs]k_(?:live|test)_|(?:pat|ghp|github_pat)_|xox[baprs]-)[A-Za-z0-9_./+=:-]{12,}\b/gi;
|
package/dist/index.mjs
CHANGED
|
@@ -356,10 +356,13 @@ var SDK_RELEASE = {
|
|
|
356
356
|
// silently materializing them as unmatched results.
|
|
357
357
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
358
358
|
// automatically without blocking their current command.
|
|
359
|
-
|
|
360
|
-
|
|
359
|
+
// 0.1.241 hard-cuts published and launched plays to immutable cjs_node20
|
|
360
|
+
// artifacts. Older clients can submit esm_workers artifacts that the Absurd
|
|
361
|
+
// runtime intentionally no longer compiles at publish or launch time.
|
|
362
|
+
version: "0.1.241",
|
|
363
|
+
apiContract: "2026-07-immutable-cjs-play-artifacts-hard-cutover",
|
|
361
364
|
supportPolicy: {
|
|
362
|
-
latest: "0.1.
|
|
365
|
+
latest: "0.1.241",
|
|
363
366
|
minimumSupported: "0.1.53",
|
|
364
367
|
deprecatedBelow: "0.1.219",
|
|
365
368
|
commandMinimumSupported: [
|
|
@@ -1100,7 +1103,7 @@ function normalizePlayRunLedgerTerminalSource(value) {
|
|
|
1100
1103
|
var SECRET_REDACTION_PLACEHOLDER = "[REDACTED_SECRET]";
|
|
1101
1104
|
var BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/g;
|
|
1102
1105
|
var BASIC_CREDENTIAL_RE = /\bBasic\s+[A-Za-z0-9+/]{4,}={0,2}\b/g;
|
|
1103
|
-
var JWT_RE = /\b[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{1,}\.[A-Za-z0-9_-]{8,}\b/g;
|
|
1106
|
+
var JWT_RE = /\b[A-Za-z0-9_-]{8,16384}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{8,16384}\b/g;
|
|
1104
1107
|
var PRIVATE_KEY_RE = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
|
|
1105
1108
|
var SENSITIVE_FIELD_NAME = "authorization|cookie|password|(?:client[_-]?)?secret|(?:access|refresh)[_-]?token|(?:x[_-]?)?api[_-]?key|private[_-]?key|token|credentials";
|
|
1106
1109
|
var COMMON_SECRET_RE = /\b(?:[prs]k_(?:live|test)_|(?:pat|ghp|github_pat)_|xox[baprs]-)[A-Za-z0-9_./+=:-]{12,}\b/gi;
|
|
@@ -80,6 +80,8 @@ var PRIVATE_KEY_PATTERN = /-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY----
|
|
|
80
80
|
var BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
|
|
81
81
|
var ASSIGNMENT_SECRET_LITERAL_PATTERN = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
|
|
82
82
|
var HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
|
|
83
|
+
var UUID_IDENTIFIER_PATTERN = /^((?:[A-Za-z0-9]+[-_])*)?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
84
|
+
var SECRET_LABEL_PATTERN = /(?:^|[-_])(?:api|auth|access|secret|token|key|password|credential|bearer|sk|pk|live)(?:[-_]|$)/i;
|
|
83
85
|
function shannonEntropy(value) {
|
|
84
86
|
const counts = /* @__PURE__ */ new Map();
|
|
85
87
|
for (const char of value) counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
@@ -88,6 +90,12 @@ function shannonEntropy(value) {
|
|
|
88
90
|
return entropy - p * Math.log2(p);
|
|
89
91
|
}, 0);
|
|
90
92
|
}
|
|
93
|
+
function isNonSecretUuidIdentifier(value) {
|
|
94
|
+
const match = UUID_IDENTIFIER_PATTERN.exec(value);
|
|
95
|
+
if (!match) return false;
|
|
96
|
+
const label = match[1] ?? "";
|
|
97
|
+
return !SECRET_LABEL_PATTERN.test(label);
|
|
98
|
+
}
|
|
91
99
|
function collectInlineSecretFindings(sourceCode) {
|
|
92
100
|
const findings = [];
|
|
93
101
|
for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
|
|
@@ -101,6 +109,7 @@ function collectInlineSecretFindings(sourceCode) {
|
|
|
101
109
|
}
|
|
102
110
|
for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
|
|
103
111
|
const literal = match[1] ?? "";
|
|
112
|
+
if (isNonSecretUuidIdentifier(literal)) continue;
|
|
104
113
|
if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
|
|
105
114
|
findings.push("high-entropy string literal");
|
|
106
115
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepline",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.241",
|
|
4
4
|
"description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -62,6 +62,6 @@
|
|
|
62
62
|
"typescript": "^5.9.3"
|
|
63
63
|
},
|
|
64
64
|
"deepline": {
|
|
65
|
-
"apiContract": "2026-
|
|
65
|
+
"apiContract": "2026-07-immutable-cjs-play-artifacts-hard-cutover"
|
|
66
66
|
}
|
|
67
67
|
}
|