deepline 0.1.289 → 0.1.291

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.
@@ -443,6 +443,8 @@ export type RunsListOptions = {
443
443
  play?: string;
444
444
  status?: string;
445
445
  limit?: number;
446
+ /** Zero-based page offset. Requires `play` because status-only inventory is not paginated. */
447
+ offset?: number;
446
448
  };
447
449
 
448
450
  /** Options for `client.runs.get(...)`. */
@@ -2952,6 +2954,21 @@ export class DeeplineClient {
2952
2954
  if (typeof options.limit === 'number' && Number.isFinite(options.limit)) {
2953
2955
  params.set('limit', String(Math.max(1, Math.floor(options.limit))));
2954
2956
  }
2957
+ if (options.offset !== undefined) {
2958
+ if (
2959
+ !Number.isFinite(options.offset) ||
2960
+ !Number.isInteger(options.offset) ||
2961
+ options.offset < 0
2962
+ ) {
2963
+ throw new Error(
2964
+ 'runs.list options.offset must be a non-negative integer.',
2965
+ );
2966
+ }
2967
+ if (!playName && options.offset > 0) {
2968
+ throw new Error('runs.list options.offset requires options.play.');
2969
+ }
2970
+ params.set('offset', String(options.offset));
2971
+ }
2955
2972
  if (!playName && !status) {
2956
2973
  throw new Error('runs.list requires options.play or options.status.');
2957
2974
  }
@@ -155,7 +155,7 @@ export const SDK_RELEASE = {
155
155
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
156
156
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
157
157
  // Operators use the checkout-local deepline-admin binary instead.
158
- version: '0.1.289',
158
+ version: '0.1.291',
159
159
  contracts: {
160
160
  api: {
161
161
  name: 'sdk-http-api',
@@ -358,6 +358,7 @@ type RuntimeApiRequest =
358
358
  export type WorkerRuntimeApiContext = {
359
359
  baseUrl: string;
360
360
  executorToken: string;
361
+ boundary?: 'app_runtime' | 'receipt_gateway';
361
362
  integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
362
363
  vercelProtectionBypassToken?: string | null;
363
364
  runtimeTestFaultHeader?: string | null;
@@ -874,6 +875,7 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
874
875
  attemptStartedAt: number;
875
876
  httpStatus?: number;
876
877
  retryAfterMs?: number | null;
878
+ boundaryLabel: string;
877
879
  }): Promise<void> {
878
880
  if (!isRequestTimeoutAbort(input.error)) {
879
881
  recordAppRuntimeRetryFailure({
@@ -936,9 +938,18 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
936
938
  action: input.action,
937
939
  attempts: input.attempt,
938
940
  cause: input.error,
941
+ boundaryLabel: input.boundaryLabel,
939
942
  });
940
943
  }
941
944
 
945
+ function runtimeApiBoundaryLabel(
946
+ context: Pick<WorkerRuntimeApiContext, 'boundary'>,
947
+ ): string {
948
+ return context.boundary === 'receipt_gateway'
949
+ ? 'Runtime receipt gateway'
950
+ : 'App runtime API';
951
+ }
952
+
942
953
  export class AppRuntimeApiTransportError extends Error {
943
954
  readonly action: RuntimeApiRequest['action'];
944
955
  readonly attempts: number;
@@ -947,11 +958,12 @@ export class AppRuntimeApiTransportError extends Error {
947
958
  action: RuntimeApiRequest['action'];
948
959
  attempts: number;
949
960
  cause: unknown;
961
+ boundaryLabel?: string;
950
962
  }) {
951
963
  const causeMessage =
952
964
  input.cause instanceof Error ? input.cause.message : String(input.cause);
953
965
  super(
954
- `App runtime API transport exhausted action=${input.action} attempts=${input.attempts}: ${causeMessage}`,
966
+ `${input.boundaryLabel ?? 'App runtime API'} transport exhausted action=${input.action} attempts=${input.attempts}: ${causeMessage}`,
955
967
  { cause: input.cause },
956
968
  );
957
969
  this.name = 'AppRuntimeApiTransportError';
@@ -979,9 +991,10 @@ export class AppRuntimeApiResponseError extends Error {
979
991
  requestId?: string | null;
980
992
  retryable: boolean;
981
993
  detail: string;
994
+ boundaryLabel?: string;
982
995
  }) {
983
996
  super(
984
- `App runtime API ${input.action} failed with status ${input.status}` +
997
+ `${input.boundaryLabel ?? 'App runtime API'} ${input.action} failed with status ${input.status}` +
985
998
  `${input.code ? ` code=${input.code}` : ''}` +
986
999
  `${input.requestId ? ` request_id=${input.requestId}` : ''}: ` +
987
1000
  input.detail,
@@ -1012,6 +1025,7 @@ async function postAppRuntimeApi<TResponse>(
1012
1025
  throw new Error('Worker runtime API requires executorToken.');
1013
1026
  }
1014
1027
  const runtimeFetch = context.fetch ?? fetch;
1028
+ const boundaryLabel = runtimeApiBoundaryLabel(context);
1015
1029
  const vercelHeaders = await vercelProtectionBypassHeaders({
1016
1030
  baseUrl,
1017
1031
  token: context.vercelProtectionBypassToken,
@@ -1091,6 +1105,7 @@ async function postAppRuntimeApi<TResponse>(
1091
1105
  action: body.action,
1092
1106
  attempts: attempt,
1093
1107
  cause: error,
1108
+ boundaryLabel,
1094
1109
  });
1095
1110
  }
1096
1111
  if (response.ok) {
@@ -1114,6 +1129,7 @@ async function postAppRuntimeApi<TResponse>(
1114
1129
  telemetry: retryTelemetry,
1115
1130
  attemptStartedAt,
1116
1131
  httpStatus: response.status,
1132
+ boundaryLabel,
1117
1133
  });
1118
1134
  continue;
1119
1135
  }
@@ -1143,7 +1159,7 @@ async function postAppRuntimeApi<TResponse>(
1143
1159
  finalAttemptStartedAt: attemptStartedAt,
1144
1160
  });
1145
1161
  throw new Error(
1146
- `App runtime API ${body.action} failed with status ${response.status}: response body timed out`,
1162
+ `${boundaryLabel} ${body.action} failed with status ${response.status}: response body timed out`,
1147
1163
  { cause: error },
1148
1164
  );
1149
1165
  }
@@ -1156,6 +1172,7 @@ async function postAppRuntimeApi<TResponse>(
1156
1172
  attemptStartedAt,
1157
1173
  httpStatus: response.status,
1158
1174
  retryAfterMs: appRuntimeRetryAfterMs(response, ''),
1175
+ boundaryLabel,
1159
1176
  });
1160
1177
  continue;
1161
1178
  }
@@ -1221,10 +1238,11 @@ async function postAppRuntimeApi<TResponse>(
1221
1238
  body: responseText,
1222
1239
  }),
