deepline 0.3.149 → 0.3.151
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/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +98 -3
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-projection-contract.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +7 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +5 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +363 -67
- 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.mjs
CHANGED
|
@@ -3063,7 +3063,7 @@ var SDK_RELEASE = {
|
|
|
3063
3063
|
// getters keep their established compatibility behavior.
|
|
3064
3064
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
3065
3065
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
3066
|
-
version: "0.3.
|
|
3066
|
+
version: "0.3.151",
|
|
3067
3067
|
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.",
|
|
3068
3068
|
packageCapabilities: {
|
|
3069
3069
|
updatePreferences: 1
|
|
@@ -3822,6 +3822,11 @@ var HttpClient = class {
|
|
|
3822
3822
|
body: options?.formData !== void 0 ? typeof options.formData === "function" ? options.formData() : options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
3823
3823
|
signal: controller.signal
|
|
3824
3824
|
});
|
|
3825
|
+
options?.onResponse?.(response);
|
|
3826
|
+
if (response.status === 404 && options?.allowNotFound) {
|
|
3827
|
+
clearTimeout(timeoutId);
|
|
3828
|
+
return null;
|
|
3829
|
+
}
|
|
3825
3830
|
clearTimeout(timeoutId);
|
|
3826
3831
|
const body = await response.text();
|
|
3827
3832
|
const parsed = parseResponseBody(body);
|
|
@@ -3961,7 +3966,8 @@ var HttpClient = class {
|
|
|
3961
3966
|
retryAfterMs: null,
|
|
3962
3967
|
networkKind: code === "NETWORK_TIMEOUT" ? "timeout" : code === "NETWORK_ABORTED" ? "unknown" : "unavailable",
|
|
3963
3968
|
networkScope: "client_to_deepline",
|
|
3964
|
-
details: mappedNetworkError.details
|
|
3969
|
+
details: mappedNetworkError.details,
|
|
3970
|
+
publicDetails: options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : null
|
|
3965
3971
|
}
|
|
3966
3972
|
),
|
|
3967
3973
|
lastError
|
|
@@ -6565,6 +6571,56 @@ var MONITOR_NON_RETRYABLE_MUTATION_OPTIONS = {
|
|
|
6565
6571
|
exactUrlOnly: true
|
|
6566
6572
|
};
|
|
6567
6573
|
var RAW_V2_EXECUTE_RESPONSE_CONTRACT = RAW_V2_TOOL_RESPONSE_CONTRACT;
|
|
6574
|
+
var DEFAULT_EXECUTION_RECOVERY_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
6575
|
+
function validateExecutionIdempotencyKey(key) {
|
|
6576
|
+
if (typeof key !== "string" || key.length < 1 || key.length > 200 || !/^[A-Za-z0-9._:-]+$/.test(key)) {
|
|
6577
|
+
throw new DeeplineError(
|
|
6578
|
+
"Execution idempotency keys must be 1\u2013200 ASCII letters, digits, dots, underscores, colons, or hyphens.",
|
|
6579
|
+
void 0,
|
|
6580
|
+
"IDEMPOTENCY_KEY_INVALID"
|
|
6581
|
+
);
|
|
6582
|
+
}
|
|
6583
|
+
}
|
|
6584
|
+
function isExecutionInProgressResponse(value) {
|
|
6585
|
+
return typeof value === "object" && value !== null && typeof value.executionRecovery === "object" && value.executionRecovery.state === "running";
|
|
6586
|
+
}
|
|
6587
|
+
function getExecutionRecoveryState(value) {
|
|
6588
|
+
if (typeof value !== "object" || value === null || typeof value.executionRecovery !== "object") {
|
|
6589
|
+
return null;
|
|
6590
|
+
}
|
|
6591
|
+
const state = value.executionRecovery.state;
|
|
6592
|
+
return state === "running" || state === "completed" || state === "outcome_unknown" ? state : null;
|
|
6593
|
+
}
|
|
6594
|
+
function isRecoverableExecutionAttemptError(error) {
|
|
6595
|
+
if (error instanceof ToolExecutionError) {
|
|
6596
|
+
return error.code === "EXECUTION_IN_PROGRESS" || error.origin === "deepline" && error.category === "network" && error.networkScope === "client_to_deepline";
|
|
6597
|
+
}
|
|
6598
|
+
return error instanceof DeeplineError && (error.code === "EXECUTION_IN_PROGRESS" || error.code?.startsWith("NETWORK_") === true && error.statusCode === void 0);
|
|
6599
|
+
}
|
|
6600
|
+
function isRecoverableExecutionLookupError(error) {
|
|
6601
|
+
if (isRecoverableExecutionAttemptError(error)) return true;
|
|
6602
|
+
return error instanceof DeeplineError && (error.statusCode === 429 || error.statusCode !== void 0 && error.statusCode >= 500);
|
|
6603
|
+
}
|
|
6604
|
+
function timeoutWithinRecoveryBudget(requestTimeoutMs, defaultRequestTimeoutMs, remainingMs) {
|
|
6605
|
+
return Math.min(requestTimeoutMs ?? defaultRequestTimeoutMs, remainingMs);
|
|
6606
|
+
}
|
|
6607
|
+
function executionRecoveryError(input2) {
|
|
6608
|
+
return new ToolExecutionError(input2.message, {
|
|
6609
|
+
toolId: input2.toolId,
|
|
6610
|
+
provider: null,
|
|
6611
|
+
operation: input2.toolId,
|
|
6612
|
+
code: input2.code,
|
|
6613
|
+
origin: "deepline",
|
|
6614
|
+
category: input2.code === "EXECUTION_OUTCOME_UNKNOWN" ? "unknown" : "conflict",
|
|
6615
|
+
retryable: input2.code !== "EXECUTION_OUTCOME_UNKNOWN",
|
|
6616
|
+
statusCode: null,
|
|
6617
|
+
requestId: null,
|
|
6618
|
+
retryAfterMs: null,
|
|
6619
|
+
networkKind: null,
|
|
6620
|
+
networkScope: null,
|
|
6621
|
+
publicDetails: { idempotencyKey: input2.idempotencyKey }
|
|
6622
|
+
});
|
|
6623
|
+
}
|
|
6568
6624
|
var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
|
|
6569
6625
|
var REGISTER_PLAY_ARTIFACTS_COMPILE_CONCURRENCY = 3;
|
|
6570
6626
|
var REGISTER_PLAY_ARTIFACTS_MAX_BATCH_COUNT = 3;
|
|
@@ -7478,29 +7534,183 @@ var DeeplineClient = class _DeeplineClient {
|
|
|
7478
7534
|
* Deepline execution envelope.
|
|
7479
7535
|
*/
|
|
7480
7536
|
async executeTool(toolId, input2, options) {
|
|
7537
|
+
const inputSnapshot = JSON.parse(JSON.stringify(input2));
|
|
7538
|
+
const metadataSnapshot = options?.metadata ? JSON.parse(JSON.stringify(options.metadata)) : void 0;
|
|
7539
|
+
const idempotencyKey = options?.idempotencyKey ?? (options?.recover ? crypto.randomUUID() : void 0);
|
|
7540
|
+
if (idempotencyKey !== void 0) {
|
|
7541
|
+
validateExecutionIdempotencyKey(idempotencyKey);
|
|
7542
|
+
if (options?.recoveryTimeoutMs !== void 0 && (!Number.isFinite(options.recoveryTimeoutMs) || options.recoveryTimeoutMs < 0)) {
|
|
7543
|
+
throw new DeeplineError(
|
|
7544
|
+
"recoveryTimeoutMs must be a finite, non-negative number.",
|
|
7545
|
+
void 0,
|
|
7546
|
+
"IDEMPOTENCY_KEY_INVALID"
|
|
7547
|
+
);
|
|
7548
|
+
}
|
|
7549
|
+
await options?.onExecution?.({ idempotencyKey });
|
|
7550
|
+
const timeoutMs2 = options?.timeout ?? resolveToolExecuteTimeoutMs(toolId, inputSnapshot);
|
|
7551
|
+
await this.lookupExecutionByKey(idempotencyKey, timeoutMs2);
|
|
7552
|
+
}
|
|
7553
|
+
const timeoutMs = options?.timeout ?? resolveToolExecuteTimeoutMs(toolId, inputSnapshot);
|
|
7481
7554
|
const headers = {
|
|
7482
7555
|
[EXECUTE_RESPONSE_CONTRACT_HEADER]: RAW_V2_EXECUTE_RESPONSE_CONTRACT,
|
|
7483
7556
|
[TOOL_EXECUTION_ERROR_SCHEMA_HEADER]: String(
|
|
7484
7557
|
TOOL_EXECUTION_ERROR_SCHEMA_VERSION
|
|
7485
7558
|
),
|
|
7486
7559
|
...options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : {},
|
|
7487
|
-
[EXECUTE_RESPONSE_INTENT_HEADER]: options?.responseIntent ?? "raw"
|
|
7560
|
+
[EXECUTE_RESPONSE_INTENT_HEADER]: options?.responseIntent ?? "raw",
|
|
7561
|
+
...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
|
|
7488
7562
|
};
|
|
7489
|
-
const
|
|
7563
|
+
const request = (requestTimeoutMs) => this.http.post(
|
|
7490
7564
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
7491
7565
|
{
|
|
7492
|
-
payload:
|
|
7493
|
-
...
|
|
7566
|
+
payload: inputSnapshot,
|
|
7567
|
+
...metadataSnapshot ? { metadata: metadataSnapshot } : {}
|
|
7494
7568
|
},
|
|
7495
7569
|
headers,
|
|
7496
7570
|
{
|
|
7497
|
-
timeout:
|
|
7498
|
-
maxRetries: options?.maxRetries ?? 0,
|
|
7571
|
+
timeout: requestTimeoutMs,
|
|
7572
|
+
maxRetries: idempotencyKey ? 0 : options?.maxRetries ?? 0,
|
|
7499
7573
|
exactUrlOnly: true,
|
|
7500
|
-
toolId
|
|
7574
|
+
toolId,
|
|
7575
|
+
idempotencyKey
|
|
7576
|
+
}
|
|
7577
|
+
);
|
|
7578
|
+
let response;
|
|
7579
|
+
let recoveryDeadline = null;
|
|
7580
|
+
const recoveryTimeoutMs = options?.recoveryTimeoutMs ?? DEFAULT_EXECUTION_RECOVERY_TIMEOUT_MS;
|
|
7581
|
+
let retryDelayMs = 1e3;
|
|
7582
|
+
const recoveryTimeoutError = () => executionRecoveryError({
|
|
7583
|
+
toolId,
|
|
7584
|
+
idempotencyKey,
|
|
7585
|
+
code: "EXECUTION_RECOVERY_TIMEOUT",
|
|
7586
|
+
message: `Execution recovery for ${idempotencyKey} did not finish before the recovery timeout. Inspect client.executions.getByKey() and resume with the same idempotency key.`
|
|
7587
|
+
});
|
|
7588
|
+
const waitForRecoveryRetry = async (deadline) => {
|
|
7589
|
+
const remainingMs = deadline - Date.now();
|
|
7590
|
+
if (remainingMs <= 0) throw recoveryTimeoutError();
|
|
7591
|
+
await new Promise(
|
|
7592
|
+
(resolve22) => setTimeout(resolve22, Math.min(retryDelayMs, remainingMs))
|
|
7593
|
+
);
|
|
7594
|
+
retryDelayMs = Math.min(retryDelayMs * 2, 5e3);
|
|
7595
|
+
};
|
|
7596
|
+
const lookupDuringRecovery = async () => {
|
|
7597
|
+
while (true) {
|
|
7598
|
+
const remainingMs = recoveryDeadline - Date.now();
|
|
7599
|
+
if (remainingMs <= 0) throw recoveryTimeoutError();
|
|
7600
|
+
try {
|
|
7601
|
+
return await this.lookupExecutionByKey(
|
|
7602
|
+
idempotencyKey,
|
|
7603
|
+
timeoutWithinRecoveryBudget(
|
|
7604
|
+
timeoutMs,
|
|
7605
|
+
this.config.timeout,
|
|
7606
|
+
remainingMs
|
|
7607
|
+
)
|
|
7608
|
+
);
|
|
7609
|
+
} catch (error) {
|
|
7610
|
+
if (!isRecoverableExecutionLookupError(error)) throw error;
|
|
7611
|
+
await waitForRecoveryRetry(recoveryDeadline);
|
|
7612
|
+
}
|
|
7613
|
+
}
|
|
7614
|
+
};
|
|
7615
|
+
while (true) {
|
|
7616
|
+
const remainingMs = recoveryDeadline === null ? null : recoveryDeadline - Date.now();
|
|
7617
|
+
if (remainingMs !== null && remainingMs <= 0) {
|
|
7618
|
+
throw recoveryTimeoutError();
|
|
7619
|
+
}
|
|
7620
|
+
try {
|
|
7621
|
+
response = await request(
|
|
7622
|
+
remainingMs === null ? timeoutMs : timeoutWithinRecoveryBudget(
|
|
7623
|
+
timeoutMs,
|
|
7624
|
+
this.config.timeout,
|
|
7625
|
+
remainingMs
|
|
7626
|
+
)
|
|
7627
|
+
);
|
|
7628
|
+
if (getExecutionRecoveryState(response) === "outcome_unknown") {
|
|
7629
|
+
if (!idempotencyKey) break;
|
|
7630
|
+
throw executionRecoveryError({
|
|
7631
|
+
toolId,
|
|
7632
|
+
idempotencyKey,
|
|
7633
|
+
code: "EXECUTION_OUTCOME_UNKNOWN",
|
|
7634
|
+
message: `The outcome of execution ${idempotencyKey} is unknown. Do not retry with a new key; inspect client.executions.getByKey().`
|
|
7635
|
+
});
|
|
7636
|
+
}
|
|
7637
|
+
if (!idempotencyKey || !isExecutionInProgressResponse(response)) {
|
|
7638
|
+
break;
|
|
7639
|
+
}
|
|
7640
|
+
if (recoveryDeadline === null) {
|
|
7641
|
+
recoveryDeadline = Date.now() + recoveryTimeoutMs;
|
|
7642
|
+
}
|
|
7643
|
+
} catch (error) {
|
|
7644
|
+
if (idempotencyKey && error instanceof DeeplineError && error.code === "EXECUTION_OUTCOME_UNKNOWN") {
|
|
7645
|
+
throw executionRecoveryError({
|
|
7646
|
+
toolId,
|
|
7647
|
+
idempotencyKey,
|
|
7648
|
+
code: "EXECUTION_OUTCOME_UNKNOWN",
|
|
7649
|
+
message: error.message
|
|
7650
|
+
});
|
|
7651
|
+
}
|
|
7652
|
+
if (!idempotencyKey || !isRecoverableExecutionAttemptError(error)) {
|
|
7653
|
+
throw error;
|
|
7654
|
+
}
|
|
7655
|
+
if (recoveryDeadline === null) {
|
|
7656
|
+
recoveryDeadline = Date.now() + recoveryTimeoutMs;
|
|
7657
|
+
}
|
|
7658
|
+
}
|
|
7659
|
+
await waitForRecoveryRetry(recoveryDeadline);
|
|
7660
|
+
const recovered = await lookupDuringRecovery();
|
|
7661
|
+
if (getExecutionRecoveryState(recovered) === "outcome_unknown") {
|
|
7662
|
+
throw executionRecoveryError({
|
|
7663
|
+
toolId,
|
|
7664
|
+
idempotencyKey,
|
|
7665
|
+
code: "EXECUTION_OUTCOME_UNKNOWN",
|
|
7666
|
+
message: `The outcome of execution ${idempotencyKey} is unknown. Do not retry with a new key; inspect client.executions.getByKey().`
|
|
7667
|
+
});
|
|
7668
|
+
}
|
|
7669
|
+
}
|
|
7670
|
+
const materialized = materializeToolExecutionResponse(response);
|
|
7671
|
+
return idempotencyKey ? { ...materialized, idempotencyKey } : materialized;
|
|
7672
|
+
}
|
|
7673
|
+
/** Read the durable state of a keyed tool execution. */
|
|
7674
|
+
async getExecutionByKey(idempotencyKey) {
|
|
7675
|
+
validateExecutionIdempotencyKey(idempotencyKey);
|
|
7676
|
+
const result = await this.lookupExecutionByKey(idempotencyKey);
|
|
7677
|
+
if (!result) {
|
|
7678
|
+
throw new DeeplineError(
|
|
7679
|
+
`No execution exists for idempotency key ${idempotencyKey}.`,
|
|
7680
|
+
404,
|
|
7681
|
+
"EXECUTION_NOT_FOUND"
|
|
7682
|
+
);
|
|
7683
|
+
}
|
|
7684
|
+
return result;
|
|
7685
|
+
}
|
|
7686
|
+
async lookupExecutionByKey(idempotencyKey, timeoutMs) {
|
|
7687
|
+
validateExecutionIdempotencyKey(idempotencyKey);
|
|
7688
|
+
let supported = false;
|
|
7689
|
+
const result = await this.http.get(
|
|
7690
|
+
`/api/v2/executions/by-key/${encodeURIComponent(idempotencyKey)}`,
|
|
7691
|
+
{
|
|
7692
|
+
exactUrlOnly: true,
|
|
7693
|
+
maxRetries: 0,
|
|
7694
|
+
timeout: timeoutMs,
|
|
7695
|
+
idempotencyKey,
|
|
7696
|
+
allowNotFound: true,
|
|
7697
|
+
onResponse: (response) => {
|
|
7698
|
+
supported = response.headers.get("X-Deepline-Idempotency-Supported") === "true";
|
|
7699
|
+
}
|
|
7501
7700
|
}
|
|
7502
7701
|
);
|
|
7503
|
-
|
|
7702
|
+
if (!supported) {
|
|
7703
|
+
throw new DeeplineError(
|
|
7704
|
+
"This Deepline server does not support recoverable tool executions; no tool was dispatched.",
|
|
7705
|
+
422,
|
|
7706
|
+
"IDEMPOTENCY_NOT_SUPPORTED"
|
|
7707
|
+
);
|
|
7708
|
+
}
|
|
7709
|
+
return result;
|
|
7710
|
+
}
|
|
7711
|
+
/** Public recovery namespace. */
|
|
7712
|
+
get executions() {
|
|
7713
|
+
return { getByKey: (key) => this.getExecutionByKey(key) };
|
|
7504
7714
|
}
|
|
7505
7715
|
/**
|
|
7506
7716
|
* Back-compatible alias for {@link executeTool}.
|
|
@@ -11139,6 +11349,15 @@ function errorToJsonPayload(error) {
|
|
|
11139
11349
|
if (typeof maybeRecord?.status === "number") {
|
|
11140
11350
|
details.status = maybeRecord.status;
|
|
11141
11351
|
}
|
|
11352
|
+
if (typeof maybeRecord?.requestId === "string" || maybeRecord?.requestId === null) {
|
|
11353
|
+
details.requestId = maybeRecord.requestId;
|
|
11354
|
+
}
|
|
11355
|
+
if (typeof maybeRecord?.retryable === "boolean") {
|
|
11356
|
+
details.retryable = maybeRecord.retryable;
|
|
11357
|
+
}
|
|
11358
|
+
if (maybeRecord?.publicDetails && typeof maybeRecord.publicDetails === "object" && !Array.isArray(maybeRecord.publicDetails)) {
|
|
11359
|
+
details.publicDetails = maybeRecord.publicDetails;
|
|
11360
|
+
}
|
|
11142
11361
|
if (maybeRecord?.details && typeof maybeRecord.details === "object" && !Array.isArray(maybeRecord.details)) {
|
|
11143
11362
|
details.details = maybeRecord.details;
|
|
11144
11363
|
}
|
|
@@ -36119,6 +36338,17 @@ function registerEnrichCommand(program) {
|
|
|
36119
36338
|
});
|
|
36120
36339
|
}
|
|
36121
36340
|
|
|
36341
|
+
// src/cli/commands/executions.ts
|
|
36342
|
+
function registerExecutionsCommands(program) {
|
|
36343
|
+
const executions = program.command("executions").description("Inspect and recover keyed tool executions.");
|
|
36344
|
+
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) => {
|
|
36345
|
+
const result = await new DeeplineClient().executions.getByKey(
|
|
36346
|
+
options.idempotencyKey
|
|
36347
|
+
);
|
|
36348
|
+
printCommandEnvelope(result, { json: true });
|
|
36349
|
+
});
|
|
36350
|
+
}
|
|
36351
|
+
|
|
36122
36352
|
// src/cli/commands/feedback.ts
|
|
36123
36353
|
import { Option as Option3 } from "commander";
|
|
36124
36354
|
import { readFileSync as readFileSync10 } from "fs";
|
|
@@ -47058,15 +47288,37 @@ Examples:
|
|
|
47058
47288
|
|
|
47059
47289
|
// src/cli/commands/tools.ts
|
|
47060
47290
|
import { Option as Option4 } from "commander";
|
|
47291
|
+
import { createHash as createHash6, randomUUID as randomUUID9 } from "crypto";
|
|
47292
|
+
|
|
47293
|
+
// ../plays/row-identity.ts
|
|
47294
|
+
function stableValue(value) {
|
|
47295
|
+
if (Array.isArray(value)) {
|
|
47296
|
+
return value.map((entry) => stableValue(entry));
|
|
47297
|
+
}
|
|
47298
|
+
if (value && typeof value === "object") {
|
|
47299
|
+
return Object.fromEntries(
|
|
47300
|
+
Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, stableValue(entry)])
|
|
47301
|
+
);
|
|
47302
|
+
}
|
|
47303
|
+
return value;
|
|
47304
|
+
}
|
|
47305
|
+
function stableStringify(value) {
|
|
47306
|
+
return JSON.stringify(stableValue(value));
|
|
47307
|
+
}
|
|
47308
|
+
|
|
47309
|
+
// src/cli/commands/tools.ts
|
|
47061
47310
|
import {
|
|
47062
47311
|
chmodSync,
|
|
47063
47312
|
existsSync as existsSync19,
|
|
47313
|
+
linkSync,
|
|
47314
|
+
mkdirSync as mkdirSync16,
|
|
47064
47315
|
mkdtempSync,
|
|
47065
47316
|
readFileSync as readFileSync22,
|
|
47317
|
+
unlinkSync as unlinkSync4,
|
|
47066
47318
|
writeFileSync as writeFileSync20
|
|
47067
47319
|
} from "fs";
|
|
47068
47320
|
import { tmpdir as tmpdir6 } from "os";
|
|
47069
|
-
import { join as join25, resolve as resolve20 } from "path";
|
|
47321
|
+
import { dirname as dirname20, join as join25, resolve as resolve20 } from "path";
|
|
47070
47322
|
|
|
47071
47323
|
// src/tool-output.ts
|
|
47072
47324
|
import {
|
|
@@ -48200,6 +48452,15 @@ Examples:
|
|
|
48200
48452
|
).option("-o, --out <path>", "Write row-shaped tool output to this CSV path").option(
|
|
48201
48453
|
"--no-preview",
|
|
48202
48454
|
"Only print the extracted output path when applicable"
|
|
48455
|
+
).option(
|
|
48456
|
+
"--idempotency-key <key>",
|
|
48457
|
+
"Stable key for retrying or recovering this tool execution"
|
|
48458
|
+
).option(
|
|
48459
|
+
"--recover",
|
|
48460
|
+
"Generate or reuse a saved idempotency key for safe recovery"
|
|
48461
|
+
).option(
|
|
48462
|
+
"--recovery-timeout-ms <milliseconds>",
|
|
48463
|
+
"Maximum wait for a keyed execution to finish (default: 900000)"
|
|
48203
48464
|
).action(async (toolId, options) => {
|
|
48204
48465
|
const args = [
|
|
48205
48466
|
toolId,
|
|
@@ -48211,7 +48472,10 @@ Examples:
|
|
|
48211
48472
|
...options.timeout ? ["--timeout", options.timeout] : [],
|
|
48212
48473
|
...options.outputFormat ? ["--output-format", options.outputFormat] : [],
|
|
48213
48474
|
...options.out ? ["--out", options.out] : [],
|
|
48214
|
-
...options.preview === false ? ["--no-preview"] : []
|
|
48475
|
+
...options.preview === false ? ["--no-preview"] : [],
|
|
48476
|
+
...options.idempotencyKey ? ["--idempotency-key", options.idempotencyKey] : [],
|
|
48477
|
+
...options.recover ? ["--recover"] : [],
|
|
48478
|
+
...options.recoveryTimeoutMs ? ["--recovery-timeout-ms", options.recoveryTimeoutMs] : []
|
|
48215
48479
|
];
|
|
48216
48480
|
process.exitCode = await executeTool(args);
|
|
48217
48481
|
});
|
|
@@ -49448,7 +49712,7 @@ function parseExecuteOptions(args) {
|
|
|
49448
49712
|
const toolId = args[0];
|
|
49449
49713
|
if (!toolId) {
|
|
49450
49714
|
throw new Error(
|
|
49451
|
-
`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]`
|
|
49715
|
+
`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]`
|
|
49452
49716
|
);
|
|
49453
49717
|
}
|
|
49454
49718
|
const params = {};
|
|
@@ -49456,6 +49720,9 @@ function parseExecuteOptions(args) {
|
|
|
49456
49720
|
let noPreview = false;
|
|
49457
49721
|
let outPath = null;
|
|
49458
49722
|
let timeoutMs;
|
|
49723
|
+
let idempotencyKey;
|
|
49724
|
+
let recover = false;
|
|
49725
|
+
let recoveryTimeoutMs;
|
|
49459
49726
|
for (let index = 1; index < args.length; index += 1) {
|
|
49460
49727
|
const arg = args[index];
|
|
49461
49728
|
if ((arg === "--param" || arg === "-p") && args[index + 1]) {
|
|
@@ -49491,13 +49758,39 @@ function parseExecuteOptions(args) {
|
|
|
49491
49758
|
noPreview = true;
|
|
49492
49759
|
continue;
|
|
49493
49760
|
}
|
|
49761
|
+
if (arg === "--idempotency-key" && args[index + 1]) {
|
|
49762
|
+
idempotencyKey = args[++index];
|
|
49763
|
+
continue;
|
|
49764
|
+
}
|
|
49765
|
+
if (arg === "--recover") {
|
|
49766
|
+
recover = true;
|
|
49767
|
+
continue;
|
|
49768
|
+
}
|
|
49769
|
+
if (arg === "--recovery-timeout-ms" && args[index + 1]) {
|
|
49770
|
+
const parsed = Number(args[++index]);
|
|
49771
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
49772
|
+
throw new Error("--recovery-timeout-ms must be a non-negative number.");
|
|
49773
|
+
}
|
|
49774
|
+
recoveryTimeoutMs = parsed;
|
|
49775
|
+
continue;
|
|
49776
|
+
}
|
|
49494
49777
|
if ((arg === "--out" || arg === "-o") && args[index + 1]) {
|
|
49495
49778
|
outPath = resolve20(args[++index]);
|
|
49496
49779
|
continue;
|
|
49497
49780
|
}
|
|
49498
49781
|
throw new Error(`Unknown option: ${arg}`);
|
|
49499
49782
|
}
|
|
49500
|
-
return {
|
|
49783
|
+
return {
|
|
49784
|
+
toolId,
|
|
49785
|
+
params,
|
|
49786
|
+
outputFormat,
|
|
49787
|
+
noPreview,
|
|
49788
|
+
outPath,
|
|
49789
|
+
timeoutMs,
|
|
49790
|
+
idempotencyKey,
|
|
49791
|
+
recover,
|
|
49792
|
+
recoveryTimeoutMs
|
|
49793
|
+
};
|
|
49501
49794
|
}
|
|
49502
49795
|
function parseToolExecuteTimeout(raw) {
|
|
49503
49796
|
const match = /^(\d+)(ms|s|m|h)?$/i.exec(raw.trim());
|
|
@@ -49517,6 +49810,65 @@ function parseToolExecuteTimeout(raw) {
|
|
|
49517
49810
|
}
|
|
49518
49811
|
return timeoutMs;
|
|
49519
49812
|
}
|
|
49813
|
+
function pendingToolExecutionPath(input2) {
|
|
49814
|
+
const config = resolveConfig();
|
|
49815
|
+
const fingerprint = createHash6("sha256").update(config.baseUrl).update("\0").update(createHash6("sha256").update(config.apiKey).digest("hex")).update("\0").update(input2.toolId).update("\0").update(stableStringify(input2.params)).update("\0").update(input2.responseIntent).digest("hex");
|
|
49816
|
+
return join25(
|
|
49817
|
+
sdkCliStateDirPath(resolveConfig().baseUrl),
|
|
49818
|
+
`pending-tool-execution-${fingerprint}.json`
|
|
49819
|
+
);
|
|
49820
|
+
}
|
|
49821
|
+
function loadOrCreatePendingToolExecution(path) {
|
|
49822
|
+
mkdirSync16(dirname20(path), { recursive: true });
|
|
49823
|
+
if (existsSync19(path)) {
|
|
49824
|
+
return readPendingToolExecution(path);
|
|
49825
|
+
}
|
|
49826
|
+
const idempotencyKey = randomUUID9();
|
|
49827
|
+
const temporaryPath = `${path}.${process.pid}.${randomUUID9()}.tmp`;
|
|
49828
|
+
try {
|
|
49829
|
+
writeFileSync20(temporaryPath, `${JSON.stringify({ idempotencyKey })}
|
|
49830
|
+
`, {
|
|
49831
|
+
encoding: "utf8",
|
|
49832
|
+
flag: "wx",
|
|
49833
|
+
mode: 384
|
|
49834
|
+
});
|
|
49835
|
+
linkSync(temporaryPath, path);
|
|
49836
|
+
return idempotencyKey;
|
|
49837
|
+
} catch (error) {
|
|
49838
|
+
if (error.code !== "EEXIST") throw error;
|
|
49839
|
+
return readPendingToolExecution(path);
|
|
49840
|
+
} finally {
|
|
49841
|
+
if (existsSync19(temporaryPath)) unlinkSync4(temporaryPath);
|
|
49842
|
+
}
|
|
49843
|
+
}
|
|
49844
|
+
function readPendingToolExecution(path) {
|
|
49845
|
+
const saved = JSON.parse(readFileSync22(path, "utf8"));
|
|
49846
|
+
if (typeof saved.idempotencyKey !== "string") {
|
|
49847
|
+
throw new Error(
|
|
49848
|
+
`Cannot resume the saved tool execution at ${path}: invalid recovery key.`
|
|
49849
|
+
);
|
|
49850
|
+
}
|
|
49851
|
+
return saved.idempotencyKey;
|
|
49852
|
+
}
|
|
49853
|
+
async function completePendingToolExecution(path) {
|
|
49854
|
+
if (!path || !existsSync19(path)) return;
|
|
49855
|
+
await new Promise((resolve22, reject) => {
|
|
49856
|
+
const onError = (error) => {
|
|
49857
|
+
process.stdout.off("error", onError);
|
|
49858
|
+
reject(error);
|
|
49859
|
+
};
|
|
49860
|
+
process.stdout.once("error", onError);
|
|
49861
|
+
process.stdout.write("", (error) => {
|
|
49862
|
+
process.stdout.off("error", onError);
|
|
49863
|
+
if (error) {
|
|
49864
|
+
reject(error);
|
|
49865
|
+
} else {
|
|
49866
|
+
resolve22();
|
|
49867
|
+
}
|
|
49868
|
+
});
|
|
49869
|
+
});
|
|
49870
|
+
if (existsSync19(path)) unlinkSync4(path);
|
|
49871
|
+
}
|
|
49520
49872
|
function safeFileStem(value) {
|
|
49521
49873
|
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
49522
49874
|
}
|
|
@@ -49634,6 +49986,8 @@ function buildToolExecuteBaseEnvelope(input2) {
|
|
|
49634
49986
|
] : [];
|
|
49635
49987
|
return {
|
|
49636
49988
|
...envelope,
|
|
49989
|
+
...input2.idempotencyKey ? { idempotency_key: input2.idempotencyKey } : {},
|
|
49990
|
+
...isRecord12(input2.rawResponse) && isRecord12(input2.rawResponse.executionRecovery) ? { execution_recovery: input2.rawResponse.executionRecovery } : {},
|
|
49637
49991
|
...envelopeHasCanonicalOutput || envelopeHasDeclaredOutput ? { output_preview: outputPreview } : { output: outputPreview },
|
|
49638
49992
|
...summaryEntries.length > 0 ? { summary: input2.summary } : {},
|
|
49639
49993
|
...warningMessages.length > 0 ? { warnings: warningMessages } : {},
|
|
@@ -49817,10 +50171,25 @@ async function executeTool(args) {
|
|
|
49817
50171
|
}
|
|
49818
50172
|
return 2;
|
|
49819
50173
|
}
|
|
50174
|
+
let idempotencyKey = parsed.idempotencyKey;
|
|
50175
|
+
let pendingRecoveryPath = null;
|
|
50176
|
+
const responseIntent = parsed.outPath || parsed.outputFormat === "csv" || parsed.outputFormat === "csv_file" ? "row_artifact" : "raw";
|
|
50177
|
+
if (parsed.recover && !idempotencyKey) {
|
|
50178
|
+
pendingRecoveryPath = pendingToolExecutionPath({
|
|
50179
|
+
toolId: parsed.toolId,
|
|
50180
|
+
params: parsed.params,
|
|
50181
|
+
responseIntent
|
|
50182
|
+
});
|
|
50183
|
+
idempotencyKey = loadOrCreatePendingToolExecution(pendingRecoveryPath);
|
|
50184
|
+
}
|
|
50185
|
+
if (idempotencyKey) {
|
|
50186
|
+
console.error(`Tool execution recovery key: ${idempotencyKey}`);
|
|
50187
|
+
}
|
|
49820
50188
|
const rawResponse = await client2.executeTool(parsed.toolId, parsed.params, {
|
|
49821
50189
|
...parsed.timeoutMs !== void 0 ? { timeout: parsed.timeoutMs } : {},
|
|
49822
|
-
responseIntent
|
|
49823
|
-
...
|
|
50190
|
+
responseIntent,
|
|
50191
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
50192
|
+
...parsed.recoveryTimeoutMs !== void 0 ? { recoveryTimeoutMs: parsed.recoveryTimeoutMs } : {}
|
|
49824
50193
|
});
|
|
49825
50194
|
const listConversion = tryConvertToList(rawResponse, {
|
|
49826
50195
|
listExtractorPaths: listExtractorPathsFromUsageGuidance(metadata)
|
|
@@ -49829,12 +50198,14 @@ async function executeTool(args) {
|
|
|
49829
50198
|
const baseEnvelope = buildToolExecuteBaseEnvelope({
|
|
49830
50199
|
toolId: parsed.toolId,
|
|
49831
50200
|
params: parsed.params,
|
|
50201
|
+
...idempotencyKey ? { idempotencyKey } : {},
|
|
49832
50202
|
rawResponse,
|
|
49833
50203
|
listConversion,
|
|
49834
50204
|
summary
|
|
49835
50205
|
});
|
|
49836
50206
|
if (!parsed.outPath && (parsed.outputFormat === "json" || parsed.outputFormat === "auto" && shouldEmitJson())) {
|
|
49837
50207
|
printCommandEnvelope(baseEnvelope, { json: true });
|
|
50208
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49838
50209
|
return 0;
|
|
49839
50210
|
}
|
|
49840
50211
|
if (parsed.outputFormat === "json_file") {
|
|
@@ -49852,6 +50223,7 @@ async function executeTool(args) {
|
|
|
49852
50223
|
},
|
|
49853
50224
|
{ json: true }
|
|
49854
50225
|
);
|
|
50226
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49855
50227
|
return 0;
|
|
49856
50228
|
}
|
|
49857
50229
|
if (!listConversion) {
|
|
@@ -49880,9 +50252,11 @@ async function executeTool(args) {
|
|
|
49880
50252
|
},
|
|
49881
50253
|
{ json: parsed.outputFormat === "csv_file" || shouldEmitJson() }
|
|
49882
50254
|
);
|
|
50255
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49883
50256
|
return 0;
|
|
49884
50257
|
}
|
|
49885
50258
|
printCommandEnvelope(baseEnvelope, { json: false });
|
|
50259
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49886
50260
|
return 0;
|
|
49887
50261
|
}
|
|
49888
50262
|
const rowOutput = projectRowOutput(listConversion);
|
|
@@ -49905,6 +50279,7 @@ async function executeTool(args) {
|
|
|
49905
50279
|
});
|
|
49906
50280
|
if (parsed.outputFormat === "csv_file") {
|
|
49907
50281
|
printCommandEnvelope(materializedEnvelope, { json: true });
|
|
50282
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49908
50283
|
return 0;
|
|
49909
50284
|
}
|
|
49910
50285
|
if (parsed.outPath) {
|
|
@@ -49913,6 +50288,7 @@ async function executeTool(args) {
|
|
|
49913
50288
|
text: `Wrote ${csv.rowCount} row(s) to ${csv.path}
|
|
49914
50289
|
`
|
|
49915
50290
|
});
|
|
50291
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49916
50292
|
return 0;
|
|
49917
50293
|
}
|
|
49918
50294
|
if (parsed.noPreview) {
|
|
@@ -49927,9 +50303,11 @@ async function executeTool(args) {
|
|
|
49927
50303
|
{ json: shouldEmitJson(), text: `${csv.path}
|
|
49928
50304
|
` }
|
|
49929
50305
|
);
|
|
50306
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49930
50307
|
return 0;
|
|
49931
50308
|
}
|
|
49932
50309
|
printCommandEnvelope(materializedEnvelope, { json: false });
|
|
50310
|
+
await completePendingToolExecution(pendingRecoveryPath);
|
|
49933
50311
|
return 0;
|
|
49934
50312
|
}
|
|
49935
50313
|
|
|
@@ -50012,10 +50390,10 @@ Examples:
|
|
|
50012
50390
|
|
|
50013
50391
|
// src/cli/commands/workflow.ts
|
|
50014
50392
|
import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
50015
|
-
import { dirname as
|
|
50393
|
+
import { dirname as dirname21, join as join26, resolve as resolve21 } from "path";
|
|
50016
50394
|
|
|
50017
50395
|
// src/cli/workflow-to-play.ts
|
|
50018
|
-
import { createHash as
|
|
50396
|
+
import { createHash as createHash7 } from "crypto";
|
|
50019
50397
|
var HITL_WAIT_FOR_SIGNAL_TOOL = "deepline_workflow_wait_for_signal";
|
|
50020
50398
|
var HITL_SLACK_TOOL = "slack_message_with_hitl";
|
|
50021
50399
|
var SUB_WORKFLOW_TOOL_PREFIX = "deepline_workflow_";
|
|
@@ -50121,7 +50499,7 @@ function sanitizePlayNameSegment(value) {
|
|
|
50121
50499
|
}
|
|
50122
50500
|
function deriveWorkflowPlayName(workflowName) {
|
|
50123
50501
|
const base = sanitizePlayNameSegment(workflowName) || "workflow";
|
|
50124
|
-
const suffix =
|
|
50502
|
+
const suffix = createHash7("sha256").update(workflowName).digest("hex").slice(0, 8);
|
|
50125
50503
|
const reserved = suffix.length + 1;
|
|
50126
50504
|
const allowedBase = Math.max(1, MAX_PLAY_NAME_LENGTH - reserved);
|
|
50127
50505
|
let name = `${base.slice(0, allowedBase)}_${suffix}`;
|
|
@@ -50264,7 +50642,7 @@ async function transformOne(api, workflowId, outDir, publish) {
|
|
|
50264
50642
|
{ workflowName: workflow.name, version: revision.version }
|
|
50265
50643
|
);
|
|
50266
50644
|
const file = join26(resolve21(outDir), `${compiled.playName}.play.ts`);
|
|
50267
|
-
await mkdir5(
|
|
50645
|
+
await mkdir5(dirname21(file), { recursive: true });
|
|
50268
50646
|
await writeFile5(file, compiled.sourceCode, "utf8");
|
|
50269
50647
|
let published = false;
|
|
50270
50648
|
if (publish) {
|
|
@@ -50526,6 +50904,7 @@ function registerDeeplineCommandGroups(program) {
|
|
|
50526
50904
|
registerAuthCommands(program);
|
|
50527
50905
|
registerProviderCommands(program);
|
|
50528
50906
|
registerToolsCommands(program);
|
|
50907
|
+
registerExecutionsCommands(program);
|
|
50529
50908
|
registerPlayCommands(program);
|
|
50530
50909
|
registerSessionsCommands(program);
|
|
50531
50910
|
registerWorkflowCommands(program);
|