deepline 0.3.201 → 0.3.202
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 +62 -1
- package/dist/bundling-sources/sdk/src/http.ts +55 -6
- package/dist/bundling-sources/sdk/src/index.ts +1 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +8 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +4 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +460 -96
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +11 -1
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-invocation-fence.ts +6 -2
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +401 -2
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +458 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-no-progress.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +25 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +119 -171
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-work-counters.ts +37 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +16 -7
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +136 -25
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +64 -15
- package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +5 -5
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/index.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/result-receipt-policy.ts +46 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +14 -5
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +5 -1
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +3 -9
- package/dist/cli/index.js +343 -39
- package/dist/cli/index.mjs +346 -41
- package/dist/index.d.mts +129 -1
- package/dist/index.d.ts +129 -1
- package/dist/index.js +64 -7
- package/dist/index.mjs +64 -7
- package/dist/install-integrity.json +3 -0
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3051,7 +3051,7 @@ var SDK_RELEASE = {
|
|
|
3051
3051
|
// getters keep their established compatibility behavior.
|
|
3052
3052
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
3053
3053
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
3054
|
-
version: "0.3.
|
|
3054
|
+
version: "0.3.202",
|
|
3055
3055
|
updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
|
|
3056
3056
|
packageCapabilities: {
|
|
3057
3057
|
updatePreferences: 1
|
|
@@ -3642,6 +3642,11 @@ var MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
|
|
|
3642
3642
|
var COWORK_NETWORK_HINT = "Claude Cowork appears to be running Deepline in a network-restricted sandbox. In Claude Desktop, open Settings > Capabilities, turn on Allow network egress, and set Domain allowlist to All domains for the Cowork session.";
|
|
3643
3643
|
var REQUEST_TIMEOUT_MARKER = Symbol("deeplineRequestTimeout");
|
|
3644
3644
|
var REQUEST_ABORT_MARKER = Symbol("deeplineRequestAbort");
|
|
3645
|
+
function requestCancelledError(signal) {
|
|
3646
|
+
const error = withErrorCause(new Error("Request aborted"), signal.reason);
|
|
3647
|
+
error[REQUEST_ABORT_MARKER] = true;
|
|
3648
|
+
return error;
|
|
3649
|
+
}
|
|
3645
3650
|
function normalizeRequestAbortError(error, input2) {
|
|
3646
3651
|
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
3647
3652
|
const tagged = normalized;
|
|
@@ -3820,14 +3825,27 @@ var HttpClient = class {
|
|
|
3820
3825
|
const candidateUrls = options?.exactUrlOnly ? [url] : buildCandidateUrls(url);
|
|
3821
3826
|
let retryAfterDelayMs = null;
|
|
3822
3827
|
const maxRetries = options?.maxRetries ?? this.config.maxRetries;
|
|
3823
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
3828
|
+
requestAttempts: for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
3829
|
+
if (options?.signal?.aborted) {
|
|
3830
|
+
lastError = requestCancelledError(options.signal);
|
|
3831
|
+
break;
|
|
3832
|
+
}
|
|
3824
3833
|
if (attempt > 0) {
|
|
3825
3834
|
const backoffMs = Math.min(1e3 * Math.pow(2, attempt - 1), 3e4);
|
|
3826
3835
|
const delayMs = retryAfterDelayMs === null ? backoffMs : Math.max(backoffMs, retryAfterDelayMs);
|
|
3827
3836
|
retryAfterDelayMs = null;
|
|
3828
|
-
|
|
3837
|
+
try {
|
|
3838
|
+
await sleep(delayMs, options?.signal);
|
|
3839
|
+
} catch (error) {
|
|
3840
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3841
|
+
break;
|
|
3842
|
+
}
|
|
3829
3843
|
}
|
|
3830
3844
|
for (const candidateUrl of candidateUrls) {
|
|
3845
|
+
if (options?.signal?.aborted) {
|
|
3846
|
+
lastError = requestCancelledError(options.signal);
|
|
3847
|
+
break requestAttempts;
|
|
3848
|
+
}
|
|
3831
3849
|
const controller = new AbortController();
|
|
3832
3850
|
const timeoutMs = options?.timeout ?? this.config.timeout;
|
|
3833
3851
|
let requestTimedOut = false;
|
|
@@ -3835,6 +3853,10 @@ var HttpClient = class {
|
|
|
3835
3853
|
requestTimedOut = true;
|
|
3836
3854
|
controller.abort();
|
|
3837
3855
|
}, timeoutMs);
|
|
3856
|
+
const abortRequest = () => controller.abort();
|
|
3857
|
+
options?.signal?.addEventListener("abort", abortRequest, {
|
|
3858
|
+
once: true
|
|
3859
|
+
});
|
|
3838
3860
|
try {
|
|
3839
3861
|
const response = await fetch(candidateUrl, {
|
|
3840
3862
|
method,
|
|
@@ -3847,8 +3869,8 @@ var HttpClient = class {
|
|
|
3847
3869
|
clearTimeout(timeoutId);
|
|
3848
3870
|
return null;
|
|
3849
3871
|
}
|
|
3850
|
-
clearTimeout(timeoutId);
|
|
3851
3872
|
const body = await response.text();
|
|
3873
|
+
clearTimeout(timeoutId);
|
|
3852
3874
|
const parsed = parseResponseBody(body);
|
|
3853
3875
|
const structuredToolError = options?.toolId && !response.ok ? deserializeToolExecutionFailure(
|
|
3854
3876
|
apiErrorMessage(parsed, response.status),
|
|
@@ -3943,6 +3965,10 @@ var HttpClient = class {
|
|
|
3943
3965
|
return parsed;
|
|
3944
3966
|
} catch (error) {
|
|
3945
3967
|
clearTimeout(timeoutId);
|
|
3968
|
+
if (options?.signal?.aborted) {
|
|
3969
|
+
lastError = requestCancelledError(options.signal);
|
|
3970
|
+
break requestAttempts;
|
|
3971
|
+
}
|
|
3946
3972
|
if (error instanceof AuthError || error instanceof DeeplineError) {
|
|
3947
3973
|
throw error;
|
|
3948
3974
|
}
|
|
@@ -3954,6 +3980,9 @@ var HttpClient = class {
|
|
|
3954
3980
|
timeoutMs,
|
|
3955
3981
|
timedOut: requestTimedOut
|
|
3956
3982
|
});
|
|
3983
|
+
} finally {
|
|
3984
|
+
clearTimeout(timeoutId);
|
|
3985
|
+
options?.signal?.removeEventListener("abort", abortRequest);
|
|
3957
3986
|
}
|
|
3958
3987
|
}
|
|
3959
3988
|
if (attempt < maxRetries) continue;
|
|
@@ -4282,8 +4311,23 @@ function decodeSseFrame(frame) {
|
|
|
4282
4311
|
}
|
|
4283
4312
|
return parsed;
|
|
4284
4313
|
}
|
|
4285
|
-
function sleep(ms) {
|
|
4286
|
-
return new Promise((resolve22) =>
|
|
4314
|
+
function sleep(ms, signal) {
|
|
4315
|
+
return new Promise((resolve22, reject) => {
|
|
4316
|
+
if (signal?.aborted) {
|
|
4317
|
+
reject(requestCancelledError(signal));
|
|
4318
|
+
return;
|
|
4319
|
+
}
|
|
4320
|
+
const timeoutId = setTimeout(() => {
|
|
4321
|
+
signal?.removeEventListener("abort", abort);
|
|
4322
|
+
resolve22();
|
|
4323
|
+
}, ms);
|
|
4324
|
+
const abort = () => {
|
|
4325
|
+
clearTimeout(timeoutId);
|
|
4326
|
+
signal?.removeEventListener("abort", abort);
|
|
4327
|
+
reject(requestCancelledError(signal));
|
|
4328
|
+
};
|
|
4329
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
4330
|
+
});
|
|
4287
4331
|
}
|
|
4288
4332
|
function withCoworkNetworkHint(message) {
|
|
4289
4333
|
if (!isCoworkLikeSandbox2() || message.includes(COWORK_NETWORK_HINT)) {
|
|
@@ -5470,9 +5514,13 @@ function normalizePlayRunLedgerSnapshot(value, fallback) {
|
|
|
5470
5514
|
tableNamespace: optionalString(rawDataset.tableNamespace) ?? datasetId,
|
|
5471
5515
|
phase,
|
|
5472
5516
|
persistedRows: Math.max(0, finiteNumber(rawDataset.persistedRows) ?? 0),
|
|
5517
|
+
persistedRowsObserved: rawDataset.persistedRowsObserved === true,
|
|
5473
5518
|
succeededRows: Math.max(0, finiteNumber(rawDataset.succeededRows) ?? 0),
|
|
5519
|
+
succeededRowsObserved: rawDataset.succeededRowsObserved === true,
|
|
5474
5520
|
failedRows: Math.max(0, finiteNumber(rawDataset.failedRows) ?? 0),
|
|
5521
|
+
failedRowsObserved: rawDataset.failedRowsObserved === true,
|
|
5475
5522
|
complete: rawDataset.complete === true,
|
|
5523
|
+
completeObserved: rawDataset.completeObserved === true,
|
|
5476
5524
|
// ADR 0019 birth survives the snapshot round trip. A partial record is
|
|
5477
5525
|
// dropped rather than half-admitted, so a dataset either states where its
|
|
5478
5526
|
// rows came from or says nothing.
|
|
@@ -7212,6 +7260,7 @@ var DeeplineClient = class _DeeplineClient {
|
|
|
7212
7260
|
this.http = new HttpClient(this.config);
|
|
7213
7261
|
this.runs = {
|
|
7214
7262
|
get: (runId, options2) => this.getRunStatus(runId, options2),
|
|
7263
|
+
status: (runId) => this.getRunLifecycleStatus(runId),
|
|
7215
7264
|
list: (options2) => this.listRuns(options2),
|
|
7216
7265
|
listPage: (options2) => this.listRunsPage(options2),
|
|
7217
7266
|
tail: (runId, options2) => this.tailRun(runId, options2),
|
|
@@ -8624,6 +8673,13 @@ var DeeplineClient = class _DeeplineClient {
|
|
|
8624
8673
|
}
|
|
8625
8674
|
};
|
|
8626
8675
|
}
|
|
8676
|
+
/** Read only the bounded scalar projection; does not hydrate the run package. */
|
|
8677
|
+
async getRunLifecycleStatus(runId) {
|
|
8678
|
+
const response = await this.http.get(
|
|
8679
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/status`
|
|
8680
|
+
);
|
|
8681
|
+
return response;
|
|
8682
|
+
}
|
|
8627
8683
|
/**
|
|
8628
8684
|
* List play runs using the public runs resource model.
|
|
8629
8685
|
*
|
|
@@ -9017,7 +9073,8 @@ var DeeplineClient = class _DeeplineClient {
|
|
|
9017
9073
|
params.set("rowMode", "all");
|
|
9018
9074
|
}
|
|
9019
9075
|
const result = await this.http.get(
|
|
9020
|
-
`/api/v2/plays/${encodeURIComponent(input2.playName)}/sheet?${params.toString()}
|
|
9076
|
+
`/api/v2/plays/${encodeURIComponent(input2.playName)}/sheet?${params.toString()}`,
|
|
9077
|
+
{ signal: input2.signal }
|
|
9021
9078
|
);
|
|
9022
9079
|
const requestedRunId = input2.runId?.trim() || "";
|
|
9023
9080
|
if (requestedRunId) {
|
|
@@ -20662,6 +20719,12 @@ var FIXTURE_BEHAVIOR_REPLAY_VERSION = 3;
|
|
|
20662
20719
|
var MAX_FIXTURE_RESPONSE_DELAY_SAMPLES = 256;
|
|
20663
20720
|
var MAX_FIXTURE_RESPONSE_DELAY_MS = 8 * 6e4;
|
|
20664
20721
|
var MAX_FIXTURE_RESPONSE_ERROR_MESSAGE_LENGTH = 500;
|
|
20722
|
+
var MAX_FIXTURE_RETRY_AFTER_MS = 60 * 6e4;
|
|
20723
|
+
var FIXTURE_RATE_WINDOWS = [
|
|
20724
|
+
{ field: "requestsPerSecond", name: "rps", windowMs: 1e3 },
|
|
20725
|
+
{ field: "requestsPerMinute", name: "rpm", windowMs: 6e4 },
|
|
20726
|
+
{ field: "requestsPerHour", name: "rph", windowMs: 36e5 }
|
|
20727
|
+
];
|
|
20665
20728
|
function validateFixtureBehavior(value) {
|
|
20666
20729
|
if (value === void 0 || value === null) {
|
|
20667
20730
|
return { ok: true, behavior: null };
|
|
@@ -20673,6 +20736,8 @@ function validateFixtureBehavior(value) {
|
|
|
20673
20736
|
const supportedKeys = record3.version === FIXTURE_BEHAVIOR_RESPONSE_VERSION || record3.version === FIXTURE_BEHAVIOR_REPLAY_VERSION ? /* @__PURE__ */ new Set([
|
|
20674
20737
|
"version",
|
|
20675
20738
|
"responseSamples",
|
|
20739
|
+
"transient429",
|
|
20740
|
+
"sharedRateLimit",
|
|
20676
20741
|
...record3.version === FIXTURE_BEHAVIOR_REPLAY_VERSION ? ["replayBundle"] : []
|
|
20677
20742
|
]) : /* @__PURE__ */ new Set(["version", "responseDelaySamplesMs"]);
|
|
20678
20743
|
const unknownKeys = Object.keys(record3).filter(
|
|
@@ -20769,7 +20834,7 @@ function validateFixtureBehavior(value) {
|
|
|
20769
20834
|
}
|
|
20770
20835
|
const error = sample.httpError;
|
|
20771
20836
|
const errorUnknownKeys = Object.keys(error).filter(
|
|
20772
|
-
(key) => key !== "status" && key !== "message"
|
|
20837
|
+
(key) => key !== "status" && key !== "message" && key !== "retryAfterMs" && key !== "retryAfterFormat" && key !== "failuresBeforeSuccess"
|
|
20773
20838
|
);
|
|
20774
20839
|
if (errorUnknownKeys.length > 0) {
|
|
20775
20840
|
return {
|
|
@@ -20789,7 +20854,33 @@ function validateFixtureBehavior(value) {
|
|
|
20789
20854
|
error: `fixtureBehavior.responseSamples[${index}].httpError.message must contain 1-${MAX_FIXTURE_RESPONSE_ERROR_MESSAGE_LENGTH} characters.`
|
|
20790
20855
|
};
|
|
20791
20856
|
}
|
|
20792
|
-
|
|
20857
|
+
if (error.retryAfterMs !== void 0 && (error.status !== 429 || typeof error.retryAfterMs !== "number" || !Number.isSafeInteger(error.retryAfterMs) || error.retryAfterMs < 1 || error.retryAfterMs > MAX_FIXTURE_RETRY_AFTER_MS)) {
|
|
20858
|
+
return {
|
|
20859
|
+
ok: false,
|
|
20860
|
+
error: `fixtureBehavior.responseSamples[${index}].httpError.retryAfterMs requires status 429 and an integer from 1 through ${MAX_FIXTURE_RETRY_AFTER_MS}.`
|
|
20861
|
+
};
|
|
20862
|
+
}
|
|
20863
|
+
if (error.retryAfterFormat !== void 0 && (error.status !== 429 || error.retryAfterFormat !== "delta_seconds" && error.retryAfterFormat !== "http_date")) {
|
|
20864
|
+
return {
|
|
20865
|
+
ok: false,
|
|
20866
|
+
error: `fixtureBehavior.responseSamples[${index}].httpError.retryAfterFormat requires status 429 and delta_seconds or http_date.`
|
|
20867
|
+
};
|
|
20868
|
+
}
|
|
20869
|
+
if (error.failuresBeforeSuccess !== void 0 && (error.status !== 429 || typeof error.failuresBeforeSuccess !== "number" || !Number.isSafeInteger(error.failuresBeforeSuccess) || error.failuresBeforeSuccess < 1 || error.failuresBeforeSuccess > 20)) {
|
|
20870
|
+
return {
|
|
20871
|
+
ok: false,
|
|
20872
|
+
error: `fixtureBehavior.responseSamples[${index}].httpError.failuresBeforeSuccess requires status 429 and an integer from 1 through 20.`
|
|
20873
|
+
};
|
|
20874
|
+
}
|
|
20875
|
+
httpError = {
|
|
20876
|
+
status: error.status,
|
|
20877
|
+
message: error.message.trim(),
|
|
20878
|
+
...error.retryAfterMs !== void 0 ? { retryAfterMs: error.retryAfterMs } : {},
|
|
20879
|
+
...error.retryAfterFormat !== void 0 ? {
|
|
20880
|
+
retryAfterFormat: error.retryAfterFormat
|
|
20881
|
+
} : {},
|
|
20882
|
+
...error.failuresBeforeSuccess !== void 0 ? { failuresBeforeSuccess: error.failuresBeforeSuccess } : {}
|
|
20883
|
+
};
|
|
20793
20884
|
}
|
|
20794
20885
|
samples2.push({
|
|
20795
20886
|
delayMs: sample.delayMs,
|
|
@@ -20797,6 +20888,106 @@ function validateFixtureBehavior(value) {
|
|
|
20797
20888
|
...httpError ? { httpError } : {}
|
|
20798
20889
|
});
|
|
20799
20890
|
}
|
|
20891
|
+
let sharedRateLimit;
|
|
20892
|
+
if (record3.sharedRateLimit !== void 0) {
|
|
20893
|
+
const plan = record3.sharedRateLimit;
|
|
20894
|
+
if (!plan || typeof plan !== "object" || Array.isArray(plan)) {
|
|
20895
|
+
return {
|
|
20896
|
+
ok: false,
|
|
20897
|
+
error: "fixtureBehavior.sharedRateLimit must be an object."
|
|
20898
|
+
};
|
|
20899
|
+
}
|
|
20900
|
+
const planRecord = plan;
|
|
20901
|
+
const entries = Object.entries(planRecord);
|
|
20902
|
+
const boundaryAfterMs = planRecord.requestsPerHourBoundaryAfterMs;
|
|
20903
|
+
const quotaEntries = entries.filter(
|
|
20904
|
+
([name]) => name !== "requestsPerHourBoundaryAfterMs"
|
|
20905
|
+
);
|
|
20906
|
+
if (quotaEntries.length === 0 || quotaEntries.some(
|
|
20907
|
+
([name, limit]) => !FIXTURE_RATE_WINDOWS.some((window) => window.field === name) || typeof limit !== "number" || !Number.isSafeInteger(limit) || limit < 1
|
|
20908
|
+
)) {
|
|
20909
|
+
return {
|
|
20910
|
+
ok: false,
|
|
20911
|
+
error: "fixtureBehavior.sharedRateLimit requires positive integer requestsPerSecond, requestsPerMinute, or requestsPerHour quotas."
|
|
20912
|
+
};
|
|
20913
|
+
}
|
|
20914
|
+
if (boundaryAfterMs !== void 0 && (planRecord.requestsPerHour === void 0 || typeof boundaryAfterMs !== "number" || !Number.isSafeInteger(boundaryAfterMs) || boundaryAfterMs < 1e3 || boundaryAfterMs > 3e5)) {
|
|
20915
|
+
return {
|
|
20916
|
+
ok: false,
|
|
20917
|
+
error: "fixtureBehavior.sharedRateLimit.requestsPerHourBoundaryAfterMs requires requestsPerHour and an integer from 1000 through 300000."
|
|
20918
|
+
};
|
|
20919
|
+
}
|
|
20920
|
+
sharedRateLimit = Object.fromEntries(entries);
|
|
20921
|
+
}
|
|
20922
|
+
let transient429;
|
|
20923
|
+
if (record3.transient429 !== void 0) {
|
|
20924
|
+
const selected = record3.transient429;
|
|
20925
|
+
if (!selected || typeof selected !== "object" || Array.isArray(selected)) {
|
|
20926
|
+
return {
|
|
20927
|
+
ok: false,
|
|
20928
|
+
error: "fixtureBehavior.transient429 must be an object."
|
|
20929
|
+
};
|
|
20930
|
+
}
|
|
20931
|
+
const fault = selected;
|
|
20932
|
+
const unknownFaultKeys = Object.keys(fault).filter(
|
|
20933
|
+
(key) => key !== "everyNthCall" && key !== "retryAfterMs" && key !== "failuresBeforeSuccess" && key !== "when"
|
|
20934
|
+
);
|
|
20935
|
+
if (unknownFaultKeys.length > 0) {
|
|
20936
|
+
return {
|
|
20937
|
+
ok: false,
|
|
20938
|
+
error: `Unsupported fixtureBehavior.transient429 field "${unknownFaultKeys[0]}".`
|
|
20939
|
+
};
|
|
20940
|
+
}
|
|
20941
|
+
for (const [name, value2, max] of [
|
|
20942
|
+
["everyNthCall", fault.everyNthCall, 1e4],
|
|
20943
|
+
["retryAfterMs", fault.retryAfterMs, MAX_FIXTURE_RETRY_AFTER_MS],
|
|
20944
|
+
["failuresBeforeSuccess", fault.failuresBeforeSuccess, 20]
|
|
20945
|
+
]) {
|
|
20946
|
+
if (typeof value2 !== "number" || !Number.isSafeInteger(value2) || value2 < 1 || value2 > max) {
|
|
20947
|
+
return {
|
|
20948
|
+
ok: false,
|
|
20949
|
+
error: `fixtureBehavior.transient429.${name} must be an integer from 1 through ${max}.`
|
|
20950
|
+
};
|
|
20951
|
+
}
|
|
20952
|
+
}
|
|
20953
|
+
let when;
|
|
20954
|
+
if (fault.when !== void 0) {
|
|
20955
|
+
if (!fault.when || typeof fault.when !== "object" || Array.isArray(fault.when)) {
|
|
20956
|
+
return {
|
|
20957
|
+
ok: false,
|
|
20958
|
+
error: "fixtureBehavior.transient429.when must be an object."
|
|
20959
|
+
};
|
|
20960
|
+
}
|
|
20961
|
+
const condition = fault.when;
|
|
20962
|
+
const unknownConditionKeys = Object.keys(condition).filter(
|
|
20963
|
+
(key) => key !== "provider" && key !== "operation"
|
|
20964
|
+
);
|
|
20965
|
+
if (unknownConditionKeys.length > 0) {
|
|
20966
|
+
return {
|
|
20967
|
+
ok: false,
|
|
20968
|
+
error: `Unsupported fixtureBehavior.transient429.when field "${unknownConditionKeys[0]}".`
|
|
20969
|
+
};
|
|
20970
|
+
}
|
|
20971
|
+
const provider = normalizeFixtureScopeValue(condition.provider, 100);
|
|
20972
|
+
const operation = normalizeFixtureScopeValue(condition.operation, 200);
|
|
20973
|
+
if (provider.error || operation.error || !provider.value && !operation.value) {
|
|
20974
|
+
return {
|
|
20975
|
+
ok: false,
|
|
20976
|
+
error: "fixtureBehavior.transient429.when must select a valid provider or operation."
|
|
20977
|
+
};
|
|
20978
|
+
}
|
|
20979
|
+
when = {
|
|
20980
|
+
...provider.value ? { provider: provider.value } : {},
|
|
20981
|
+
...operation.value ? { operation: operation.value } : {}
|
|
20982
|
+
};
|
|
20983
|
+
}
|
|
20984
|
+
transient429 = {
|
|
20985
|
+
everyNthCall: fault.everyNthCall,
|
|
20986
|
+
retryAfterMs: fault.retryAfterMs,
|
|
20987
|
+
failuresBeforeSuccess: fault.failuresBeforeSuccess,
|
|
20988
|
+
...when ? { when } : {}
|
|
20989
|
+
};
|
|
20990
|
+
}
|
|
20800
20991
|
let replayBundle;
|
|
20801
20992
|
if (record3.version === FIXTURE_BEHAVIOR_REPLAY_VERSION) {
|
|
20802
20993
|
if (!record3.replayBundle || typeof record3.replayBundle !== "object" || Array.isArray(record3.replayBundle)) {
|
|
@@ -20859,6 +21050,8 @@ function validateFixtureBehavior(value) {
|
|
|
20859
21050
|
behavior: {
|
|
20860
21051
|
version: FIXTURE_BEHAVIOR_REPLAY_VERSION,
|
|
20861
21052
|
responseSamples: samples2,
|
|
21053
|
+
...transient429 ? { transient429 } : {},
|
|
21054
|
+
...sharedRateLimit ? { sharedRateLimit } : {},
|
|
20862
21055
|
replayBundle
|
|
20863
21056
|
}
|
|
20864
21057
|
};
|
|
@@ -20867,7 +21060,9 @@ function validateFixtureBehavior(value) {
|
|
|
20867
21060
|
ok: true,
|
|
20868
21061
|
behavior: {
|
|
20869
21062
|
version: FIXTURE_BEHAVIOR_RESPONSE_VERSION,
|
|
20870
|
-
responseSamples: samples2
|
|
21063
|
+
responseSamples: samples2,
|
|
21064
|
+
...transient429 ? { transient429 } : {},
|
|
21065
|
+
...sharedRateLimit ? { sharedRateLimit } : {}
|
|
20871
21066
|
}
|
|
20872
21067
|
};
|
|
20873
21068
|
}
|
|
@@ -22751,43 +22946,52 @@ async function readRunIdFile(destination) {
|
|
|
22751
22946
|
}
|
|
22752
22947
|
return parsed;
|
|
22753
22948
|
}
|
|
22754
|
-
async function writePlayRunIdFile(destination, runId) {
|
|
22949
|
+
async function writePlayRunIdFile(destination, runId, options = {}) {
|
|
22755
22950
|
const existing = await readRunIdFile(destination);
|
|
22756
22951
|
if (existing) {
|
|
22757
|
-
if (existing.runId
|
|
22758
|
-
|
|
22759
|
-
|
|
22760
|
-
|
|
22952
|
+
if (existing.runId !== runId) {
|
|
22953
|
+
throw new Error(
|
|
22954
|
+
`--run-id-file destination already records run ${existing.runId}; refusing to replace it with ${runId}: ${destination}`
|
|
22955
|
+
);
|
|
22956
|
+
}
|
|
22957
|
+
if (!options.launchConfirmed || existing.launchConfirmed) return;
|
|
22761
22958
|
}
|
|
22762
22959
|
const tempPath = runIdFileTempPath(destination);
|
|
22763
22960
|
let handle = null;
|
|
22764
22961
|
try {
|
|
22765
22962
|
handle = await (0, import_promises9.open)(tempPath, "wx", 384);
|
|
22766
22963
|
await handle.writeFile(
|
|
22767
|
-
`${JSON.stringify({ version: 1, runId })}
|
|
22964
|
+
`${JSON.stringify({ version: 1, runId, ...options })}
|
|
22768
22965
|
`,
|
|
22769
22966
|
"utf8"
|
|
22770
22967
|
);
|
|
22771
22968
|
await handle.sync();
|
|
22772
22969
|
await handle.close();
|
|
22773
22970
|
handle = null;
|
|
22774
|
-
|
|
22775
|
-
await (0, import_promises9.
|
|
22776
|
-
|
|
22777
|
-
|
|
22778
|
-
|
|
22779
|
-
|
|
22780
|
-
|
|
22781
|
-
|
|
22971
|
+
if (existing) {
|
|
22972
|
+
await (0, import_promises9.rename)(tempPath, destination);
|
|
22973
|
+
} else {
|
|
22974
|
+
try {
|
|
22975
|
+
await (0, import_promises9.link)(tempPath, destination);
|
|
22976
|
+
} catch (error) {
|
|
22977
|
+
if (error.code !== "EEXIST") throw error;
|
|
22978
|
+
const raced = await readRunIdFile(destination);
|
|
22979
|
+
if (raced?.runId !== runId) {
|
|
22980
|
+
throw new Error(
|
|
22981
|
+
`--run-id-file destination already records run ${raced?.runId ?? "<invalid>"}; refusing to replace it with ${runId}: ${destination}`
|
|
22982
|
+
);
|
|
22782
22983
|
}
|
|
22984
|
+
if (!options.launchConfirmed || raced.launchConfirmed) return;
|
|
22985
|
+
await (0, import_promises9.rename)(tempPath, destination);
|
|
22986
|
+
}
|
|
22987
|
+
}
|
|
22988
|
+
if (process.platform !== "win32") {
|
|
22989
|
+
const directory = await (0, import_promises9.open)((0, import_node_path17.dirname)(destination), "r");
|
|
22990
|
+
try {
|
|
22991
|
+
await directory.sync();
|
|
22992
|
+
} finally {
|
|
22993
|
+
await directory.close();
|
|
22783
22994
|
}
|
|
22784
|
-
} catch (error) {
|
|
22785
|
-
if (error.code !== "EEXIST") throw error;
|
|
22786
|
-
const raced = await readRunIdFile(destination);
|
|
22787
|
-
if (raced?.runId === runId) return;
|
|
22788
|
-
throw new Error(
|
|
22789
|
-
`--run-id-file destination already records run ${raced?.runId ?? "<invalid>"}; refusing to replace it with ${runId}: ${destination}`
|
|
22790
|
-
);
|
|
22791
22995
|
}
|
|
22792
22996
|
} finally {
|
|
22793
22997
|
await handle?.close();
|
|
@@ -23844,6 +24048,16 @@ function getRunIdFromLiveEvent(event) {
|
|
|
23844
24048
|
function eventConfirmsPlayRunLaunch(event) {
|
|
23845
24049
|
return event.type === "play.run.status" && getEventPayload(event).launchConfirmed === true;
|
|
23846
24050
|
}
|
|
24051
|
+
function statusProvesPlayRunExecution(status) {
|
|
24052
|
+
return status === "waiting" || status === "completed";
|
|
24053
|
+
}
|
|
24054
|
+
function eventProvesPlayRunExecution(event) {
|
|
24055
|
+
if (statusProvesPlayRunExecution(getStatusFromLiveEvent(event))) return true;
|
|
24056
|
+
const payload = getEventPayload(event);
|
|
24057
|
+
return Boolean(getStringField(payload, "stepId")) && (event.type === "play.step.progress" || event.type === "play.step.status" && ["running", "completed", "failed", "skipped"].includes(
|
|
24058
|
+
getStringField(payload, "status") ?? ""
|
|
24059
|
+
));
|
|
24060
|
+
}
|
|
23847
24061
|
function getStatusFromLiveEvent(event) {
|
|
23848
24062
|
if (event.type !== "play.run.status" && event.type !== "play.run.snapshot" && event.type !== "play.run.final_status") {
|
|
23849
24063
|
return null;
|
|
@@ -24320,6 +24534,12 @@ async function waitForPlayCompletionByStream(input2) {
|
|
|
24320
24534
|
const durableStatusProbeIntervalMs = input2.durableStatusProbeIntervalMs ?? PLAY_DURABLE_STATUS_PROBE_INTERVAL_MS;
|
|
24321
24535
|
let nextDurableProbeAt = Date.now() + durableStatusProbeIntervalMs;
|
|
24322
24536
|
let durableStatusProbe = null;
|
|
24537
|
+
let admissionConfirmed = false;
|
|
24538
|
+
const confirmAdmission = async () => {
|
|
24539
|
+
if (admissionConfirmed) return;
|
|
24540
|
+
admissionConfirmed = true;
|
|
24541
|
+
await input2.onRunAdmitted?.(input2.workflowId);
|
|
24542
|
+
};
|
|
24323
24543
|
const withDashboardUrl = (status) => input2.dashboardUrl ? { ...status, dashboardUrl: input2.dashboardUrl } : status;
|
|
24324
24544
|
const remainingWaitMs = () => input2.waitTimeoutMs === null ? null : input2.waitTimeoutMs - (Date.now() - input2.startedAt);
|
|
24325
24545
|
const fetchTerminalStatus = async () => {
|
|
@@ -24336,6 +24556,9 @@ async function waitForPlayCompletionByStream(input2) {
|
|
|
24336
24556
|
}
|
|
24337
24557
|
throw error;
|
|
24338
24558
|
}
|
|
24559
|
+
if (refreshed.runId === input2.workflowId && statusProvesPlayRunExecution(refreshed.status)) {
|
|
24560
|
+
await confirmAdmission();
|
|
24561
|
+
}
|
|
24339
24562
|
return TERMINAL_PLAY_STATUSES2.has(refreshed.status) ? withDashboardUrl(refreshed) : null;
|
|
24340
24563
|
})();
|
|
24341
24564
|
durableStatusProbe = probe;
|
|
@@ -24347,6 +24570,9 @@ async function waitForPlayCompletionByStream(input2) {
|
|
|
24347
24570
|
};
|
|
24348
24571
|
const handleLiveEvent = async (event, probeDurableStatus) => {
|
|
24349
24572
|
assertPlayWaitNotTimedOut({ ...input2, lastPhase });
|
|
24573
|
+
if (getRunIdFromLiveEvent(event) === input2.workflowId && (eventConfirmsPlayRunLaunch(event) || eventProvesPlayRunExecution(event))) {
|
|
24574
|
+
await confirmAdmission();
|
|
24575
|
+
}
|
|
24350
24576
|
const now = Date.now();
|
|
24351
24577
|
if (now >= nextDurableProbeAt) {
|
|
24352
24578
|
nextDurableProbeAt = now + durableStatusProbeIntervalMs;
|
|
@@ -24596,6 +24822,11 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
24596
24822
|
let eventCount = 0;
|
|
24597
24823
|
let firstRunIdMs = null;
|
|
24598
24824
|
let launchConfirmed = false;
|
|
24825
|
+
const confirmAdmission = async (runId) => {
|
|
24826
|
+
if (launchConfirmed || !runId || runId === "pending") return;
|
|
24827
|
+
launchConfirmed = true;
|
|
24828
|
+
await input2.onRunAdmitted?.(runId);
|
|
24829
|
+
};
|
|
24599
24830
|
let lastPhase = null;
|
|
24600
24831
|
const timeout = input2.waitTimeoutMs === null ? null : setTimeout(
|
|
24601
24832
|
() => {
|
|
@@ -24619,6 +24850,9 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
24619
24850
|
}
|
|
24620
24851
|
throw error;
|
|
24621
24852
|
}
|
|
24853
|
+
if (refreshed.runId === lastKnownWorkflowId && statusProvesPlayRunExecution(refreshed.status)) {
|
|
24854
|
+
await confirmAdmission(lastKnownWorkflowId);
|
|
24855
|
+
}
|
|
24622
24856
|
return TERMINAL_PLAY_STATUSES2.has(refreshed.status) ? {
|
|
24623
24857
|
...refreshed,
|
|
24624
24858
|
dashboardUrl,
|
|
@@ -24646,8 +24880,8 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
24646
24880
|
await input2.onRunStarted?.(eventRunId);
|
|
24647
24881
|
}
|
|
24648
24882
|
}
|
|
24649
|
-
if (eventConfirmsPlayRunLaunch(event) && eventRunId === lastKnownWorkflowId) {
|
|
24650
|
-
|
|
24883
|
+
if (!launchConfirmed && eventConfirmsPlayRunLaunch(event) && eventRunId === lastKnownWorkflowId) {
|
|
24884
|
+
await confirmAdmission(lastKnownWorkflowId);
|
|
24651
24885
|
}
|
|
24652
24886
|
const workflowId = lastKnownWorkflowId || "pending";
|
|
24653
24887
|
if (workflowId !== "pending" && !emittedDashboardUrl) {
|
|
@@ -24762,6 +24996,9 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
24762
24996
|
firstRunIdMs,
|
|
24763
24997
|
lastPhase
|
|
24764
24998
|
});
|
|
24999
|
+
if (statusProvesPlayRunExecution(canonicalTerminal.status)) {
|
|
25000
|
+
await confirmAdmission(lastKnownWorkflowId);
|
|
25001
|
+
}
|
|
24765
25002
|
return canonicalTerminal;
|
|
24766
25003
|
}
|
|
24767
25004
|
if (lastKnownWorkflowId && launchConfirmed) {
|
|
@@ -24789,7 +25026,8 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
24789
25026
|
waitTimeoutMs: input2.waitTimeoutMs,
|
|
24790
25027
|
startedAt,
|
|
24791
25028
|
state,
|
|
24792
|
-
progress: input2.progress
|
|
25029
|
+
progress: input2.progress,
|
|
25030
|
+
onRunAdmitted: confirmAdmission
|
|
24793
25031
|
});
|
|
24794
25032
|
}
|
|
24795
25033
|
}
|
|
@@ -24849,7 +25087,8 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
24849
25087
|
waitTimeoutMs: input2.waitTimeoutMs,
|
|
24850
25088
|
startedAt,
|
|
24851
25089
|
state,
|
|
24852
|
-
progress: input2.progress
|
|
25090
|
+
progress: input2.progress,
|
|
25091
|
+
onRunAdmitted: confirmAdmission
|
|
24853
25092
|
});
|
|
24854
25093
|
}
|
|
24855
25094
|
if (!lastKnownWorkflowId && isTransientPlayStreamError(error)) {
|
|
@@ -24902,7 +25141,8 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
24902
25141
|
waitTimeoutMs: input2.waitTimeoutMs,
|
|
24903
25142
|
startedAt,
|
|
24904
25143
|
state,
|
|
24905
|
-
progress: input2.progress
|
|
25144
|
+
progress: input2.progress,
|
|
25145
|
+
onRunAdmitted: confirmAdmission
|
|
24906
25146
|
});
|
|
24907
25147
|
}
|
|
24908
25148
|
const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
|
|
@@ -25845,8 +26085,15 @@ function formatPackageDatasetActionLines(packaged, options) {
|
|
|
25845
26085
|
const isReturned = datasetId && returned.datasetIds.has(datasetId) || returned.paths.has(path);
|
|
25846
26086
|
const category = isReturned ? "returned" : dataset.recovered === true ? "recovered" : "persisted";
|
|
25847
26087
|
const rowCount = typeof dataset.rowCount === "number" && Number.isFinite(dataset.rowCount) ? Math.max(0, Math.trunc(dataset.rowCount)) : null;
|
|
26088
|
+
const lifecycle = readRecord(dataset.lifecycle);
|
|
26089
|
+
const countUnconfirmed = lifecycle?.complete === false;
|
|
26090
|
+
const persistedLowerBound = typeof lifecycle?.persistedRowsAtLeast === "number" && Number.isFinite(lifecycle.persistedRowsAtLeast) ? Math.max(0, Math.trunc(lifecycle.persistedRowsAtLeast)) : 0;
|
|
25848
26091
|
const actions = readRecord(dataset.actions) ?? {};
|
|
25849
|
-
if (
|
|
26092
|
+
if (countUnconfirmed) {
|
|
26093
|
+
lines.push(
|
|
26094
|
+
` dataset ${path}: row count unconfirmed (at least ${formatInteger(persistedLowerBound)} persisted); export this run to inspect saved rows`
|
|
26095
|
+
);
|
|
26096
|
+
} else if (includeSummary) {
|
|
25850
26097
|
if (category === "recovered" && Object.keys(actions).length === 0) {
|
|
25851
26098
|
lines.push(
|
|
25852
26099
|
` dataset ${path}: available, ${rowCount === null ? "persisted" : formatInteger(rowCount)} rows persisted; re-running reuses completed work`
|
|
@@ -28194,7 +28441,8 @@ async function handleFileBackedRun(options, hooks) {
|
|
|
28194
28441
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
28195
28442
|
open: options.open,
|
|
28196
28443
|
progress,
|
|
28197
|
-
onRunStarted: hooks?.onRunStarted
|
|
28444
|
+
onRunStarted: hooks?.onRunStarted,
|
|
28445
|
+
onRunAdmitted: hooks?.onRunAdmitted
|
|
28198
28446
|
}).catch(async (error) => {
|
|
28199
28447
|
throw await normalizePlayStartError(client2, error, playName);
|
|
28200
28448
|
})
|
|
@@ -28235,6 +28483,7 @@ async function handleFileBackedRun(options, hooks) {
|
|
|
28235
28483
|
})
|
|
28236
28484
|
);
|
|
28237
28485
|
await hooks?.onRunStarted?.(started.workflowId);
|
|
28486
|
+
await hooks?.onRunAdmitted?.(started.workflowId);
|
|
28238
28487
|
const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
|
|
28239
28488
|
openPlayDashboard(resolvedDashboardUrl, options.open);
|
|
28240
28489
|
progress.phase("started run");
|
|
@@ -28377,7 +28626,8 @@ async function handleNamedRun(options, hooks) {
|
|
|
28377
28626
|
waitTimeoutMs: options.waitTimeoutMs,
|
|
28378
28627
|
open: options.open,
|
|
28379
28628
|
progress,
|
|
28380
|
-
onRunStarted: hooks?.onRunStarted
|
|
28629
|
+
onRunStarted: hooks?.onRunStarted,
|
|
28630
|
+
onRunAdmitted: hooks?.onRunAdmitted
|
|
28381
28631
|
}).catch(async (error) => {
|
|
28382
28632
|
throw await normalizePlayStartError(client2, error, playName, {
|
|
28383
28633
|
suggestMissingPlay: true
|
|
@@ -28423,6 +28673,7 @@ async function handleNamedRun(options, hooks) {
|
|
|
28423
28673
|
})
|
|
28424
28674
|
);
|
|
28425
28675
|
await hooks?.onRunStarted?.(started.workflowId);
|
|
28676
|
+
await hooks?.onRunAdmitted?.(started.workflowId);
|
|
28426
28677
|
const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
|
|
28427
28678
|
openPlayDashboard(resolvedDashboardUrl, options.open);
|
|
28428
28679
|
progress.phase("started run");
|
|
@@ -28460,6 +28711,19 @@ async function handlePlayRun(args, hooks) {
|
|
|
28460
28711
|
);
|
|
28461
28712
|
}
|
|
28462
28713
|
await hooks?.onRunStarted?.(runId);
|
|
28714
|
+
},
|
|
28715
|
+
onRunAdmitted: async (runId) => {
|
|
28716
|
+
try {
|
|
28717
|
+
await writePlayRunIdFile(options.runIdFile, runId, {
|
|
28718
|
+
launchConfirmed: true
|
|
28719
|
+
});
|
|
28720
|
+
} catch (error) {
|
|
28721
|
+
throw new Error(
|
|
28722
|
+
`Run ${runId} was admitted, but its confirmation could not be persisted to ${options.runIdFile}: ${error instanceof Error ? error.message : String(error)}. Inspect it with 'deepline runs get ${runId} --json'; do not submit a replacement run.`,
|
|
28723
|
+
{ cause: error }
|
|
28724
|
+
);
|
|
28725
|
+
}
|
|
28726
|
+
await hooks?.onRunAdmitted?.(runId);
|
|
28463
28727
|
}
|
|
28464
28728
|
} : hooks;
|
|
28465
28729
|
if (options.target.kind === "file") {
|
|
@@ -28532,6 +28796,36 @@ async function handleRunGet(args) {
|
|
|
28532
28796
|
});
|
|
28533
28797
|
return 0;
|
|
28534
28798
|
}
|
|
28799
|
+
async function handleRunLifecycleStatus(args) {
|
|
28800
|
+
const usage = "Usage: deepline runs status <run-id> [--json]";
|
|
28801
|
+
let runId;
|
|
28802
|
+
try {
|
|
28803
|
+
runId = parseRunIdPositional(args, usage);
|
|
28804
|
+
} catch (error) {
|
|
28805
|
+
console.error(error instanceof Error ? error.message : usage);
|
|
28806
|
+
return 1;
|
|
28807
|
+
}
|
|
28808
|
+
const status = await new DeeplineClient().runs.status(runId);
|
|
28809
|
+
if (argsWantJson(args)) {
|
|
28810
|
+
printCommandEnvelope(status, { json: true });
|
|
28811
|
+
return 0;
|
|
28812
|
+
}
|
|
28813
|
+
if (!status.available) {
|
|
28814
|
+
console.log(`Scalar lifecycle status is not available for run ${runId}.`);
|
|
28815
|
+
return 0;
|
|
28816
|
+
}
|
|
28817
|
+
const formatCount = (count) => count.value === null ? "unknown" : `${count.value}${count.confirmed ? "" : " (unconfirmed)"}`;
|
|
28818
|
+
console.log(`${status.lifecycle.status} \xB7 ${runId}`);
|
|
28819
|
+
console.log(
|
|
28820
|
+
`Map-step work (summed, not unique rows): completed ${formatCount(status.mapStepWork.completed)} / ${formatCount(status.mapStepWork.total)}, failed ${formatCount(status.mapStepWork.failed)}`
|
|
28821
|
+
);
|
|
28822
|
+
if (status.lifecycle.status === "waiting") {
|
|
28823
|
+
console.log(
|
|
28824
|
+
status.wait.confirmed && status.wait.kind ? `Waiting: ${status.wait.kind}` : "Waiting reason: unknown"
|
|
28825
|
+
);
|
|
28826
|
+
}
|
|
28827
|
+
return 0;
|
|
28828
|
+
}
|
|
28535
28829
|
async function handleRunsRerun(args) {
|
|
28536
28830
|
const usage = "Usage: deepline runs rerun <run-id> [--idempotency-key <key>] [--json]";
|
|
28537
28831
|
let runId;
|
|
@@ -29014,6 +29308,10 @@ async function handleRunStop(args) {
|
|
|
29014
29308
|
title: "run stop",
|
|
29015
29309
|
lines: [
|
|
29016
29310
|
`Cancellation queued for ${result.runId || runId}; sandbox cleanup is pending`,
|
|
29311
|
+
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : [],
|
|
29312
|
+
...result.hitlCancellationPending === true ? [
|
|
29313
|
+
"warning: HITL cancellation or same-card Slack refresh is still pending"
|
|
29314
|
+
] : [],
|
|
29017
29315
|
`inspect: deepline runs get ${runId} --full --json`
|
|
29018
29316
|
]
|
|
29019
29317
|
}
|
|
@@ -30928,6 +31226,12 @@ Examples:
|
|
|
30928
31226
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
30929
31227
|
`
|
|
30930
31228
|
);
|
|
31229
|
+
runs.command("status <runId>").description("Read bounded lifecycle and confirmed work counts for a run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
31230
|
+
process.exitCode = await handleRunLifecycleStatus([
|
|
31231
|
+
runId,
|
|
31232
|
+
...options.json ? ["--json"] : []
|
|
31233
|
+
]);
|
|
31234
|
+
});
|
|
30931
31235
|
runs.command("get <runId>").description(
|
|
30932
31236
|
"Get status, progress, result, errors, and recovery metadata for a play run."
|
|
30933
31237
|
).addHelpText(
|