deepline 0.3.149 → 0.3.150
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 +347 -12
- package/dist/bundling-sources/sdk/src/http.ts +28 -2
- package/dist/bundling-sources/sdk/src/index.ts +4 -0
- package/dist/bundling-sources/sdk/src/play.ts +55 -5
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +18 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +33 -0
- package/dist/cli/index.js +393 -17
- package/dist/cli/index.mjs +399 -20
- package/dist/index.d.mts +61 -2
- package/dist/index.d.ts +61 -2
- package/dist/index.js +236 -13
- package/dist/index.mjs +236 -13
- 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
|
@@ -3068,7 +3068,7 @@ var SDK_RELEASE = {
|
|
|
3068
3068
|
// getters keep their established compatibility behavior.
|
|
3069
3069
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
3070
3070
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
3071
|
-
version: "0.3.
|
|
3071
|
+
version: "0.3.150",
|
|
3072
3072
|
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.",
|
|
3073
3073
|
packageCapabilities: {
|
|
3074
3074
|
updatePreferences: 1
|
|
@@ -3827,6 +3827,11 @@ var HttpClient = class {
|
|
|
3827
3827
|
body: options?.formData !== void 0 ? typeof options.formData === "function" ? options.formData() : options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
3828
3828
|
signal: controller.signal
|
|
3829
3829
|
});
|
|
3830
|
+
options?.onResponse?.(response);
|
|
3831
|
+
if (response.status === 404 && options?.allowNotFound) {
|
|
3832
|
+
clearTimeout(timeoutId);
|
|
3833
|
+
return null;
|
|
3834
|
+
}
|
|
3830
3835
|
clearTimeout(timeoutId);
|
|
3831
3836
|
const body = await response.text();
|
|
3832
3837
|
const parsed = parseResponseBody(body);
|
|
@@ -3966,7 +3971,8 @@ var HttpClient = class {
|
|
|
3966
3971
|
retryAfterMs: null,
|
|
3967
3972
|
networkKind: code === "NETWORK_TIMEOUT" ? "timeout" : code === "NETWORK_ABORTED" ? "unknown" : "unavailable",
|
|
3968
3973
|
networkScope: "client_to_deepline",
|
|
3969
|
-
details: mappedNetworkError.details
|
|
3974
|
+
details: mappedNetworkError.details,
|
|
3975
|
+
publicDetails: options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : null
|
|
3970
3976
|
}
|
|
3971
3977
|
),
|
|
3972
3978
|
lastError
|
|
@@ -6570,6 +6576,56 @@ var MONITOR_NON_RETRYABLE_MUTATION_OPTIONS = {
|
|
|
6570
6576
|
exactUrlOnly: true
|
|
6571
6577
|
};
|
|
6572
6578
|
var RAW_V2_EXECUTE_RESPONSE_CONTRACT = RAW_V2_TOOL_RESPONSE_CONTRACT;
|
|
6579
|
+
var DEFAULT_EXECUTION_RECOVERY_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
6580
|
+
function validateExecutionIdempotencyKey(key) {
|
|
6581
|
+
if (typeof key !== "string" || key.length < 1 || key.length > 200 || !/^[A-Za-z0-9._:-]+$/.test(key)) {
|
|
6582
|
+
throw new DeeplineError(
|
|
6583
|
+
"Execution idempotency keys must be 1\u2013200 ASCII letters, digits, dots, underscores, colons, or hyphens.",
|
|
6584
|
+
void 0,
|
|
6585
|
+
"IDEMPOTENCY_KEY_INVALID"
|
|
6586
|
+
);
|
|
6587
|
+
}
|
|
6588
|
+
}
|
|
6589
|
+
function isExecutionInProgressResponse(value) {
|
|
6590
|
+
return typeof value === "object" && value !== null && typeof value.executionRecovery === "object" && value.executionRecovery.state === "running";
|
|
6591
|
+
}
|
|
6592
|
+
function getExecutionRecoveryState(value) {
|
|
6593
|
+
if (typeof value !== "object" || value === null || typeof value.executionRecovery !== "object") {
|
|
6594
|
+
return null;
|
|
6595
|
+
}
|
|
6596
|
+
const state = value.executionRecovery.state;
|
|
6597
|
+
return state === "running" || state === "completed" || state === "outcome_unknown" ? state : null;
|
|
6598
|
+
}
|
|
6599
|
+
function isRecoverableExecutionAttemptError(error) {
|
|
6600
|
+
if (error instanceof ToolExecutionError) {
|
|
6601
|
+
return error.code === "EXECUTION_IN_PROGRESS" || error.origin === "deepline" && error.category === "network" && error.networkScope === "client_to_deepline";
|
|
6602
|
+
}
|
|
6603
|
+
return error instanceof DeeplineError && (error.code === "EXECUTION_IN_PROGRESS" || error.code?.startsWith("NETWORK_") === true && error.statusCode === void 0);
|
|
6604
|
+
}
|
|
6605
|
+
function isRecoverableExecutionLookupError(error) {
|
|
6606
|
+
if (isRecoverableExecutionAttemptError(error)) return true;
|
|
6607
|
+
return error instanceof DeeplineError && (error.statusCode === 429 || error.statusCode !== void 0 && error.statusCode >= 500);
|
|
6608
|
+
}
|
|
6609
|
+
function timeoutWithinRecoveryBudget(requestTimeoutMs, defaultRequestTimeoutMs, remainingMs) {
|
|
6610
|
+
return Math.min(requestTimeoutMs ?? defaultRequestTimeoutMs, remainingMs);
|
|
6611
|
+
}
|
|
6612
|
+
function executionRecoveryError(input2) {
|
|
6613
|
+
return new ToolExecutionError(input2.message, {
|
|
6614
|
+
toolId: input2.toolId,
|
|
6615
|
+
provider: null,
|
|
6616
|
+
operation: input2.toolId,
|
|
6617
|
+
code: input2.code,
|
|
6618
|
+
origin: "deepline",
|
|
6619
|
+
category: input2.code === "EXECUTION_OUTCOME_UNKNOWN" ? "unknown" : "conflict",
|
|
6620
|
+
retryable: input2.code !== "EXECUTION_OUTCOME_UNKNOWN",
|
|
6621
|
+
statusCode: null,
|
|
6622
|
+
requestId: null,
|
|
6623
|
+
retryAfterMs: null,
|
|
6624
|
+
networkKind: null,
|
|
6625
|
+
networkScope: null,
|
|
6626
|
+
publicDetails: { idempotencyKey: input2.idempotencyKey }
|
|
6627
|
+
});
|
|
6628
|
+
}
|
|
6573
6629
|
var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
|
|
6574
6630
|
var REGISTER_PLAY_ARTIFACTS_COMPILE_CONCURRENCY = 3;
|
|
6575
6631
|
var REGISTER_PLAY_ARTIFACTS_MAX_BATCH_COUNT = 3;
|
|
@@ -7483,29 +7539,183 @@ var DeeplineClient = class _DeeplineClient {
|
|
|
7483
7539
|
* Deepline execution envelope.
|
|
7484
7540
|
*/
|
|
7485
7541
|
async executeTool(toolId, input2, options) {
|
|
7542
|
+
const inputSnapshot = JSON.parse(JSON.stringify(input2));
|
|
7543
|
+
const metadataSnapshot = options?.metadata ? JSON.parse(JSON.stringify(options.metadata)) : void 0;
|
|
7544
|
+
const idempotencyKey = options?.idempotencyKey ?? (options?.recover ? crypto.randomUUID() : void 0);
|
|
7545
|
+
if (idempotencyKey !== void 0) {
|
|
7546
|
+
validateExecutionIdempotencyKey(idempotencyKey);
|
|
7547
|
+
if (options?.recoveryTimeoutMs !== void 0 && (!Number.isFinite(options.recoveryTimeoutMs) || options.recoveryTimeoutMs < 0)) {
|
|
7548
|
+
throw new DeeplineError(
|
|
7549
|
+
"recoveryTimeoutMs must be a finite, non-negative number.",
|
|
7550
|
+
void 0,
|
|
7551
|
+
"IDEMPOTENCY_KEY_INVALID"
|
|
7552
|
+
);
|
|
7553
|
+
}
|
|
7554
|
+
await options?.onExecution?.({ idempotencyKey });
|
|
7555
|
+
const timeoutMs2 = options?.timeout ?? resolveToolExecuteTimeoutMs(toolId, inputSnapshot);
|
|
7556
|
+
await this.lookupExecutionByKey(idempotencyKey, timeoutMs2);
|
|
7557
|
+
}
|
|
7558
|
+
const timeoutMs = options?.timeout ?? resolveToolExecuteTimeoutMs(toolId, inputSnapshot);
|
|
7486
7559
|
const headers = {
|
|
7487
7560
|
[EXECUTE_RESPONSE_CONTRACT_HEADER]: RAW_V2_EXECUTE_RESPONSE_CONTRACT,
|
|
7488
7561
|
[TOOL_EXECUTION_ERROR_SCHEMA_HEADER]: String(
|
|
7489
7562
|
TOOL_EXECUTION_ERROR_SCHEMA_VERSION
|
|
7490
7563
|
),
|
|
7491
7564
|
...options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : {},
|
|
7492
|
-
[EXECUTE_RESPONSE_INTENT_HEADER]: options?.responseIntent ?? "raw"
|
|
7565
|
+
[EXECUTE_RESPONSE_INTENT_HEADER]: options?.responseIntent ?? "raw",
|
|
7566
|
+
...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
|
|
7493
7567
|
};
|
|
7494
|
-
const
|
|
7568
|
+
const request = (requestTimeoutMs) => this.http.post(
|
|
7495
7569
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
7496
7570
|
{
|
|
7497
|
-
payload:
|
|
7498
|
-
...
|
|
7571
|
+
payload: inputSnapshot,
|
|
7572
|
+
...metadataSnapshot ? { metadata: metadataSnapshot } : {}
|
|
7499
7573
|
},
|
|
7500
7574
|
headers,
|
|
7501
7575
|
{
|
|
7502
|
-
timeout:
|
|
7503
|
-
maxRetries: options?.maxRetries ?? 0,
|
|
7576
|
+
timeout: requestTimeoutMs,
|
|
7577
|
+
maxRetries: idempotencyKey ? 0 : options?.maxRetries ?? 0,
|
|
7578
|
+
exactUrlOnly: true,
|
|
7579
|
+
toolId,
|
|
7580
|
+
idempotencyKey
|
|
7581
|
+
}
|
|
7582
|
+
);
|
|
7583
|
+
let response;
|
|
7584
|
+
let recoveryDeadline = null;
|
|
7585
|
+
const recoveryTimeoutMs = options?.recoveryTimeoutMs ?? DEFAULT_EXECUTION_RECOVERY_TIMEOUT_MS;
|
|
7586
|
+
let retryDelayMs = 1e3;
|
|
7587
|
+
const recoveryTimeoutError = () => executionRecoveryError({
|
|
7588
|
+
toolId,
|
|
7589
|
+
idempotencyKey,
|
|
7590
|
+
code: "EXECUTION_RECOVERY_TIMEOUT",
|
|
7591
|
+
message: `Execution recovery for ${idempotencyKey} did not finish before the recovery timeout. Inspect client.executions.getByKey() and resume with the same idempotency key.`
|
|
7592
|
+
});
|
|
7593
|
+
const waitForRecoveryRetry = async (deadline) => {
|
|
7594
|
+
const remainingMs = deadline - Date.now();
|
|
7595
|
+
if (remainingMs <= 0) throw recoveryTimeoutError();
|
|
7596
|
+
await new Promise(
|
|
7597
|
+
(resolve22) => setTimeout(resolve22, Math.min(retryDelayMs, remainingMs))
|
|
7598
|
+
);
|
|
7599
|
+
retryDelayMs = Math.min(retryDelayMs * 2, 5e3);
|
|
7600
|
+
};
|
|
7601
|
+
const lookupDuringRecovery = async () => {
|
|
7602
|
+
while (true) {
|
|
7603
|
+
const remainingMs = recoveryDeadline - Date.now();
|
|
7604
|
+
if (remainingMs <= 0) throw recoveryTimeoutError();
|
|
7605
|
+
try {
|
|
7606
|
+
return await this.lookupExecutionByKey(
|
|
7607
|
+
idempotencyKey,
|
|
7608
|
+
timeoutWithinRecoveryBudget(
|
|
7609
|
+
timeoutMs,
|
|
7610
|
+
this.config.timeout,
|
|
7611
|
+
remainingMs
|
|
7612
|
+
)
|
|
7613
|
+
);
|
|
7614
|
+
} catch (error) {
|
|
7615
|
+
if (!isRecoverableExecutionLookupError(error)) throw error;
|
|
7616
|
+
await waitForRecoveryRetry(recoveryDeadline);
|
|
7617
|
+
}
|
|
7618
|
+
}
|
|
7619
|
+
};
|
|
7620
|
+
while (true) {
|
|
7621
|
+
const remainingMs = recoveryDeadline === null ? null : recoveryDeadline - Date.now();
|
|
7622
|
+
if (remainingMs !== null && remainingMs <= 0) {
|
|
7623
|
+
throw recoveryTimeoutError();
|
|
7624
|
+
}
|
|
7625
|
+
try {
|
|
7626
|
+
response = await request(
|
|
7627
|
+
remainingMs === null ? timeoutMs : timeoutWithinRecoveryBudget(
|
|
7628
|
+
timeoutMs,
|
|
7629
|
+
this.config.timeout,
|
|
7630
|
+
remainingMs
|
|
7631
|
+
)
|
|
7632
|
+
);
|
|
7633
|
+
if (getExecutionRecoveryState(response) === "outcome_unknown") {
|
|
7634
|
+
if (!idempotencyKey) break;
|
|
7635
|
+
throw executionRecoveryError({
|
|
7636
|
+
toolId,
|
|
7637
|
+
idempotencyKey,
|
|
7638
|
+
code: "EXECUTION_OUTCOME_UNKNOWN",
|
|
7639
|
+
message: `The outcome of execution ${idempotencyKey} is unknown. Do not retry with a new key; inspect client.executions.getByKey().`
|
|
7640
|
+
});
|
|
7641
|
+
}
|
|
7642
|
+
if (!idempotencyKey || !isExecutionInProgressResponse(response)) {
|
|
7643
|
+
break;
|
|
7644
|
+
}
|
|
7645
|
+
if (recoveryDeadline === null) {
|
|
7646
|
+
recoveryDeadline = Date.now() + recoveryTimeoutMs;
|
|
7647
|
+
}
|
|
7648
|
+
} catch (error) {
|
|
7649
|
+
if (idempotencyKey && error instanceof DeeplineError && error.code === "EXECUTION_OUTCOME_UNKNOWN") {
|
|
7650
|
+
throw executionRecoveryError({
|
|
7651
|
+
toolId,
|
|
7652
|
+
idempotencyKey,
|
|
7653
|
+
code: "EXECUTION_OUTCOME_UNKNOWN",
|
|
7654
|
+
message: error.message
|
|
7655
|
+
});
|
|
7656
|
+
}
|
|
7657
|
+
if (!idempotencyKey || !isRecoverableExecutionAttemptError(error)) {
|
|
7658
|
+
throw error;
|
|
7659
|
+
}
|
|
7660
|
+
if (recoveryDeadline === null) {
|
|
7661
|
+
recoveryDeadline = Date.now() + recoveryTimeoutMs;
|
|
7662
|
+
}
|
|
7663
|
+
}
|
|
7664
|
+
await waitForRecoveryRetry(recoveryDeadline);
|
|
7665
|
+
const recovered = await lookupDuringRecovery();
|
|
7666
|
+
if (getExecutionRecoveryState(recovered) === "outcome_unknown") {
|
|
7667
|
+
throw executionRecoveryError({
|
|
7668
|
+
toolId,
|
|
7669
|
+
idempotencyKey,
|
|
7670
|
+
code: "EXECUTION_OUTCOME_UNKNOWN",
|
|
7671
|
+
message: `The outcome of execution ${idempotencyKey} is unknown. Do not retry with a new key; inspect client.executions.getByKey().`
|
|
7672
|
+
});
|
|
7673
|
+
}
|
|
7674
|
+
}
|
|
7675
|
+
const materialized = materializeToolExecutionResponse(response);
|
|
7676
|
+
return idempotencyKey ? { ...materialized, idempotencyKey } : materialized;
|
|
7677
|
+
}
|
|
7678
|
+
/** Read the durable state of a keyed tool execution. */
|
|
7679
|
+
async getExecutionByKey(idempotencyKey) {
|
|
7680
|
+
validateExecutionIdempotencyKey(idempotencyKey);
|
|
7681
|
+
const result = await this.lookupExecutionByKey(idempotencyKey);
|
|
7682
|
+
if (!result) {
|
|
7683
|
+
throw new DeeplineError(
|
|
7684
|
+
`No execution exists for idempotency key ${idempotencyKey}.`,
|
|
7685
|
+
404,
|
|
7686
|
+
"EXECUTION_NOT_FOUND"
|
|
7687
|
+
);
|
|
7688
|
+
}
|
|
7689
|
+
return result;
|
|
7690
|
+
}
|
|
7691
|
+
async lookupExecutionByKey(idempotencyKey, timeoutMs) {
|
|
7692
|
+
validateExecutionIdempotencyKey(idempotencyKey);
|
|
7693
|
+
let supported = false;
|
|
7694
|
+
const result = await this.http.get(
|
|
7695
|
+
`/api/v2/executions/by-key/${encodeURIComponent(idempotencyKey)}`,
|
|
7696
|
+
{
|
|
7504
7697
|
exactUrlOnly: true,
|
|
7505
|
-
|
|
7698
|
+
maxRetries: 0,
|
|
7699
|
+
timeout: timeoutMs,
|
|
7700
|
+
idempotencyKey,
|
|
7701
|
+
allowNotFound: true,
|
|
7702
|
+
onResponse: (response) => {
|
|
7703
|
+
supported = response.headers.get("X-Deepline-Idempotency-Supported") === "true";
|
|
7704
|
+
}
|
|
7506
7705
|
}
|
|
7507
7706
|
);
|
|
7508
|
-
|
|
7707
|
+
if (!supported) {
|
|
7708
|
+
throw new DeeplineError(
|
|
7709
|
+
"This Deepline server does not support recoverable tool executions; no tool was dispatched.",
|
|
7710
|
+
422,
|
|
7711
|
+
"IDEMPOTENCY_NOT_SUPPORTED"
|
|
7712
|
+
);
|
|
7713
|
+
}
|
|
7714
|
+
return result;
|
|
7715
|
+
}
|
|
7716
|
+
/** Public recovery namespace. */
|
|
7717
|
+
get executions() {
|
|
7718
|
+
return { getByKey: (key) => this.getExecutionByKey(key) };
|
|
7509
7719
|
}
|
|
7510
7720
|
/**
|
|
7511
7721
|
* Back-compatible alias for {@link executeTool}.
|
|
@@ -11132,6 +11342,15 @@ function errorToJsonPayload(error) {
|
|
|
11132
11342
|
if (typeof maybeRecord?.status === "number") {
|
|
11133
11343
|
details.status = maybeRecord.status;
|
|
11134
11344
|
}
|
|
11345
|
+
if (typeof maybeRecord?.requestId === "string" || maybeRecord?.requestId === null) {
|
|
11346
|
+
details.requestId = maybeRecord.requestId;
|
|
11347
|
+
}
|
|
11348
|
+
if (typeof maybeRecord?.retryable === "boolean") {
|
|
11349
|
+
details.retryable = maybeRecord.retryable;
|
|
11350
|
+
}
|
|
11351
|
+
if (maybeRecord?.publicDetails && typeof maybeRecord.publicDetails === "object" && !Array.isArray(maybeRecord.publicDetails)) {
|
|
11352
|
+
details.publicDetails = maybeRecord.publicDetails;
|
|
11353
|
+
}
|
|
11135
11354
|
if (maybeRecord?.details && typeof maybeRecord.details === "object" && !Array.isArray(maybeRecord.details)) {
|
|
11136
11355
|
details.details = maybeRecord.details;
|
|
11137
11356
|
}
|
|
@@ -36047,6 +36266,17 @@ function registerEnrichCommand(program) {
|
|
|
36047
36266
|
});
|
|
36048
36267
|
}
|
|
36049
36268
|
|
|
36269
|
+
// src/cli/commands/executions.ts
|
|
36270
|
+
function registerExecutionsCommands(program) {
|
|
36271
|
+
const executions = program.command("executions").description("Inspect and recover keyed tool executions.");
|
|
36272
|
+
executions.command("get").description("Retrieve a tool execution by its idempotency key.").requiredOption("--idempotency-key <key>", "Stable execution recovery key").option("--json", "Emit the stable JSON response").action(async (options) => {
|
|
36273
|
+
const result = await new DeeplineClient().executions.getByKey(
|
|
36274
|
+
options.idempotencyKey
|
|
36275
|
+
);
|
|
36276
|
+
printCommandEnvelope(result, { json: true });
|
|
36277
|
+
});
|
|
36278
|
+
}
|
|
36279
|
+
|
|
36050
36280
|
// src/cli/commands/feedback.ts
|
|
36051
36281
|
var import_commander3 = require("commander");
|
|
36052
36282
|
var import_node_fs13 = require("fs");
|
|
@@ -46928,6 +47158,25 @@ Examples:
|
|
|
46928
47158
|
|
|
46929
47159
|
// src/cli/commands/tools.ts
|
|
46930
47160
|
var import_commander4 = require("commander");
|
|
47161
|
+
var import_node_crypto12 = require("crypto");
|
|
47162
|
+
|
|
47163
|
+
// ../plays/row-identity.ts
|
|
47164
|
+
function stableValue(value) {
|
|
47165
|
+
if (Array.isArray(value)) {
|
|
47166
|
+
return value.map((entry) => stableValue(entry));
|
|
47167
|
+
}
|
|
47168
|
+
if (value && typeof value === "object") {
|
|
47169
|
+
return Object.fromEntries(
|
|
47170
|
+
Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, stableValue(entry)])
|
|
47171
|
+
);
|
|
47172
|
+
}
|
|
47173
|
+
return value;
|
|
47174
|
+
}
|
|
47175
|
+
function stableStringify(value) {
|
|
47176
|
+
return JSON.stringify(stableValue(value));
|
|
47177
|
+
}
|
|
47178
|
+
|
|
47179
|
+
// src/cli/commands/tools.ts
|
|
46931
47180
|
var import_node_fs26 = require("fs");
|
|
46932
47181
|
var import_node_os18 = require("os");
|
|
46933
47182
|
var import_node_path30 = require("path");
|
|
@@ -48058,6 +48307,15 @@ Examples:
|
|
|
48058
48307
|
).option("-o, --out <path>", "Write row-shaped tool output to this CSV path").option(
|
|
48059
48308
|
"--no-preview",
|
|
48060
48309
|
"Only print the extracted output path when applicable"
|
|
48310
|
+
).option(
|
|
48311
|
+
"--idempotency-key <key>",
|
|
48312
|
+
"Stable key for retrying or recovering this tool execution"
|
|
48313
|
+
).option(
|
|
48314
|
+
"--recover",
|
|
48315
|
+
"Generate or reuse a saved idempotency key for safe recovery"
|
|
48316
|
+
).option(
|
|
48317
|
+
"--recovery-timeout-ms <milliseconds>",
|
|
48318
|
+
"Maximum wait for a keyed execution to finish (default: 900000)"
|
|
48061
48319
|
).action(async (toolId, options) => {
|
|
48062
48320
|
const args = [
|
|
48063
48321
|
toolId,
|
|
@@ -48069,7 +48327,10 @@ Examples:
|
|
|
48069
48327
|
...options.timeout ? ["--timeout", options.timeout] : [],
|
|
48070
48328
|
...options.outputFormat ? ["--output-format", options.outputFormat] : [],
|
|
48071
48329
|
...options.out ? ["--out", options.out] : [],
|
|
48072
|
-
...options.preview === false ? ["--no-preview"] : []
|
|
48330
|
+
...options.preview === false ? ["--no-preview"] : [],
|
|
48331
|
+
...options.idempotencyKey ? ["--idempotency-key", options.idempotencyKey] : [],
|
|
48332
|
+
...options.recover ? ["--recover"] : [],
|
|
48333
|
+
...options.recoveryTimeoutMs ? ["--recovery-timeout-ms", options.recoveryTimeoutMs] : []
|
|
48073
48334
|
];
|
|
48074
48335
|
process.exitCode = await executeTool(args);
|
|
48075
48336
|
});
|
|
@@ -49306,7 +49567,7 @@ function parseExecuteOptions(args) {
|
|
|
49306
49567
|
const toolId = args[0];
|
|
49307
49568
|
if (!toolId) {
|
|
49308
49569
|
throw new Error(
|
|
49309
|
-
`Usage: deepline tools execute <toolId> [--param key=value ...] [--input '{"k":"v"}'] [--timeout <duration>] [--out rows.csv] [--output-format auto|csv|csv_file|json|json_file] [--no-preview]`
|
|
49570
|
+
`Usage: deepline tools execute <toolId> [--param key=value ...] [--input '{"k":"v"}'] [--timeout <duration>] [--recover | --idempotency-key <key>] [--recovery-timeout-ms <ms>] [--out rows.csv] [--output-format auto|csv|csv_file|json|json_file] [--no-preview]`
|
|
49310
49571
|
);
|
|
49311
49572
|
}
|
|
49312
49573
|
const params = {};
|
|
@@ -49314,6 +49575,9 @@ function parseExecuteOptions(args) {
|
|
|
49314
49575
|
let noPreview = false;
|
|
49315
49576
|
let outPath = null;
|
|
49316
49577
|
let timeoutMs;
|
|
49578
|
+
let idempotencyKey;
|
|
49579
|
+
let recover = false;
|
|
49580
|
+
let recoveryTimeoutMs;
|
|
49317
49581
|
for (let index = 1; index < args.length; index += 1) {
|
|
49318
49582
|
const arg = args[index];
|
|
49319
49583
|
if ((arg === "--param" || arg === "-p") && args[index + 1]) {
|
|
@@ -49349,13 +49613,39 @@ function parseExecuteOptions(args) {
|
|
|
49349
49613
|
noPreview = true;
|
|
49350
49614
|
continue;
|
|
49351
49615
|
}
|
|
49616
|
+
if (arg === "--idempotency-key" && args[index + 1]) {
|
|
49617
|
+
idempotencyKey = args[++index];
|
|
49618
|
+
continue;
|
|
49619
|
+
}
|
|
49620
|
+
if (arg === "--recover") {
|
|
49621
|
+
recover = true;
|
|
49622
|
+
continue;
|
|
49623
|
+
}
|
|
49624
|
+
if (arg === "--recovery-timeout-ms" && args[index + 1]) {
|
|
49625
|
+
const parsed = Number(args[++index]);
|
|
49626
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
49627
|
+
throw new Error("--recovery-timeout-ms must be a non-negative number.");
|
|
49628
|
+
}
|
|
49629
|
+
recoveryTimeoutMs = parsed;
|
|
49630
|
+
continue;
|
|
49631
|
+
}
|
|
49352
49632
|
if ((arg === "--out" || arg === "-o") && args[index + 1]) {
|
|
49353
49633
|
outPath = (0, import_node_path30.resolve)(args[++index]);
|
|
49354
49634
|
continue;
|
|
49355
49635
|
}
|
|
49356
49636
|
throw new Error(`Unknown option: ${arg}`);
|
|
49357
49637
|
}
|
|
49358
|
-
return {
|
|
49638
|
+
return {
|
|
49639
|
+
toolId,
|
|
49640
|
+
params,
|
|
49641
|
+
outputFormat,
|
|
49642
|
+
noPreview,
|
|
49643
|
+
outPath,
|
|
49644
|
+
timeoutMs,
|
|
49645
|
+
idempotencyKey,
|
|
49646
|
+
recover,
|
|
49647
|
+
recoveryTimeoutMs
|
|
49648
|
+
};
|
|
49359
49649
|
}
|
|
49360
49650
|
function parseToolExecuteTimeout(raw) {
|
|
49361
49651
|
const match = /^(\d+)(ms|s|m|h)?$/i.exec(raw.trim());
|
|
@@ -49375,6 +49665,65 @@ function parseToolExecuteTimeout(raw) {
|
|
|
49375
49665
|
}
|
|
49376
49666
|
return timeoutMs;
|
|
49377
49667
|
}
|
|
49668
|
+
function pendingToolExecutionPath(input2) {
|
|
49669
|
+
const config = resolveConfig();
|
|
49670
|
+
const fingerprint = (0, import_node_crypto12.createHash)("sha256").update(config.baseUrl).update("\0").update((0, import_node_crypto12.createHash)("sha256").update(config.apiKey).digest("hex")).update("\0").update(input2.toolId).update("\0").update(stableStringify(input2.params)).update("\0").update(input2.responseIntent).digest("hex");
|
|
49671
|
+
return (0, import_node_path30.join)(
|
|
49672
|
+
sdkCliStateDirPath(resolveConfig().baseUrl),
|
|
49673
|
+
`pending-tool-execution-${fingerprint}.json`
|
|
49674
|
+
);
|
|
49675
|
+
}
|
|
49676
|
+
function loadOrCreatePendingToolExecution(path) {
|
|
49677
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path30.dirname)(path), { recursive: true });
|
|
49678
|
+
if ((0, import_node_fs26.existsSync)(path)) {
|
|
49679
|
+
return readPendingToolExecution(path);
|
|
49680
|
+
}
|
|
49681
|
+
const idempotencyKey = (0, import_node_crypto12.randomUUID)();
|
|
49682
|
+
const temporaryPath = `${path}.${process.pid}.${(0, import_node_crypto12.randomUUID)()}.tmp`;
|
|
49683
|
+
try {
|
|
49684
|
+
(0, import_node_fs26.writeFileSync)(temporaryPath, `${JSON.stringify({ idempotencyKey })}
|
|
49685
|
+
`, {
|
|
49686
|
+
encoding: "utf8",
|
|
49687
|
+
flag: "wx",
|
|
49688
|
+
mode: 384
|
|
49689
|
+
});
|
|
49690
|
+
(0, import_node_fs26.linkSync)(temporaryPath, path);
|
|
49691
|
+
return idempotencyKey;
|
|
49692
|
+
} catch (error) {
|
|
49693
|
+
if (error.code !== "EEXIST") throw error;
|
|
49694
|
+
return readPendingToolExecution(path);
|
|
49695
|
+
} finally {
|
|
49696
|
+
if ((0, import_node_fs26.existsSync)(temporaryPath)) (0, import_node_fs26.unlinkSync)(temporaryPath);
|
|
49697
|
+
}
|
|
49698
|
+
}
|
|
49699
|
+
function readPendingToolExecution(path) {
|
|
49700
|
+
const saved = JSON.parse((0, import_node_fs26.readFileSync)(path, "utf8"));
|
|
49701
|
+
if (typeof saved.idempotencyKey !== "string") {
|
|
49702
|
+
throw new Error(
|
|
49703
|
+
`Cannot resume the saved tool execution at ${path}: invalid recovery key.`
|
|
49704
|
+
);
|
|
49705
|
+
}
|
|
49706
|
+
return saved.idempotencyKey;
|
|
49707
|
+
}
|
|
49708
|
+
async function completePendingToolExecution(path) {
|
|
49709
|
+
if (!path || !(0, import_node_fs26.existsSync)(path)) return;
|
|
49710
|
+
await new Promise((resolve22, reject) => {
|
|
49711
|
+
const onError = (error) => {
|
|
49712
|
+
process.stdout.off("error", onError);
|
|
49713
|
+
reject(error);
|
|
49714
|
+
};
|
|
49715
|
+
process.stdout.once("error", onError);
|
|
49716
|
+
process.stdout.write("", (error) => {
|
|
49717
|
+
process.stdout.off("error", onError);
|
|
49718
|
+
if (error) {
|
|
49719
|
+
reject(error);
|
|
49720
|
+
} else {
|
|
49721
|
+
resolve22();
|
|
49722
|
+
}
|
|
49723
|
+
});
|
|
49724
|
+
});
|
|
49725
|
+
if ((0, import_node_fs26.existsSync)(path)) (0, import_node_fs26.unlinkSync)(path);
|
|
49726
|
+
}
|
|
49378
49727
|
function safeFileStem(value) {
|
|
49379
49728
|
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
49380
49729
|
}
|
|
@@ -49492,6 +49841,8 @@ function buildToolExecuteBaseEnvelope(input2) {
|
|
|
49492
49841
|
] : [];
|
|
49493
49842
|
return {
|
|
49494
49843
|
...envelope,
|
|
49844
|
+
...input2.idempotencyKey ? { idempotency_key: input2.idempotencyKey } : {},
|
|
49845
|
+
...isRecord12(input2.rawResponse) && isRecord12(input2.rawResponse.executionRecovery) ? { execution_recovery: input2.rawResponse.executionRecovery } : {},
|
|
49495
49846
|
...envelopeHasCanonicalOutput || envelopeHasDeclaredOutput ? { output_preview: outputPreview } : { output: outputPreview },
|
|
49496
49847
|
...summaryEntries.length > 0 ? { summary: input2.summary } : {},
|
|
49497
49848
|
...warningMessages.length > 0 ? { warnings: warningMessages } : {},
|
|
@@ -49675,10 +50026,25 @@ async function executeTool(args) {
|
|
|
49675
50026
|
}
|
|
49676
50027
|
return 2;
|
|
49677
50028
|
}
|
|
50029
|
+
let idempotencyKey = parsed.idempotencyKey;
|
|
50030
|
+
let pendingRecoveryPath = null;
|
|
50031
|
+
const responseIntent = parsed.outPath || parsed.outputFormat === "csv" || parsed.outputFormat === "csv_file" ? "row_artifact" : "raw";
|
|
50032
|
+
if (parsed.recover && !idempotencyKey) {
|
|
50033
|
+
pendingRecoveryPath = pendingToolExecutionPath({
|
|
50034
|
+
toolId: parsed.toolId,
|
|
50035
|
+
params: parsed.params,
|
|
50036
|
+
responseIntent
|
|
50037
|
+
});
|
|
50038
|
+
idempotencyKey = loadOrCreatePendingToolExecution(pendingRecoveryPath);
|
|
50039
|
+
}
|
|
50040
|
+
if (idempotencyKey) {
|
|
50041
|
+
console.error(`Tool execution recovery key: ${idempotencyKey}`);
|
|
50042
|
+
}
|
|
49678
50043
|
const rawResponse = await client2.executeTool(parsed.toolId, parsed.params, {
|
|
49679
50044
|
...parsed.timeoutMs !== void 0 ? { timeout: parsed.timeoutMs } : {},
|
|
49680
|
-
responseIntent
|
|
49681
|
-
...
|
|
50045
|
+
responseIntent,
|
|
50046
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
50047
|
+
...parsed.recoveryTimeoutMs !== void 0 ? { recoveryTimeoutMs: parsed.recoveryTimeoutMs } : {}
|
|
49682
50048
|
});
|
|
49683
50049
|
const listConversion = tryConvertToList(rawResponse, {
|
|
49684
50050
|
listExtractorPaths: listExtractorPathsFromUsageGuidance(metadata)
|
|
@@ -49687,12 +50053,14 @@ async function executeTool(args) {
|
|
|
49687
50053
|
const baseEnvelope = buildToolExecuteBaseEnvelope({
|
|
49688
50054
|
toolId: parsed.toolId,
|
|
49689
50055
|
params: parsed.params,
|
|
50056
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
49690
50057
|
rawResponse,
|
|
49691
50058
|
listConversion,
|
|
49692
50059
|
summary
|
|
49693
50060
|
});
|
|
49694
50061
|
if (!parsed.outPath && (parsed.outputFormat === "json" || parsed.outputFormat === "auto" && shouldEmitJson())) {
|
|
49695
50062
|
printCommandEnvelope(baseEnvelope, { json: true });
|
|
50063
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49696
50064
|
return 0;
|
|
49697
50065
|
}
|
|
49698
50066
|
if (parsed.outputFormat === "json_file") {
|
|
@@ -49710,6 +50078,7 @@ async function executeTool(args) {
|
|
|
49710
50078
|
},
|
|
49711
50079
|
{ json: true }
|
|
49712
50080
|
);
|
|
50081
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49713
50082
|
return 0;
|
|
49714
50083
|
}
|
|
49715
50084
|
if (!listConversion) {
|
|
@@ -49738,9 +50107,11 @@ async function executeTool(args) {
|
|
|
49738
50107
|
},
|
|
49739
50108
|
{ json: parsed.outputFormat === "csv_file" || shouldEmitJson() }
|
|
49740
50109
|
);
|
|
50110
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49741
50111
|
return 0;
|
|
49742
50112
|
}
|
|
49743
50113
|
printCommandEnvelope(baseEnvelope, { json: false });
|
|
50114
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49744
50115
|
return 0;
|
|
49745
50116
|
}
|
|
49746
50117
|
const rowOutput = projectRowOutput(listConversion);
|
|
@@ -49763,6 +50134,7 @@ async function executeTool(args) {
|
|
|
49763
50134
|
});
|
|
49764
50135
|
if (parsed.outputFormat === "csv_file") {
|
|
49765
50136
|
printCommandEnvelope(materializedEnvelope, { json: true });
|
|
50137
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49766
50138
|
return 0;
|
|
49767
50139
|
}
|
|
49768
50140
|
if (parsed.outPath) {
|
|
@@ -49771,6 +50143,7 @@ async function executeTool(args) {
|
|
|
49771
50143
|
text: `Wrote ${csv.rowCount} row(s) to ${csv.path}
|
|
49772
50144
|
`
|
|
49773
50145
|
});
|
|
50146
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49774
50147
|
return 0;
|
|
49775
50148
|
}
|
|
49776
50149
|
if (parsed.noPreview) {
|
|
@@ -49785,9 +50158,11 @@ async function executeTool(args) {
|
|
|
49785
50158
|
{ json: shouldEmitJson(), text: `${csv.path}
|
|
49786
50159
|
` }
|
|
49787
50160
|
);
|
|
50161
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49788
50162
|
return 0;
|
|
49789
50163
|
}
|
|
49790
50164
|
printCommandEnvelope(materializedEnvelope, { json: false });
|
|
50165
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49791
50166
|
return 0;
|
|
49792
50167
|
}
|
|
49793
50168
|
|
|
@@ -49873,7 +50248,7 @@ var import_promises13 = require("fs/promises");
|
|
|
49873
50248
|
var import_node_path31 = require("path");
|
|
49874
50249
|
|
|
49875
50250
|
// src/cli/workflow-to-play.ts
|
|
49876
|
-
var
|
|
50251
|
+
var import_node_crypto13 = require("crypto");
|
|
49877
50252
|
var HITL_WAIT_FOR_SIGNAL_TOOL = "deepline_workflow_wait_for_signal";
|
|
49878
50253
|
var HITL_SLACK_TOOL = "slack_message_with_hitl";
|
|
49879
50254
|
var SUB_WORKFLOW_TOOL_PREFIX = "deepline_workflow_";
|
|
@@ -49979,7 +50354,7 @@ function sanitizePlayNameSegment(value) {
|
|
|
49979
50354
|
}
|
|
49980
50355
|
function deriveWorkflowPlayName(workflowName) {
|
|
49981
50356
|
const base = sanitizePlayNameSegment(workflowName) || "workflow";
|
|
49982
|
-
const suffix = (0,
|
|
50357
|
+
const suffix = (0, import_node_crypto13.createHash)("sha256").update(workflowName).digest("hex").slice(0, 8);
|
|
49983
50358
|
const reserved = suffix.length + 1;
|
|
49984
50359
|
const allowedBase = Math.max(1, MAX_PLAY_NAME_LENGTH - reserved);
|
|
49985
50360
|
let name = `${base.slice(0, allowedBase)}_${suffix}`;
|
|
@@ -50384,6 +50759,7 @@ function registerDeeplineCommandGroups(program) {
|
|
|
50384
50759
|
registerAuthCommands(program);
|
|
50385
50760
|
registerProviderCommands(program);
|
|
50386
50761
|
registerToolsCommands(program);
|
|
50762
|
+
registerExecutionsCommands(program);
|
|
50387
50763
|
registerPlayCommands(program);
|
|
50388
50764
|
registerSessionsCommands(program);
|
|
50389
50765
|
registerWorkflowCommands(program);
|