1223
1240
  detail: summarizeAppRuntimeErrorBody(responseText),
1241
+ boundaryLabel,
1224
1242
  });
1225
1243
  }
1226
1244
 
1227
- throw new Error(`App runtime API ${body.action} failed after retries.`);
1245
+ throw new Error(`${boundaryLabel} ${body.action} failed after retries.`);
1228
1246
  }
1229
1247
 
1230
1248
  type SignedR2ReadUrlResponse = {
@@ -54,7 +54,7 @@ const DAYTONA_UPLOAD_ATTEMPT_DEADLINE_MS = 90_000;
54
54
  const RUNTIME_POSTGRES_CONNECT_RETRY_PATTERN =
55
55
  /\bRuntime Postgres\b.*\b(connection timed out|connect timeout|ETIMEDOUT|ECONNRESET|ECONNREFUSED|Connection terminated|Connection ended unexpectedly)\b/i;
56
56
  const RUNTIME_RECEIPT_GATEWAY_TRANSPORT_RETRY_PATTERN =
57
- /AppRuntimeApiTransportError: App runtime API transport exhausted action=(?:get|claim|mark|complete|fail|heartbeat|release|skip)_runtime_step_receipts?/i;
57
+ /AppRuntimeApiTransportError: (?:App runtime API|Runtime receipt gateway) transport exhausted action=(?:get|claim|mark|complete|fail|heartbeat|release|skip)_runtime_step_receipts?/i;
58
58
  const RUNTIME_API_TRANSPORT_RETRY_PATTERN =
59
59
  /\bRuntime API request to .+ failed before receiving a response:\s*(?:fetch failed|connection (?:terminated|timed out|closed|reset)|ECONNRESET|ETIMEDOUT|ECONNREFUSED)\b/i;
60
60
  const DAYTONA_INFRASTRUCTURE_RETRY_PATTERN =
@@ -78,6 +78,7 @@ import {
78
78
  sameOwnerTerminalAttemptEpochSql,
79
79
  } from './sheet-attempt-sql';
80
80
  import type { MapRowOutcome } from './durability-store';
81
+ import { RUNTIME_CAPACITY_POLICY } from './runtime-capacity-policy';
81
82
  import {
82
83
  normalizeRuntimeMapInputIndex,
83
84
  prepareRuntimeSheetRowsForJsonTransport,
@@ -349,7 +350,8 @@ const RUNTIME_POSTGRES_RECEIPT_ADMISSION_MAX_QUEUED = 2_000;
349
350
  // memory independently from lightweight receipt requests; overflow is a
350
351
  // retryable 503 and waits outside this long-lived gateway process.
351
352
  const RUNTIME_POSTGRES_SHEET_ADMISSION_MAX_QUEUED = 4;
352
- const RUNTIME_POSTGRES_RECEIPT_ADMISSION_TIMEOUT_MS = 10_000;
353
+ const RUNTIME_POSTGRES_RECEIPT_ADMISSION_TIMEOUT_MS =
354
+ RUNTIME_CAPACITY_POLICY.receiptGateway.admissionTimeoutMs;
353
355
  const RECEIPT_STATUS_QUEUED = RECEIPT_STATUS_CODE.queued;
354
356
  const RECEIPT_STATUS_PENDING = RECEIPT_STATUS_CODE.pending;
355
357
  const RECEIPT_STATUS_RUNNING = RECEIPT_STATUS_CODE.running;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * One fixed capacity policy for the Absurd runtime.
3
+ *
4
+ * Postgres owns backlog. Workers drain it at a predictable rate; queue depth
5
+ * never creates more runtime or receipt traffic by itself. Keep these values
6
+ * together so the worker, queue policy, scaler, gateway, and tests cannot
7
+ * independently invent concurrency or timeout budgets.
8
+ */
9
+ export const RUNTIME_CAPACITY_POLICY = {
10
+ absurd: {
11
+ activeLaneMachines: 2,
12
+ workerSlotsPerMachine: 8,
13
+ perOrgConcurrency: 4,
14
+ },
15
+ receiptGateway: {
16
+ admissionTimeoutMs: 10_000,
17
+ requestTimeoutMs: 30_000,
18
+ },
19
+ } as const;
20
+
21
+ export const ABSURD_GLOBAL_RUN_CONCURRENCY =
22
+ RUNTIME_CAPACITY_POLICY.absurd.activeLaneMachines *
23
+ RUNTIME_CAPACITY_POLICY.absurd.workerSlotsPerMachine;
24
+
25
+ /**
26
+ * Active releases retain fixed capacity. Historical releases stay cold until
27
+ * they have immediately runnable or currently claimed work.
28
+ */
29
+ export function desiredAbsurdLaneMachines(input: {
30
+ active: boolean;
31
+ claimableRuns: number;
32
+ runningRuns?: number;
33
+ }): number {
34
+ if (input.active) {
35
+ return RUNTIME_CAPACITY_POLICY.absurd.activeLaneMachines;
36
+ }
37
+ return input.claimableRuns > 0 || (input.runningRuns ?? 0) > 0 ? 1 : 0;
38
+ }
39
+
40
+ export function assertRuntimeCapacityPolicy(): void {
41
+ if (
42
+ RUNTIME_CAPACITY_POLICY.receiptGateway.requestTimeoutMs <=
43
+ RUNTIME_CAPACITY_POLICY.receiptGateway.admissionTimeoutMs
44
+ ) {
45
+ throw new Error(
46
+ 'Receipt gateway request timeout must exceed its admission timeout.',
47
+ );
48
+ }
49
+ if (
50
+ RUNTIME_CAPACITY_POLICY.absurd.perOrgConcurrency >
51
+ ABSURD_GLOBAL_RUN_CONCURRENCY
52
+ ) {
53
+ throw new Error(
54
+ 'Per-org runtime concurrency cannot exceed global runtime concurrency.',
55
+ );
56
+ }
57
+ }
58
+
59
+ assertRuntimeCapacityPolicy();
@@ -0,0 +1,110 @@
1
+ export const LEGACY_WEBHOOK_RETRY_CODES = [
2
+ 'ACTIVE_CONCURRENCY_LIMIT',
3
+ 'SCHEDULER_CAPACITY',
4
+ 'TRANSIENT_LAUNCH_FAILURE',
5
+ ] as const;
6
+
7
+ export type LegacyWebhookRetryCode =
8
+ (typeof LEGACY_WEBHOOK_RETRY_CODES)[number];
9
+
10
+ export const LEGACY_WEBHOOK_QUARANTINE_CODES = [
11
+ 'PLAY_DEFINITION_MISSING',
12
+ 'PLAY_BINDING_MISSING',
13
+ 'PLAY_BINDING_DISABLED',
14
+ 'PLAY_REVISION_MISSING',
15
+ 'PLAY_REVISION_MISMATCH',
16
+ 'ARTIFACT_RUNTIME_INCOMPATIBLE',
17
+ 'ARTIFACT_REFERENCE_MISSING',
18
+ 'WEBHOOK_PAYLOAD_INVALID',
19
+ 'PLAY_PRE_RUN_VALIDATION_FAILED',
20
+ 'RUN_IDENTITY_CONFLICT',
21
+ 'MAX_DELIVERY_ATTEMPTS_EXCEEDED',
22
+ ] as const;
23
+
24
+ export type LegacyWebhookQuarantineCode =
25
+ (typeof LEGACY_WEBHOOK_QUARANTINE_CODES)[number];
26
+
27
+ export function isLegacyWebhookQuarantineCode(
28
+ value: unknown,
29
+ ): value is LegacyWebhookQuarantineCode {
30
+ return (
31
+ typeof value === 'string' &&
32
+ (LEGACY_WEBHOOK_QUARANTINE_CODES as readonly string[]).includes(value)
33
+ );
34
+ }
35
+
36
+ export type LegacyWebhookQuarantineFailure =
37
+ | { code: 'PLAY_DEFINITION_MISSING' }
38
+ | { code: 'PLAY_BINDING_MISSING' }
39
+ | { code: 'PLAY_BINDING_DISABLED'; actualStatus: string }
40
+ | { code: 'PLAY_REVISION_MISSING' }
41
+ | {
42
+ code: 'PLAY_REVISION_MISMATCH';
43
+ expectedDefinitionId: string;
44
+ actualDefinitionId: string;
45
+ }
46
+ | {
47
+ code: 'ARTIFACT_RUNTIME_INCOMPATIBLE';
48
+ expectedArtifactKind: 'cjs_node20';
49
+ actualArtifactKind: string;
50
+ }
51
+ | { code: 'ARTIFACT_REFERENCE_MISSING' }
52
+ | {
53
+ code: 'WEBHOOK_PAYLOAD_INVALID';
54
+ reason: 'unsupported_shape';
55
+ }
56
+ | { code: 'PLAY_PRE_RUN_VALIDATION_FAILED' }
57
+ | { code: 'RUN_IDENTITY_CONFLICT' }
58
+ | { code: 'MAX_DELIVERY_ATTEMPTS_EXCEEDED' };
59
+
60
+ export type LegacyWebhookDrainState =
61
+ | { kind: 'pending' }
62
+ | {
63
+ kind: 'leased';
64
+ leaseOwner: string;
65
+ leaseExpiresAt: number;
66
+ attempts: number;
67
+ }
68
+ | {
69
+ kind: 'retry';
70
+ failureCode: LegacyWebhookRetryCode;
71
+ retryAt: number;
72
+ attempts: number;
73
+ error: string;
74
+ }
75
+ | {
76
+ kind: 'quarantined';
77
+ failure: LegacyWebhookQuarantineFailure;
78
+ quarantinedAt: number;
79
+ attempts: number;
80
+ error: string;
81
+ }
82
+ | { kind: 'launched'; launchedAt: number }
83
+ | {
84
+ kind: 'migrated';
85
+ migratedAt: number;
86
+ migratedWebhookEventId: string;
87
+ migratedAbsurdTaskId: string;
88
+ }
89
+ | {
90
+ kind: 'retired';
91
+ retiredAt: number;
92
+ retiredBy: string;
93
+ reason: string;
94
+ };
95
+
96
+ export type LegacyWebhookDrainOutcome =
97
+ | { kind: 'started' }
98
+ | { kind: 'quarantined'; failure: LegacyWebhookQuarantineFailure }
99
+ | { kind: 'retry'; failureCode: LegacyWebhookRetryCode }
100
+ | { kind: 'lease_lost' };
101
+
102
+ export function isLegacyWebhookPayload(
103
+ value: unknown,
104
+ ): value is Record<string, unknown> | unknown[] {
105
+ return Array.isArray(value) || (value !== null && typeof value === 'object');
106
+ }
107
+
108
+ export function assertNever(value: never, context: string): never {
109
+ throw new Error(`${context}: ${JSON.stringify(value)}`);
110
+ }
@@ -8,6 +8,8 @@ const ASSIGNMENT_SECRET_LITERAL_PATTERN =
8
8
  const HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
9
9
  const UUID_IDENTIFIER_PATTERN =
10
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 BOOTSTRAP_RESOURCE_IDENTIFIER_PATTERN =
12
+ /^bootstrap-[0-9a-f]{32}(?:\/[a-z0-9][a-z0-9_-]{0,127})?$/i;
11
13
  const SECRET_LABEL_PATTERN =
12
14
  /(?:^|[-_])(?:api|auth|access|secret|token|key|password|credential|bearer|sk|pk|live)(?:[-_]|$)/i;
13
15
 
@@ -27,6 +29,10 @@ function isNonSecretUuidIdentifier(value: string): boolean {
27
29
  return !SECRET_LABEL_PATTERN.test(label);
28
30
  }
29
31
 
32
+ function isNonSecretBootstrapResourceIdentifier(value: string): boolean {
33
+ return BOOTSTRAP_RESOURCE_IDENTIFIER_PATTERN.test(value);
34
+ }
35
+
30
36
  /**
31
37
  * Returns the inline-secret findings in a string (empty if none). The throwing
32
38
  * validator below and the workflows→plays migration validator both call this so
@@ -49,6 +55,10 @@ export function collectInlineSecretFindings(sourceCode: string): string[] {
49
55
  // UUID-bearing resource names are structured identifiers, not opaque
50
56
  // credentials. Keep secret-looking labels on the conservative path.
51
57
  if (isNonSecretUuidIdentifier(literal)) continue;
58
+ // Named CI orgs use a deterministic public `bootstrap-<sha256-prefix>`
59
+ // slug. It is an address, not a credential, and owner-qualified ctx.runPlay
60
+ // references must embed it as a literal for static child resolution.
61
+ if (isNonSecretBootstrapResourceIdentifier(literal)) continue;
52
62
  if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
53
63
  findings.push('high-entropy string literal');
54
64
  break;
package/dist/cli/index.js CHANGED
@@ -718,7 +718,7 @@ var SDK_RELEASE = {
718
718
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
719
719
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
720
720
  // Operators use the checkout-local deepline-admin binary instead.
721
- version: "0.1.289",
721
+ version: "0.1.291",
722
722
  contracts: {
723
723
  api: {
724
724
  name: "sdk-http-api",
@@ -4424,6 +4424,17 @@ var DeeplineClient = class {
4424
4424
  if (typeof options.limit === "number" && Number.isFinite(options.limit)) {
4425
4425
  params.set("limit", String(Math.max(1, Math.floor(options.limit))));
4426
4426
  }
4427
+ if (options.offset !== void 0) {
4428
+ if (!Number.isFinite(options.offset) || !Number.isInteger(options.offset) || options.offset < 0) {
4429
+ throw new Error(
4430
+ "runs.list options.offset must be a non-negative integer."
4431
+ );
4432
+ }
4433
+ if (!playName && options.offset > 0) {
4434
+ throw new Error("runs.list options.offset requires options.play.");
4435
+ }
4436
+ params.set("offset", String(options.offset));
4437
+ }
4427
4438
  if (!playName && !status) {
4428
4439
  throw new Error("runs.list requires options.play or options.status.");
4429
4440
  }
@@ -16754,9 +16765,11 @@ async function handleRunGet(args) {
16754
16765
  return 0;
16755
16766
  }
16756
16767
  async function handleRunsList(args) {
16757
- const usage = "Usage: deepline runs list [--play <play-name>] [--status <status>] [--json]";
16768
+ const usage = "Usage: deepline runs list [--play <play-name>] [--status <status>] [--limit <count>] [--offset <count>] [--json]";
16758
16769
  let playName = null;
16759
16770
  let statusFilter = null;
16771
+ let limit;
16772
+ let offset;
16760
16773
  for (let index = 0; index < args.length; index += 1) {
16761
16774
  const arg = args[index];
16762
16775
  if ((arg === "--play" || arg === "--name") && args[index + 1]) {
@@ -16767,6 +16780,24 @@ async function handleRunsList(args) {
16767
16780
  statusFilter = args[++index].trim().toLowerCase();
16768
16781
  continue;
16769
16782
  }
16783
+ if (arg === "--limit" || arg === "--offset") {
16784
+ const rawValue = args[index + 1];
16785
+ const parsed = rawValue && /^\d+$/.test(rawValue) ? Number.parseInt(rawValue, 10) : Number.NaN;
16786
+ const valid = Number.isSafeInteger(parsed) && (arg === "--limit" ? parsed > 0 : parsed >= 0);
16787
+ if (!valid) {
16788
+ console.error(
16789
+ `${arg} must be ${arg === "--limit" ? "a positive" : "a non-negative"} integer.`
16790
+ );
16791
+ return 1;
16792
+ }
16793
+ if (arg === "--limit") {
16794
+ limit = parsed;
16795
+ } else {
16796
+ offset = parsed;
16797
+ }
16798
+ index += 1;
16799
+ continue;
16800
+ }
16770
16801
  if (arg === "--json" || arg === "--compact") {
16771
16802
  continue;
16772
16803
  }
@@ -16775,10 +16806,16 @@ async function handleRunsList(args) {
16775
16806
  console.error(usage);
16776
16807
  return 1;
16777
16808
  }
16809
+ if ((offset ?? 0) > 0 && !playName) {
16810
+ console.error("--offset requires --play.");
16811
+ return 1;
16812
+ }
16778
16813
  const client2 = new DeeplineClient();
16779
16814
  const runs = (await client2.runs.list({
16780
16815
  ...playName ? { play: playName } : {},
16781
- ...statusFilter ? { status: statusFilter } : {}
16816
+ ...statusFilter ? { status: statusFilter } : {},
16817
+ ...limit !== void 0 ? { limit } : {},
16818
+ ...offset !== void 0 ? { offset } : {}
16782
16819
  })).map((run) => ({
16783
16820
  runId: run.workflowId,
16784
16821
  workflowId: run.workflowId,
@@ -18335,10 +18372,12 @@ Examples:
18335
18372
  deepline runs list --play my-play --status failed --compact --json
18336
18373
  deepline runs list --status running --compact --json
18337
18374
  `
18338
- ).option("--play <name>", "Play name to filter runs").option("--status <status>", "Filter by run status").option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
18375
+ ).option("--play <name>", "Play name to filter runs").option("--status <status>", "Filter by run status").option("--limit <count>", "Maximum runs to return").option("--offset <count>", "Zero-based page offset (requires --play)").option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
18339
18376
  process.exitCode = await handleRunsList([
18340
18377
  ...options.play ? ["--play", options.play] : [],
18341
18378
  ...options.status ? ["--status", options.status] : [],
18379
+ ...options.limit ? ["--limit", options.limit] : [],
18380
+ ...options.offset ? ["--offset", options.offset] : [],
18342
18381
  ...options.compact ? ["--compact"] : [],
18343
18382
  ...options.json ? ["--json"] : []
18344
18383
  ]);
@@ -29296,6 +29335,7 @@ var BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
29296
29335
  var ASSIGNMENT_SECRET_LITERAL_PATTERN = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
29297
29336
  var HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
29298
29337
  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;
29338
+ var BOOTSTRAP_RESOURCE_IDENTIFIER_PATTERN = /^bootstrap-[0-9a-f]{32}(?:\/[a-z0-9][a-z0-9_-]{0,127})?$/i;
29299
29339
  var SECRET_LABEL_PATTERN = /(?:^|[-_])(?:api|auth|access|secret|token|key|password|credential|bearer|sk|pk|live)(?:[-_]|$)/i;
29300
29340
  function shannonEntropy(value) {
29301
29341
  const counts = /* @__PURE__ */ new Map();
@@ -29311,6 +29351,9 @@ function isNonSecretUuidIdentifier(value) {
29311
29351
  const label = match[1] ?? "";
29312
29352
  return !SECRET_LABEL_PATTERN.test(label);
29313
29353
  }
29354
+ function isNonSecretBootstrapResourceIdentifier(value) {
29355
+ return BOOTSTRAP_RESOURCE_IDENTIFIER_PATTERN.test(value);
29356
+ }
29314
29357
  function collectInlineSecretFindings(sourceCode) {
29315
29358
  const findings = [];
29316
29359
  for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
@@ -29325,6 +29368,7 @@ function collectInlineSecretFindings(sourceCode) {
29325
29368
  for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
29326
29369
  const literal = match[1] ?? "";
29327
29370
  if (isNonSecretUuidIdentifier(literal)) continue;
29371
+ if (isNonSecretBootstrapResourceIdentifier(literal)) continue;
29328
29372
  if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
29329
29373
  findings.push("high-entropy string literal");
29330
29374
  break;
@@ -703,7 +703,7 @@ var SDK_RELEASE = {
703
703
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
704
704
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
705
705
  // Operators use the checkout-local deepline-admin binary instead.
706
- version: "0.1.289",
706
+ version: "0.1.291",
707
707
  contracts: {
708
708
  api: {
709
709
  name: "sdk-http-api",
@@ -4409,6 +4409,17 @@ var DeeplineClient = class {
4409
4409
  if (typeof options.limit === "number" && Number.isFinite(options.limit)) {
4410
4410
  params.set("limit", String(Math.max(1, Math.floor(options.limit))));
4411
4411
  }
4412
+ if (options.offset !== void 0) {
4413
+ if (!Number.isFinite(options.offset) || !Number.isInteger(options.offset) || options.offset < 0) {
4414
+ throw new Error(
4415
+ "runs.list options.offset must be a non-negative integer."
4416
+ );
4417
+ }
4418
+ if (!playName && options.offset > 0) {
4419
+ throw new Error("runs.list options.offset requires options.play.");
4420
+ }
4421
+ params.set("offset", String(options.offset));
4422
+ }
4412
4423
  if (!playName && !status) {
4413
4424
  throw new Error("runs.list requires options.play or options.status.");
4414
4425
  }
@@ -16783,9 +16794,11 @@ async function handleRunGet(args) {
16783
16794
  return 0;
16784
16795
  }
16785
16796
  async function handleRunsList(args) {
16786
- const usage = "Usage: deepline runs list [--play <play-name>] [--status <status>] [--json]";
16797
+ const usage = "Usage: deepline runs list [--play <play-name>] [--status <status>] [--limit <count>] [--offset <count>] [--json]";
16787
16798
  let playName = null;
16788
16799
  let statusFilter = null;
16800
+ let limit;
16801
+ let offset;
16789
16802
  for (let index = 0; index < args.length; index += 1) {
16790
16803
  const arg = args[index];
16791
16804
  if ((arg === "--play" || arg === "--name") && args[index + 1]) {
@@ -16796,6 +16809,24 @@ async function handleRunsList(args) {
16796
16809
  statusFilter = args[++index].trim().toLowerCase();
16797
16810
  continue;
16798
16811
  }
16812
+ if (arg === "--limit" || arg === "--offset") {
16813
+ const rawValue = args[index + 1];
16814
+ const parsed = rawValue && /^\d+$/.test(rawValue) ? Number.parseInt(rawValue, 10) : Number.NaN;
16815
+ const valid = Number.isSafeInteger(parsed) && (arg === "--limit" ? parsed > 0 : parsed >= 0);
16816
+ if (!valid) {
16817
+ console.error(
16818
+ `${arg} must be ${arg === "--limit" ? "a positive" : "a non-negative"} integer.`
16819
+ );
16820
+ return 1;
16821
+ }
16822
+ if (arg === "--limit") {
16823
+ limit = parsed;
16824
+ } else {
16825
+ offset = parsed;
16826
+ }
16827
+ index += 1;
16828
+ continue;
16829
+ }
16799
16830
  if (arg === "--json" || arg === "--compact") {
16800
16831
  continue;
16801
16832
  }
@@ -16804,10 +16835,16 @@ async function handleRunsList(args) {
16804
16835
  console.error(usage);
16805
16836
  return 1;
16806
16837
  }
16838
+ if ((offset ?? 0) > 0 && !playName) {
16839
+ console.error("--offset requires --play.");
16840
+ return 1;
16841
+ }
16807
16842
  const client2 = new DeeplineClient();
16808
16843
  const runs = (await client2.runs.list({
16809
16844
  ...playName ? { play: playName } : {},
16810
- ...statusFilter ? { status: statusFilter } : {}
16845
+ ...statusFilter ? { status: statusFilter } : {},
16846
+ ...limit !== void 0 ? { limit } : {},
16847
+ ...offset !== void 0 ? { offset } : {}
16811
16848
  })).map((run) => ({
16812
16849
  runId: run.workflowId,
16813
16850
  workflowId: run.workflowId,
@@ -18364,10 +18401,12 @@ Examples:
18364
18401
  deepline runs list --play my-play --status failed --compact --json
18365
18402
  deepline runs list --status running --compact --json
18366
18403
  `
18367
- ).option("--play <name>", "Play name to filter runs").option("--status <status>", "Filter by run status").option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
18404
+ ).option("--play <name>", "Play name to filter runs").option("--status <status>", "Filter by run status").option("--limit <count>", "Maximum runs to return").option("--offset <count>", "Zero-based page offset (requires --play)").option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
18368
18405
  process.exitCode = await handleRunsList([
18369
18406
  ...options.play ? ["--play", options.play] : [],
18370
18407
  ...options.status ? ["--status", options.status] : [],
18408
+ ...options.limit ? ["--limit", options.limit] : [],
18409
+ ...options.offset ? ["--offset", options.offset] : [],
18371
18410
  ...options.compact ? ["--compact"] : [],
18372
18411
  ...options.json ? ["--json"] : []
18373
18412
  ]);
@@ -29344,6 +29383,7 @@ var BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
29344
29383
  var ASSIGNMENT_SECRET_LITERAL_PATTERN = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
29345
29384
  var HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
29346
29385
  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;
29386
+ var BOOTSTRAP_RESOURCE_IDENTIFIER_PATTERN = /^bootstrap-[0-9a-f]{32}(?:\/[a-z0-9][a-z0-9_-]{0,127})?$/i;
29347
29387
  var SECRET_LABEL_PATTERN = /(?:^|[-_])(?:api|auth|access|secret|token|key|password|credential|bearer|sk|pk|live)(?:[-_]|$)/i;
29348
29388
  function shannonEntropy(value) {
29349
29389
  const counts = /* @__PURE__ */ new Map();
@@ -29359,6 +29399,9 @@ function isNonSecretUuidIdentifier(value) {
29359
29399
  const label = match[1] ?? "";
29360
29400
  return !SECRET_LABEL_PATTERN.test(label);
29361
29401
  }
29402
+ function isNonSecretBootstrapResourceIdentifier(value) {
29403
+ return BOOTSTRAP_RESOURCE_IDENTIFIER_PATTERN.test(value);
29404
+ }
29362
29405
  function collectInlineSecretFindings(sourceCode) {
29363
29406
  const findings = [];
29364
29407
  for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
@@ -29373,6 +29416,7 @@ function collectInlineSecretFindings(sourceCode) {
29373
29416
  for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
29374
29417
  const literal = match[1] ?? "";
29375
29418
  if (isNonSecretUuidIdentifier(literal)) continue;
29419
+ if (isNonSecretBootstrapResourceIdentifier(literal)) continue;
29376
29420
  if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
29377
29421
  findings.push("high-entropy string literal");
29378
29422
  break;
package/dist/index.d.mts CHANGED
@@ -1717,6 +1717,8 @@ type RunsListOptions = {
1717
1717
  play?: string;
1718
1718
  status?: string;
1719
1719
  limit?: number;
1720
+ /** Zero-based page offset. Requires `play` because status-only inventory is not paginated. */
1721
+ offset?: number;
1720
1722
  };
1721
1723
  /** Options for `client.runs.get(...)`. */
1722
1724
  type RunsGetOptions = {
package/dist/index.d.ts CHANGED
@@ -1717,6 +1717,8 @@ type RunsListOptions = {
1717
1717
  play?: string;
1718
1718
  status?: string;
1719
1719
  limit?: number;
1720
+ /** Zero-based page offset. Requires `play` because status-only inventory is not paginated. */
1721
+ offset?: number;
1720
1722
  };
1721
1723
  /** Options for `client.runs.get(...)`. */
1722
1724
  type RunsGetOptions = {
package/dist/index.js CHANGED
@@ -438,7 +438,7 @@ var SDK_RELEASE = {
438
438
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
439
439
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
440
440
  // Operators use the checkout-local deepline-admin binary instead.
441
- version: "0.1.289",
441
+ version: "0.1.291",
442
442
  contracts: {
443
443
  api: {
444
444
  name: "sdk-http-api",
@@ -4144,6 +4144,17 @@ var DeeplineClient = class {
4144
4144
  if (typeof options.limit === "number" && Number.isFinite(options.limit)) {
4145
4145
  params.set("limit", String(Math.max(1, Math.floor(options.limit))));
4146
4146
  }
4147
+ if (options.offset !== void 0) {
4148
+ if (!Number.isFinite(options.offset) || !Number.isInteger(options.offset) || options.offset < 0) {
4149
+ throw new Error(
4150
+ "runs.list options.offset must be a non-negative integer."
4151
+ );
4152
+ }
4153
+ if (!playName && options.offset > 0) {
4154
+ throw new Error("runs.list options.offset requires options.play.");
4155
+ }
4156
+ params.set("offset", String(options.offset));
4157
+ }
4147
4158
  if (!playName && !status) {
4148
4159
  throw new Error("runs.list requires options.play or options.status.");
4149
4160
  }
package/dist/index.mjs CHANGED
@@ -367,7 +367,7 @@ var SDK_RELEASE = {
367
367
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
368
368
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
369
369
  // Operators use the checkout-local deepline-admin binary instead.
370
- version: "0.1.289",
370
+ version: "0.1.291",
371
371
  contracts: {
372
372
  api: {
373
373
  name: "sdk-http-api",
@@ -4073,6 +4073,17 @@ var DeeplineClient = class {
4073
4073
  if (typeof options.limit === "number" && Number.isFinite(options.limit)) {
4074
4074
  params.set("limit", String(Math.max(1, Math.floor(options.limit))));
4075
4075
  }
4076
+ if (options.offset !== void 0) {
4077
+ if (!Number.isFinite(options.offset) || !Number.isInteger(options.offset) || options.offset < 0) {
4078
+ throw new Error(
4079
+ "runs.list options.offset must be a non-negative integer."
4080
+ );
4081
+ }
4082
+ if (!playName && options.offset > 0) {
4083
+ throw new Error("runs.list options.offset requires options.play.");
4084
+ }
4085
+ params.set("offset", String(options.offset));
4086
+ }
4076
4087
  if (!playName && !status) {
4077
4088
  throw new Error("runs.list requires options.play or options.status.");
4078
4089
  }
@@ -70,6 +70,7 @@ var BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
70
70
  var ASSIGNMENT_SECRET_LITERAL_PATTERN = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
71
71
  var HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
72
72
  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;
73
+ var BOOTSTRAP_RESOURCE_IDENTIFIER_PATTERN = /^bootstrap-[0-9a-f]{32}(?:\/[a-z0-9][a-z0-9_-]{0,127})?$/i;
73
74
  var SECRET_LABEL_PATTERN = /(?:^|[-_])(?:api|auth|access|secret|token|key|password|credential|bearer|sk|pk|live)(?:[-_]|$)/i;
74
75
  function shannonEntropy(value) {
75
76
  const counts = /* @__PURE__ */ new Map();
@@ -85,6 +86,9 @@ function isNonSecretUuidIdentifier(value) {
85
86
  const label = match[1] ?? "";
86
87
  return !SECRET_LABEL_PATTERN.test(label);
87
88
  }
89
+ function isNonSecretBootstrapResourceIdentifier(value) {
90
+ return BOOTSTRAP_RESOURCE_IDENTIFIER_PATTERN.test(value);
91
+ }
88
92
  function collectInlineSecretFindings(sourceCode) {
89
93
  const findings = [];
90
94
  for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
@@ -99,6 +103,7 @@ function collectInlineSecretFindings(sourceCode) {
99
103
  for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
100
104
  const literal = match[1] ?? "";
101
105
  if (isNonSecretUuidIdentifier(literal)) continue;
106
+ if (isNonSecretBootstrapResourceIdentifier(literal)) continue;
102
107
  if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
103
108
  findings.push("high-entropy string literal");
104
109
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.289",
3
+ "version": "0.1.291",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {