deepline 0.1.220 → 0.1.221
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +62 -3
- package/dist/cli/index.js +226 -39
- package/dist/cli/index.mjs +226 -39
- package/dist/index.d.mts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/dist/viewer/viewer.css +11 -0
- package/dist/viewer/viewer.js +44 -2
- package/package.json +1 -1
|
@@ -108,10 +108,10 @@ export const SDK_RELEASE = {
|
|
|
108
108
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
109
109
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
110
110
|
// automatically without blocking their current command.
|
|
111
|
-
version: '0.1.
|
|
111
|
+
version: '0.1.221',
|
|
112
112
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
113
113
|
supportPolicy: {
|
|
114
|
-
latest: '0.1.
|
|
114
|
+
latest: '0.1.221',
|
|
115
115
|
minimumSupported: '0.1.53',
|
|
116
116
|
deprecatedBelow: '0.1.219',
|
|
117
117
|
commandMinimumSupported: [
|
|
@@ -997,6 +997,27 @@ export interface PlayCheckResult {
|
|
|
997
997
|
summary?: string;
|
|
998
998
|
artifactHash?: string | null;
|
|
999
999
|
graphHash?: string | null;
|
|
1000
|
+
/** SHA-256 of the exact source bytes checked by Deepline. */
|
|
1001
|
+
sourceHash?: string | null;
|
|
1002
|
+
/** Enforceable byte budgets measured during cloud preflight. */
|
|
1003
|
+
limits?: {
|
|
1004
|
+
revisionStorage: {
|
|
1005
|
+
usedBytes: number;
|
|
1006
|
+
limitBytes: number;
|
|
1007
|
+
withinLimit: boolean;
|
|
1008
|
+
breakdown: {
|
|
1009
|
+
sourceCodeBytes: number;
|
|
1010
|
+
staticPipelineBytes: number;
|
|
1011
|
+
bindingsBytes: number;
|
|
1012
|
+
descriptionBytes: number;
|
|
1013
|
+
};
|
|
1014
|
+
};
|
|
1015
|
+
bundle: {
|
|
1016
|
+
usedBytes: number;
|
|
1017
|
+
limitBytes: number;
|
|
1018
|
+
withinLimit: boolean;
|
|
1019
|
+
};
|
|
1020
|
+
};
|
|
1000
1021
|
}
|
|
1001
1022
|
|
|
1002
1023
|
/** Severity of a {@link PlayCheckIssue}. `error` fails the check; `warning` does not. */
|
|
@@ -8741,6 +8741,7 @@ export class PlayContextImpl {
|
|
|
8741
8741
|
const httpFailureAttempt = httpFailureAttempts.next({
|
|
8742
8742
|
toolId,
|
|
8743
8743
|
status: response.status,
|
|
8744
|
+
bodyText: text,
|
|
8744
8745
|
transientHttpRetrySafe: retrySafeTransientHttp,
|
|
8745
8746
|
});
|
|
8746
8747
|
const failure = classifyToolExecuteHttpFailure({
|
|
@@ -5,6 +5,14 @@ import {
|
|
|
5
5
|
} from './tool-http-errors';
|
|
6
6
|
|
|
7
7
|
export const TOOL_EXECUTE_TRANSIENT_HTTP_MAX_ATTEMPTS = 2;
|
|
8
|
+
// A provider may need to finish the original request before it can replay the
|
|
9
|
+
// result for this key. With no Retry-After signal, these two polls give the
|
|
10
|
+
// original request ten seconds to finish without repeatedly hammering it.
|
|
11
|
+
export const TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_RETRY_DELAYS_MS = [
|
|
12
|
+
5_000, 5_000,
|
|
13
|
+
] as const;
|
|
14
|
+
export const TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_MAX_ATTEMPTS =
|
|
15
|
+
TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_RETRY_DELAYS_MS.length + 1;
|
|
8
16
|
export const TOOL_EXECUTE_RATE_LIMIT_MAX_ATTEMPTS = 8;
|
|
9
17
|
export const TOOL_EXECUTE_BARE_RATE_LIMIT_MAX_ATTEMPTS = 2;
|
|
10
18
|
export const TOOL_EXECUTE_TRANSPORT_MAX_ATTEMPTS = 3;
|
|
@@ -13,6 +21,13 @@ export const TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS = 1_000;
|
|
|
13
21
|
export const TOOL_EXECUTE_RETRY_DELAY_MAX_MS = 5_000;
|
|
14
22
|
export const TOOL_EXECUTE_BARE_RATE_LIMIT_BACKPRESSURE_MS = 60_000;
|
|
15
23
|
export const TOOL_EXECUTE_AUTH_SCOPE_CHANGED_CODE = 'AUTH_SCOPE_CHANGED';
|
|
24
|
+
/**
|
|
25
|
+
* A provider/action-local outcome contract emits this only when a provider has
|
|
26
|
+
* explicitly said that the same idempotency key is still executing. It is not
|
|
27
|
+
* a generic HTTP 409 policy.
|
|
28
|
+
*/
|
|
29
|
+
export const TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_CODE =
|
|
30
|
+
'UPSTREAM_IDEMPOTENCY_IN_PROGRESS';
|
|
16
31
|
|
|
17
32
|
export class ToolExecuteAuthScopeChangedError extends Error {
|
|
18
33
|
readonly code = TOOL_EXECUTE_AUTH_SCOPE_CHANGED_CODE;
|
|
@@ -29,6 +44,7 @@ export type ToolExecuteHttpRetryDecision = {
|
|
|
29
44
|
attemptCap: number;
|
|
30
45
|
reason:
|
|
31
46
|
| 'rate_limit'
|
|
47
|
+
| 'idempotency_in_progress'
|
|
32
48
|
| 'retry_safe_transient_5xx'
|
|
33
49
|
| 'unsafe_transient_5xx'
|
|
34
50
|
| 'hard_billing_error'
|
|
@@ -49,6 +65,7 @@ export type ToolExecuteHttpFailureAttemptTracker = {
|
|
|
49
65
|
next(input: {
|
|
50
66
|
toolId: string;
|
|
51
67
|
status: number;
|
|
68
|
+
bodyText?: string;
|
|
52
69
|
transientHttpRetrySafe?: boolean;
|
|
53
70
|
}): number;
|
|
54
71
|
};
|
|
@@ -65,6 +82,29 @@ function parseJsonObject(text: string): Record<string, unknown> | null {
|
|
|
65
82
|
}
|
|
66
83
|
}
|
|
67
84
|
|
|
85
|
+
function isIdempotencyInProgressResponse(input: {
|
|
86
|
+
status: number;
|
|
87
|
+
bodyText: string;
|
|
88
|
+
}): boolean {
|
|
89
|
+
if (input.status !== 409) return false;
|
|
90
|
+
const body = parseJsonObject(input.bodyText);
|
|
91
|
+
return (
|
|
92
|
+
body?.code === TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_CODE &&
|
|
93
|
+
body.error_category === 'idempotency_in_progress'
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function idempotencyInProgressRetryDelayMs(attempt: number): number {
|
|
98
|
+
return (
|
|
99
|
+
TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_RETRY_DELAYS_MS[
|
|
100
|
+
Math.min(
|
|
101
|
+
Math.max(0, attempt - 1),
|
|
102
|
+
TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_RETRY_DELAYS_MS.length - 1,
|
|
103
|
+
)
|
|
104
|
+
] ?? TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
68
108
|
export function parseToolExecuteAuthScopeChangedError(input: {
|
|
69
109
|
status: number;
|
|
70
110
|
bodyText: string;
|
|
@@ -84,6 +124,7 @@ export function parseToolExecuteAuthScopeChangedError(input: {
|
|
|
84
124
|
|
|
85
125
|
function decideToolExecuteHttpRetry(input: {
|
|
86
126
|
status: number;
|
|
127
|
+
idempotencyInProgress?: boolean;
|
|
87
128
|
hardBillingFailure?: boolean;
|
|
88
129
|
hasRetryAfterHeader?: boolean;
|
|
89
130
|
transientHttpRetrySafe?: boolean;
|
|
@@ -110,6 +151,13 @@ function decideToolExecuteHttpRetry(input: {
|
|
|
110
151
|
reason: 'rate_limit',
|
|
111
152
|
};
|
|
112
153
|
}
|
|
154
|
+
if (input.idempotencyInProgress) {
|
|
155
|
+
return {
|
|
156
|
+
retryable: true,
|
|
157
|
+
attemptCap: TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_MAX_ATTEMPTS,
|
|
158
|
+
reason: 'idempotency_in_progress',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
113
161
|
if (input.status >= 500 && input.status < 600) {
|
|
114
162
|
if (!input.transientHttpRetrySafe) {
|
|
115
163
|
return {
|
|
@@ -137,6 +185,7 @@ export function createToolExecuteHttpFailureAttemptTracker(): ToolExecuteHttpFai
|
|
|
137
185
|
number
|
|
138
186
|
> = {
|
|
139
187
|
rate_limit: 0,
|
|
188
|
+
idempotency_in_progress: 0,
|
|
140
189
|
retry_safe_transient_5xx: 0,
|
|
141
190
|
unsafe_transient_5xx: 0,
|
|
142
191
|
hard_billing_error: 0,
|
|
@@ -147,6 +196,10 @@ export function createToolExecuteHttpFailureAttemptTracker(): ToolExecuteHttpFai
|
|
|
147
196
|
next(input) {
|
|
148
197
|
const decision = decideToolExecuteHttpRetry({
|
|
149
198
|
status: input.status,
|
|
199
|
+
idempotencyInProgress: isIdempotencyInProgressResponse({
|
|
200
|
+
status: input.status,
|
|
201
|
+
bodyText: input.bodyText ?? '',
|
|
202
|
+
}),
|
|
150
203
|
hasRetryAfterHeader: true,
|
|
151
204
|
transientHttpRetrySafe: input.transientHttpRetrySafe === true,
|
|
152
205
|
});
|
|
@@ -186,8 +239,10 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
186
239
|
const hasRetryAfterHeader =
|
|
187
240
|
typeof input.retryAfterHeader === 'string' &&
|
|
188
241
|
input.retryAfterHeader.trim().length > 0;
|
|
242
|
+
const idempotencyInProgress = isIdempotencyInProgressResponse(input);
|
|
189
243
|
const initialRetryDecision = decideToolExecuteHttpRetry({
|
|
190
244
|
status: input.status,
|
|
245
|
+
idempotencyInProgress,
|
|
191
246
|
hasRetryAfterHeader,
|
|
192
247
|
transientHttpRetrySafe,
|
|
193
248
|
});
|
|
@@ -208,6 +263,7 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
208
263
|
const hardBillingFailure = isHardBillingToolHttpError(error);
|
|
209
264
|
const retryDecision = decideToolExecuteHttpRetry({
|
|
210
265
|
status: input.status,
|
|
266
|
+
idempotencyInProgress,
|
|
211
267
|
hardBillingFailure,
|
|
212
268
|
hasRetryAfterHeader,
|
|
213
269
|
transientHttpRetrySafe,
|
|
@@ -238,9 +294,12 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
238
294
|
TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS * input.attempt,
|
|
239
295
|
),
|
|
240
296
|
)
|
|
241
|
-
:
|
|
242
|
-
|
|
243
|
-
|
|
297
|
+
: retryDecision.reason === 'idempotency_in_progress' &&
|
|
298
|
+
!hasRetryAfterHeader
|
|
299
|
+
? idempotencyInProgressRetryDelayMs(input.attempt)
|
|
300
|
+
: retryAfterMs > 0
|
|
301
|
+
? Math.min(TOOL_EXECUTE_RETRY_DELAY_MAX_MS, retryAfterMs)
|
|
302
|
+
: TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS;
|
|
244
303
|
return {
|
|
245
304
|
...retryDecision,
|
|
246
305
|
error,
|
package/dist/cli/index.js
CHANGED
|
@@ -625,10 +625,10 @@ var SDK_RELEASE = {
|
|
|
625
625
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
626
626
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
627
627
|
// automatically without blocking their current command.
|
|
628
|
-
version: "0.1.
|
|
628
|
+
version: "0.1.221",
|
|
629
629
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
630
630
|
supportPolicy: {
|
|
631
|
-
latest: "0.1.
|
|
631
|
+
latest: "0.1.221",
|
|
632
632
|
minimumSupported: "0.1.53",
|
|
633
633
|
deprecatedBelow: "0.1.219",
|
|
634
634
|
commandMinimumSupported: [
|
|
@@ -14648,6 +14648,73 @@ function parsePlayCheckOptions(args) {
|
|
|
14648
14648
|
const jsonOutput = argsWantJson(args);
|
|
14649
14649
|
return { target, jsonOutput };
|
|
14650
14650
|
}
|
|
14651
|
+
function parsePlayPublishOptions(args) {
|
|
14652
|
+
const target = args[0];
|
|
14653
|
+
if (!target) {
|
|
14654
|
+
throw new Error(
|
|
14655
|
+
"Usage: deepline plays publish <play-file.ts|play-name> [--expected-artifact <hash>] [--dry-run] [--latest|--revision-id <id>] [--json]"
|
|
14656
|
+
);
|
|
14657
|
+
}
|
|
14658
|
+
let revisionId;
|
|
14659
|
+
let useLatest = false;
|
|
14660
|
+
let expectedArtifactHash;
|
|
14661
|
+
let dryRun = false;
|
|
14662
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
14663
|
+
const arg = args[index];
|
|
14664
|
+
if (arg === "--revision-id") {
|
|
14665
|
+
revisionId = args[++index];
|
|
14666
|
+
if (!revisionId) throw new Error("--revision-id requires a revision id.");
|
|
14667
|
+
continue;
|
|
14668
|
+
}
|
|
14669
|
+
if (arg === "--latest") {
|
|
14670
|
+
useLatest = true;
|
|
14671
|
+
continue;
|
|
14672
|
+
}
|
|
14673
|
+
if (arg === "--expected-artifact") {
|
|
14674
|
+
expectedArtifactHash = args[++index];
|
|
14675
|
+
if (!expectedArtifactHash) {
|
|
14676
|
+
throw new Error("--expected-artifact requires an artifact hash.");
|
|
14677
|
+
}
|
|
14678
|
+
continue;
|
|
14679
|
+
}
|
|
14680
|
+
if (arg.startsWith("--expected-artifact=")) {
|
|
14681
|
+
expectedArtifactHash = arg.slice("--expected-artifact=".length);
|
|
14682
|
+
if (!expectedArtifactHash) {
|
|
14683
|
+
throw new Error("--expected-artifact requires an artifact hash.");
|
|
14684
|
+
}
|
|
14685
|
+
continue;
|
|
14686
|
+
}
|
|
14687
|
+
if (arg === "--dry-run") {
|
|
14688
|
+
dryRun = true;
|
|
14689
|
+
continue;
|
|
14690
|
+
}
|
|
14691
|
+
if (arg === "--json") continue;
|
|
14692
|
+
throw new Error(`Unknown plays publish option: ${arg}`);
|
|
14693
|
+
}
|
|
14694
|
+
if (revisionId && useLatest) {
|
|
14695
|
+
throw new Error("Choose only one live target: --latest or --revision-id.");
|
|
14696
|
+
}
|
|
14697
|
+
return {
|
|
14698
|
+
target,
|
|
14699
|
+
revisionId,
|
|
14700
|
+
useLatest,
|
|
14701
|
+
expectedArtifactHash,
|
|
14702
|
+
dryRun,
|
|
14703
|
+
jsonOutput: argsWantJson(args)
|
|
14704
|
+
};
|
|
14705
|
+
}
|
|
14706
|
+
function formatByteBudget(usedBytes, limitBytes) {
|
|
14707
|
+
return `${usedBytes.toLocaleString()} B / ${limitBytes.toLocaleString()} B`;
|
|
14708
|
+
}
|
|
14709
|
+
function printPlayCheckLimits(limits) {
|
|
14710
|
+
if (!limits) return;
|
|
14711
|
+
console.log(
|
|
14712
|
+
` revision storage: ${formatByteBudget(limits.revisionStorage.usedBytes, limits.revisionStorage.limitBytes)}`
|
|
14713
|
+
);
|
|
14714
|
+
console.log(
|
|
14715
|
+
` bundle: ${formatByteBudget(limits.bundle.usedBytes, limits.bundle.limitBytes)}`
|
|
14716
|
+
);
|
|
14717
|
+
}
|
|
14651
14718
|
function shouldUseLocalOnlyPlayCheck() {
|
|
14652
14719
|
const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
|
|
14653
14720
|
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
@@ -14960,7 +15027,11 @@ async function handlePlayCheck(args) {
|
|
|
14960
15027
|
);
|
|
14961
15028
|
} else {
|
|
14962
15029
|
console.log(`\u2713 ${playName} passed local play check`);
|
|
15030
|
+
console.log(` source: ${graph.root.artifact.sourceHash.slice(0, 12)}`);
|
|
14963
15031
|
console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
|
|
15032
|
+
console.log(
|
|
15033
|
+
` publish: deepline plays publish ${shellQuote2(options.target)} --expected-artifact ${result2.artifactHash}`
|
|
15034
|
+
);
|
|
14964
15035
|
printToolGetterHints(result2.toolGetterHints);
|
|
14965
15036
|
}
|
|
14966
15037
|
return 0;
|
|
@@ -15002,6 +15073,15 @@ async function handlePlayCheck(args) {
|
|
|
15002
15073
|
if (enrichedResult.artifactHash) {
|
|
15003
15074
|
console.log(` artifact: ${enrichedResult.artifactHash.slice(0, 12)}`);
|
|
15004
15075
|
}
|
|
15076
|
+
if (enrichedResult.sourceHash) {
|
|
15077
|
+
console.log(` source: ${enrichedResult.sourceHash.slice(0, 12)}`);
|
|
15078
|
+
}
|
|
15079
|
+
printPlayCheckLimits(enrichedResult.limits);
|
|
15080
|
+
if (enrichedResult.artifactHash) {
|
|
15081
|
+
console.log(
|
|
15082
|
+
` publish: deepline plays publish ${shellQuote2(options.target)} --expected-artifact ${enrichedResult.artifactHash}`
|
|
15083
|
+
);
|
|
15084
|
+
}
|
|
15005
15085
|
printPlayTriggers(enrichedResult.triggers);
|
|
15006
15086
|
printRecognizedSummary(enrichedResult.recognized);
|
|
15007
15087
|
printPlayCheckIssues(enrichedResult.issues, (line) => console.log(line));
|
|
@@ -16358,28 +16438,15 @@ async function handlePlayDescribe(args) {
|
|
|
16358
16438
|
return 0;
|
|
16359
16439
|
}
|
|
16360
16440
|
async function handlePlayPublish(args) {
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
|
|
16365
|
-
);
|
|
16366
|
-
return
|
|
16367
|
-
}
|
|
16368
|
-
let revisionId;
|
|
16369
|
-
let useLatest = false;
|
|
16370
|
-
for (let index = 1; index < args.length; index += 1) {
|
|
16371
|
-
const arg = args[index];
|
|
16372
|
-
if (arg === "--revision-id" && args[index + 1]) {
|
|
16373
|
-
revisionId = args[++index];
|
|
16374
|
-
}
|
|
16375
|
-
if (arg === "--latest") {
|
|
16376
|
-
useLatest = true;
|
|
16377
|
-
}
|
|
16378
|
-
}
|
|
16379
|
-
if (revisionId && useLatest) {
|
|
16380
|
-
console.error("Choose only one live target: --latest or --revision-id.");
|
|
16381
|
-
return 1;
|
|
16441
|
+
let options;
|
|
16442
|
+
try {
|
|
16443
|
+
options = parsePlayPublishOptions(args);
|
|
16444
|
+
} catch (error) {
|
|
16445
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
16446
|
+
return 2;
|
|
16382
16447
|
}
|
|
16448
|
+
const { target: playName, useLatest } = options;
|
|
16449
|
+
let revisionId = options.revisionId;
|
|
16383
16450
|
const client2 = new DeeplineClient();
|
|
16384
16451
|
if (isFileTarget(playName)) {
|
|
16385
16452
|
if (revisionId || useLatest) {
|
|
@@ -16396,6 +16463,66 @@ async function handlePlayPublish(args) {
|
|
|
16396
16463
|
() => collectBundledPlayGraph((0, import_node_path11.resolve)(playName))
|
|
16397
16464
|
);
|
|
16398
16465
|
assertBundledPlayGraphDescriptions(graph);
|
|
16466
|
+
} catch (error) {
|
|
16467
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
16468
|
+
return 1;
|
|
16469
|
+
}
|
|
16470
|
+
const rootPlayName = graph.root.playName ?? extractPlayName(graph.root.sourceCode, graph.root.filePath);
|
|
16471
|
+
const artifactHash = graph.root.artifact.artifactHash;
|
|
16472
|
+
if (options.expectedArtifactHash && options.expectedArtifactHash !== artifactHash) {
|
|
16473
|
+
const result3 = {
|
|
16474
|
+
ok: false,
|
|
16475
|
+
code: "PLAY_ARTIFACT_HASH_MISMATCH",
|
|
16476
|
+
message: "Refusing to publish: the local file no longer produces the artifact checked earlier.",
|
|
16477
|
+
expectedArtifactHash: options.expectedArtifactHash,
|
|
16478
|
+
currentArtifactHash: artifactHash,
|
|
16479
|
+
next: `deepline plays check ${shellQuote2(playName)}`
|
|
16480
|
+
};
|
|
16481
|
+
if (options.jsonOutput) {
|
|
16482
|
+
process.stdout.write(`${JSON.stringify(result3)}
|
|
16483
|
+
`);
|
|
16484
|
+
} else {
|
|
16485
|
+
console.error(result3.message);
|
|
16486
|
+
console.error(` checked artifact: ${result3.expectedArtifactHash}`);
|
|
16487
|
+
console.error(` current artifact: ${result3.currentArtifactHash}`);
|
|
16488
|
+
console.error(` run: ${result3.next}`);
|
|
16489
|
+
}
|
|
16490
|
+
return 7;
|
|
16491
|
+
}
|
|
16492
|
+
const dryRun = {
|
|
16493
|
+
ok: true,
|
|
16494
|
+
dryRun: true,
|
|
16495
|
+
name: rootPlayName,
|
|
16496
|
+
sourcePath: graph.root.filePath,
|
|
16497
|
+
sourceHash: graph.root.artifact.sourceHash,
|
|
16498
|
+
artifactHash,
|
|
16499
|
+
graphHash: graph.root.artifact.graphHash,
|
|
16500
|
+
importedPlayCount: Math.max(0, graph.nodes.size - 1),
|
|
16501
|
+
plannedMutation: "register revision and promote it live",
|
|
16502
|
+
expectedArtifactHash: options.expectedArtifactHash ?? null
|
|
16503
|
+
};
|
|
16504
|
+
if (options.dryRun) {
|
|
16505
|
+
if (options.jsonOutput) {
|
|
16506
|
+
process.stdout.write(`${JSON.stringify(dryRun)}
|
|
16507
|
+
`);
|
|
16508
|
+
} else {
|
|
16509
|
+
console.log(`Dry run: ${rootPlayName}`);
|
|
16510
|
+
console.log(` source: ${dryRun.sourceHash}`);
|
|
16511
|
+
console.log(` artifact: ${dryRun.artifactHash}`);
|
|
16512
|
+
console.log(` would: ${dryRun.plannedMutation}`);
|
|
16513
|
+
}
|
|
16514
|
+
return 0;
|
|
16515
|
+
}
|
|
16516
|
+
let previousLive = null;
|
|
16517
|
+
try {
|
|
16518
|
+
previousLive = (await client2.getPlay(rootPlayName)).play.liveRevision ?? null;
|
|
16519
|
+
} catch (error) {
|
|
16520
|
+
if (!(error instanceof DeeplineError) || error.statusCode !== 404) {
|
|
16521
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
16522
|
+
return 5;
|
|
16523
|
+
}
|
|
16524
|
+
}
|
|
16525
|
+
try {
|
|
16399
16526
|
await traceCliSpan(
|
|
16400
16527
|
"cli.play_publish_compile_manifests",
|
|
16401
16528
|
{ targetKind: "file", nodeCount: graph.nodes.size },
|
|
@@ -16410,13 +16537,12 @@ async function handlePlayPublish(args) {
|
|
|
16410
16537
|
console.error(error instanceof Error ? error.message : String(error));
|
|
16411
16538
|
return 1;
|
|
16412
16539
|
}
|
|
16413
|
-
const rootPlayName = graph.root.playName ?? extractPlayName(graph.root.sourceCode, graph.root.filePath);
|
|
16414
16540
|
const published = await traceCliSpan(
|
|
16415
16541
|
"cli.play_publish_register_root",
|
|
16416
16542
|
{
|
|
16417
16543
|
targetKind: "file",
|
|
16418
16544
|
playName: rootPlayName,
|
|
16419
|
-
artifactHash
|
|
16545
|
+
artifactHash
|
|
16420
16546
|
},
|
|
16421
16547
|
() => client2.registerPlayArtifact({
|
|
16422
16548
|
name: rootPlayName,
|
|
@@ -16428,17 +16554,63 @@ async function handlePlayPublish(args) {
|
|
|
16428
16554
|
publish: true
|
|
16429
16555
|
})
|
|
16430
16556
|
);
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16435
|
-
|
|
16436
|
-
|
|
16437
|
-
|
|
16438
|
-
|
|
16439
|
-
|
|
16440
|
-
|
|
16441
|
-
|
|
16557
|
+
const result2 = {
|
|
16558
|
+
success: true,
|
|
16559
|
+
name: rootPlayName,
|
|
16560
|
+
sourcePath: graph.root.filePath,
|
|
16561
|
+
sourceHash: graph.root.artifact.sourceHash,
|
|
16562
|
+
artifactHash,
|
|
16563
|
+
liveVersion: published.version ?? null,
|
|
16564
|
+
revisionId: published.revisionId ?? null,
|
|
16565
|
+
previousLiveVersion: previousLive?.version ?? null,
|
|
16566
|
+
previousArtifactHash: previousLive?.artifactHash ?? null,
|
|
16567
|
+
sourceBytes: {
|
|
16568
|
+
before: previousLive?.sourceCode ? Buffer.byteLength(previousLive.sourceCode, "utf8") : null,
|
|
16569
|
+
after: Buffer.byteLength(graph.root.sourceCode, "utf8")
|
|
16570
|
+
},
|
|
16571
|
+
triggerMetadata: published.triggerMetadata ?? null,
|
|
16572
|
+
triggerBindings: published.triggerBindings ?? []
|
|
16573
|
+
};
|
|
16574
|
+
if (options.jsonOutput) {
|
|
16575
|
+
process.stdout.write(`${JSON.stringify(result2)}
|
|
16576
|
+
`);
|
|
16577
|
+
} else {
|
|
16578
|
+
console.log(
|
|
16579
|
+
`\u2713 Published ${rootPlayName} as v${result2.liveVersion ?? "?"}`
|
|
16580
|
+
);
|
|
16581
|
+
console.log(` source: ${result2.sourceHash.slice(0, 12)}`);
|
|
16582
|
+
console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
|
|
16583
|
+
if (result2.previousLiveVersion !== null) {
|
|
16584
|
+
console.log(
|
|
16585
|
+
` previous live: v${result2.previousLiveVersion} (${result2.previousArtifactHash?.slice(0, 12) ?? "unknown artifact"})`
|
|
16586
|
+
);
|
|
16587
|
+
}
|
|
16588
|
+
if (result2.sourceBytes.before !== null) {
|
|
16589
|
+
console.log(
|
|
16590
|
+
` source bytes: ${result2.sourceBytes.before.toLocaleString()} \u2192 ${result2.sourceBytes.after.toLocaleString()}`
|
|
16591
|
+
);
|
|
16592
|
+
}
|
|
16593
|
+
console.log(
|
|
16594
|
+
` revisions: deepline plays versions --name ${rootPlayName}`
|
|
16595
|
+
);
|
|
16596
|
+
}
|
|
16597
|
+
return 0;
|
|
16598
|
+
}
|
|
16599
|
+
if (options.expectedArtifactHash) {
|
|
16600
|
+
console.error("--expected-artifact only applies to a local play file.");
|
|
16601
|
+
return 2;
|
|
16602
|
+
}
|
|
16603
|
+
if (options.dryRun) {
|
|
16604
|
+
const result2 = {
|
|
16605
|
+
ok: true,
|
|
16606
|
+
dryRun: true,
|
|
16607
|
+
name: playName,
|
|
16608
|
+
plannedMutation: "promote a saved revision live",
|
|
16609
|
+
revisionId: revisionId ?? null,
|
|
16610
|
+
latest: useLatest
|
|
16611
|
+
};
|
|
16612
|
+
process.stdout.write(`${JSON.stringify(result2)}
|
|
16613
|
+
`);
|
|
16442
16614
|
return 0;
|
|
16443
16615
|
}
|
|
16444
16616
|
const resolvedName = parseReferencedPlayTarget2(playName).playName;
|
|
@@ -16802,7 +16974,9 @@ Notes:
|
|
|
16802
16974
|
Mutates cloud state. For a saved play, --latest or --revision-id promotes an
|
|
16803
16975
|
existing saved revision live.
|
|
16804
16976
|
Local .play.ts publishing bundles the file, validates it, saves a revision,
|
|
16805
|
-
and promotes that revision live.
|
|
16977
|
+
and promotes that revision live. Use --expected-artifact with the hash from
|
|
16978
|
+
plays check to refuse publishing bytes that changed after validation.
|
|
16979
|
+
--dry-run bundles only and prints the exact artifact that would be published.
|
|
16806
16980
|
Running a local file with plays run does not publish it.
|
|
16807
16981
|
|
|
16808
16982
|
Examples:
|
|
@@ -16810,27 +16984,40 @@ Examples:
|
|
|
16810
16984
|
deepline plays set-live my-play --revision-id <revision-id> --json
|
|
16811
16985
|
deepline plays set-live my.play.ts --json
|
|
16812
16986
|
deepline plays publish my.play.ts --json
|
|
16987
|
+
deepline plays check my.play.ts
|
|
16988
|
+
deepline plays publish my.play.ts --expected-artifact <artifact-hash>
|
|
16989
|
+
deepline plays publish my.play.ts --dry-run --json
|
|
16813
16990
|
`
|
|
16814
16991
|
);
|
|
16815
16992
|
addPublishHelp(
|
|
16816
16993
|
play.command("publish <target>").description(
|
|
16817
16994
|
"Promote a saved play revision or publish a local play file."
|
|
16818
16995
|
)
|
|
16819
|
-
).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
|
|
16996
|
+
).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
|
|
16997
|
+
"--expected-artifact <hash>",
|
|
16998
|
+
"Require the local file to produce this checked artifact hash"
|
|
16999
|
+
).option("--dry-run", "Bundle and show the planned publish without mutation").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
|
|
16820
17000
|
process.exitCode = await handlePlayPublish([
|
|
16821
17001
|
target,
|
|
16822
17002
|
...options.latest ? ["--latest"] : [],
|
|
16823
17003
|
...options.revisionId ? ["--revision-id", options.revisionId] : [],
|
|
17004
|
+
...options.expectedArtifact ? ["--expected-artifact", options.expectedArtifact] : [],
|
|
17005
|
+
...options.dryRun ? ["--dry-run"] : [],
|
|
16824
17006
|
...options.json ? ["--json"] : []
|
|
16825
17007
|
]);
|
|
16826
17008
|
});
|
|
16827
17009
|
addPublishHelp(
|
|
16828
17010
|
play.command("set-live <target>").description("Promote a saved revision or publish a local play file.")
|
|
16829
|
-
).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
|
|
17011
|
+
).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
|
|
17012
|
+
"--expected-artifact <hash>",
|
|
17013
|
+
"Require the local file to produce this checked artifact hash"
|
|
17014
|
+
).option("--dry-run", "Bundle and show the planned publish without mutation").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
|
|
16830
17015
|
process.exitCode = await handlePlayPublish([
|
|
16831
17016
|
target,
|
|
16832
17017
|
...options.latest ? ["--latest"] : [],
|
|
16833
17018
|
...options.revisionId ? ["--revision-id", options.revisionId] : [],
|
|
17019
|
+
...options.expectedArtifact ? ["--expected-artifact", options.expectedArtifact] : [],
|
|
17020
|
+
...options.dryRun ? ["--dry-run"] : [],
|
|
16834
17021
|
...options.json ? ["--json"] : []
|
|
16835
17022
|
]);
|
|
16836
17023
|
});
|
package/dist/cli/index.mjs
CHANGED
|
@@ -610,10 +610,10 @@ var SDK_RELEASE = {
|
|
|
610
610
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
611
611
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
612
612
|
// automatically without blocking their current command.
|
|
613
|
-
version: "0.1.
|
|
613
|
+
version: "0.1.221",
|
|
614
614
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
615
615
|
supportPolicy: {
|
|
616
|
-
latest: "0.1.
|
|
616
|
+
latest: "0.1.221",
|
|
617
617
|
minimumSupported: "0.1.53",
|
|
618
618
|
deprecatedBelow: "0.1.219",
|
|
619
619
|
commandMinimumSupported: [
|
|
@@ -14677,6 +14677,73 @@ function parsePlayCheckOptions(args) {
|
|
|
14677
14677
|
const jsonOutput = argsWantJson(args);
|
|
14678
14678
|
return { target, jsonOutput };
|
|
14679
14679
|
}
|
|
14680
|
+
function parsePlayPublishOptions(args) {
|
|
14681
|
+
const target = args[0];
|
|
14682
|
+
if (!target) {
|
|
14683
|
+
throw new Error(
|
|
14684
|
+
"Usage: deepline plays publish <play-file.ts|play-name> [--expected-artifact <hash>] [--dry-run] [--latest|--revision-id <id>] [--json]"
|
|
14685
|
+
);
|
|
14686
|
+
}
|
|
14687
|
+
let revisionId;
|
|
14688
|
+
let useLatest = false;
|
|
14689
|
+
let expectedArtifactHash;
|
|
14690
|
+
let dryRun = false;
|
|
14691
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
14692
|
+
const arg = args[index];
|
|
14693
|
+
if (arg === "--revision-id") {
|
|
14694
|
+
revisionId = args[++index];
|
|
14695
|
+
if (!revisionId) throw new Error("--revision-id requires a revision id.");
|
|
14696
|
+
continue;
|
|
14697
|
+
}
|
|
14698
|
+
if (arg === "--latest") {
|
|
14699
|
+
useLatest = true;
|
|
14700
|
+
continue;
|
|
14701
|
+
}
|
|
14702
|
+
if (arg === "--expected-artifact") {
|
|
14703
|
+
expectedArtifactHash = args[++index];
|
|
14704
|
+
if (!expectedArtifactHash) {
|
|
14705
|
+
throw new Error("--expected-artifact requires an artifact hash.");
|
|
14706
|
+
}
|
|
14707
|
+
continue;
|
|
14708
|
+
}
|
|
14709
|
+
if (arg.startsWith("--expected-artifact=")) {
|
|
14710
|
+
expectedArtifactHash = arg.slice("--expected-artifact=".length);
|
|
14711
|
+
if (!expectedArtifactHash) {
|
|
14712
|
+
throw new Error("--expected-artifact requires an artifact hash.");
|
|
14713
|
+
}
|
|
14714
|
+
continue;
|
|
14715
|
+
}
|
|
14716
|
+
if (arg === "--dry-run") {
|
|
14717
|
+
dryRun = true;
|
|
14718
|
+
continue;
|
|
14719
|
+
}
|
|
14720
|
+
if (arg === "--json") continue;
|
|
14721
|
+
throw new Error(`Unknown plays publish option: ${arg}`);
|
|
14722
|
+
}
|
|
14723
|
+
if (revisionId && useLatest) {
|
|
14724
|
+
throw new Error("Choose only one live target: --latest or --revision-id.");
|
|
14725
|
+
}
|
|
14726
|
+
return {
|
|
14727
|
+
target,
|
|
14728
|
+
revisionId,
|
|
14729
|
+
useLatest,
|
|
14730
|
+
expectedArtifactHash,
|
|
14731
|
+
dryRun,
|
|
14732
|
+
jsonOutput: argsWantJson(args)
|
|
14733
|
+
};
|
|
14734
|
+
}
|
|
14735
|
+
function formatByteBudget(usedBytes, limitBytes) {
|
|
14736
|
+
return `${usedBytes.toLocaleString()} B / ${limitBytes.toLocaleString()} B`;
|
|
14737
|
+
}
|
|
14738
|
+
function printPlayCheckLimits(limits) {
|
|
14739
|
+
if (!limits) return;
|
|
14740
|
+
console.log(
|
|
14741
|
+
` revision storage: ${formatByteBudget(limits.revisionStorage.usedBytes, limits.revisionStorage.limitBytes)}`
|
|
14742
|
+
);
|
|
14743
|
+
console.log(
|
|
14744
|
+
` bundle: ${formatByteBudget(limits.bundle.usedBytes, limits.bundle.limitBytes)}`
|
|
14745
|
+
);
|
|
14746
|
+
}
|
|
14680
14747
|
function shouldUseLocalOnlyPlayCheck() {
|
|
14681
14748
|
const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
|
|
14682
14749
|
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
@@ -14989,7 +15056,11 @@ async function handlePlayCheck(args) {
|
|
|
14989
15056
|
);
|
|
14990
15057
|
} else {
|
|
14991
15058
|
console.log(`\u2713 ${playName} passed local play check`);
|
|
15059
|
+
console.log(` source: ${graph.root.artifact.sourceHash.slice(0, 12)}`);
|
|
14992
15060
|
console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
|
|
15061
|
+
console.log(
|
|
15062
|
+
` publish: deepline plays publish ${shellQuote2(options.target)} --expected-artifact ${result2.artifactHash}`
|
|
15063
|
+
);
|
|
14993
15064
|
printToolGetterHints(result2.toolGetterHints);
|
|
14994
15065
|
}
|
|
14995
15066
|
return 0;
|
|
@@ -15031,6 +15102,15 @@ async function handlePlayCheck(args) {
|
|
|
15031
15102
|
if (enrichedResult.artifactHash) {
|
|
15032
15103
|
console.log(` artifact: ${enrichedResult.artifactHash.slice(0, 12)}`);
|
|
15033
15104
|
}
|
|
15105
|
+
if (enrichedResult.sourceHash) {
|
|
15106
|
+
console.log(` source: ${enrichedResult.sourceHash.slice(0, 12)}`);
|
|
15107
|
+
}
|
|
15108
|
+
printPlayCheckLimits(enrichedResult.limits);
|
|
15109
|
+
if (enrichedResult.artifactHash) {
|
|
15110
|
+
console.log(
|
|
15111
|
+
` publish: deepline plays publish ${shellQuote2(options.target)} --expected-artifact ${enrichedResult.artifactHash}`
|
|
15112
|
+
);
|
|
15113
|
+
}
|
|
15034
15114
|
printPlayTriggers(enrichedResult.triggers);
|
|
15035
15115
|
printRecognizedSummary(enrichedResult.recognized);
|
|
15036
15116
|
printPlayCheckIssues(enrichedResult.issues, (line) => console.log(line));
|
|
@@ -16387,28 +16467,15 @@ async function handlePlayDescribe(args) {
|
|
|
16387
16467
|
return 0;
|
|
16388
16468
|
}
|
|
16389
16469
|
async function handlePlayPublish(args) {
|
|
16390
|
-
|
|
16391
|
-
|
|
16392
|
-
|
|
16393
|
-
|
|
16394
|
-
);
|
|
16395
|
-
return
|
|
16396
|
-
}
|
|
16397
|
-
let revisionId;
|
|
16398
|
-
let useLatest = false;
|
|
16399
|
-
for (let index = 1; index < args.length; index += 1) {
|
|
16400
|
-
const arg = args[index];
|
|
16401
|
-
if (arg === "--revision-id" && args[index + 1]) {
|
|
16402
|
-
revisionId = args[++index];
|
|
16403
|
-
}
|
|
16404
|
-
if (arg === "--latest") {
|
|
16405
|
-
useLatest = true;
|
|
16406
|
-
}
|
|
16407
|
-
}
|
|
16408
|
-
if (revisionId && useLatest) {
|
|
16409
|
-
console.error("Choose only one live target: --latest or --revision-id.");
|
|
16410
|
-
return 1;
|
|
16470
|
+
let options;
|
|
16471
|
+
try {
|
|
16472
|
+
options = parsePlayPublishOptions(args);
|
|
16473
|
+
} catch (error) {
|
|
16474
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
16475
|
+
return 2;
|
|
16411
16476
|
}
|
|
16477
|
+
const { target: playName, useLatest } = options;
|
|
16478
|
+
let revisionId = options.revisionId;
|
|
16412
16479
|
const client2 = new DeeplineClient();
|
|
16413
16480
|
if (isFileTarget(playName)) {
|
|
16414
16481
|
if (revisionId || useLatest) {
|
|
@@ -16425,6 +16492,66 @@ async function handlePlayPublish(args) {
|
|
|
16425
16492
|
() => collectBundledPlayGraph(resolve8(playName))
|
|
16426
16493
|
);
|
|
16427
16494
|
assertBundledPlayGraphDescriptions(graph);
|
|
16495
|
+
} catch (error) {
|
|
16496
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
16497
|
+
return 1;
|
|
16498
|
+
}
|
|
16499
|
+
const rootPlayName = graph.root.playName ?? extractPlayName(graph.root.sourceCode, graph.root.filePath);
|
|
16500
|
+
const artifactHash = graph.root.artifact.artifactHash;
|
|
16501
|
+
if (options.expectedArtifactHash && options.expectedArtifactHash !== artifactHash) {
|
|
16502
|
+
const result3 = {
|
|
16503
|
+
ok: false,
|
|
16504
|
+
code: "PLAY_ARTIFACT_HASH_MISMATCH",
|
|
16505
|
+
message: "Refusing to publish: the local file no longer produces the artifact checked earlier.",
|
|
16506
|
+
expectedArtifactHash: options.expectedArtifactHash,
|
|
16507
|
+
currentArtifactHash: artifactHash,
|
|
16508
|
+
next: `deepline plays check ${shellQuote2(playName)}`
|
|
16509
|
+
};
|
|
16510
|
+
if (options.jsonOutput) {
|
|
16511
|
+
process.stdout.write(`${JSON.stringify(result3)}
|
|
16512
|
+
`);
|
|
16513
|
+
} else {
|
|
16514
|
+
console.error(result3.message);
|
|
16515
|
+
console.error(` checked artifact: ${result3.expectedArtifactHash}`);
|
|
16516
|
+
console.error(` current artifact: ${result3.currentArtifactHash}`);
|
|
16517
|
+
console.error(` run: ${result3.next}`);
|
|
16518
|
+
}
|
|
16519
|
+
return 7;
|
|
16520
|
+
}
|
|
16521
|
+
const dryRun = {
|
|
16522
|
+
ok: true,
|
|
16523
|
+
dryRun: true,
|
|
16524
|
+
name: rootPlayName,
|
|
16525
|
+
sourcePath: graph.root.filePath,
|
|
16526
|
+
sourceHash: graph.root.artifact.sourceHash,
|
|
16527
|
+
artifactHash,
|
|
16528
|
+
graphHash: graph.root.artifact.graphHash,
|
|
16529
|
+
importedPlayCount: Math.max(0, graph.nodes.size - 1),
|
|
16530
|
+
plannedMutation: "register revision and promote it live",
|
|
16531
|
+
expectedArtifactHash: options.expectedArtifactHash ?? null
|
|
16532
|
+
};
|
|
16533
|
+
if (options.dryRun) {
|
|
16534
|
+
if (options.jsonOutput) {
|
|
16535
|
+
process.stdout.write(`${JSON.stringify(dryRun)}
|
|
16536
|
+
`);
|
|
16537
|
+
} else {
|
|
16538
|
+
console.log(`Dry run: ${rootPlayName}`);
|
|
16539
|
+
console.log(` source: ${dryRun.sourceHash}`);
|
|
16540
|
+
console.log(` artifact: ${dryRun.artifactHash}`);
|
|
16541
|
+
console.log(` would: ${dryRun.plannedMutation}`);
|
|
16542
|
+
}
|
|
16543
|
+
return 0;
|
|
16544
|
+
}
|
|
16545
|
+
let previousLive = null;
|
|
16546
|
+
try {
|
|
16547
|
+
previousLive = (await client2.getPlay(rootPlayName)).play.liveRevision ?? null;
|
|
16548
|
+
} catch (error) {
|
|
16549
|
+
if (!(error instanceof DeeplineError) || error.statusCode !== 404) {
|
|
16550
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
16551
|
+
return 5;
|
|
16552
|
+
}
|
|
16553
|
+
}
|
|
16554
|
+
try {
|
|
16428
16555
|
await traceCliSpan(
|
|
16429
16556
|
"cli.play_publish_compile_manifests",
|
|
16430
16557
|
{ targetKind: "file", nodeCount: graph.nodes.size },
|
|
@@ -16439,13 +16566,12 @@ async function handlePlayPublish(args) {
|
|
|
16439
16566
|
console.error(error instanceof Error ? error.message : String(error));
|
|
16440
16567
|
return 1;
|
|
16441
16568
|
}
|
|
16442
|
-
const rootPlayName = graph.root.playName ?? extractPlayName(graph.root.sourceCode, graph.root.filePath);
|
|
16443
16569
|
const published = await traceCliSpan(
|
|
16444
16570
|
"cli.play_publish_register_root",
|
|
16445
16571
|
{
|
|
16446
16572
|
targetKind: "file",
|
|
16447
16573
|
playName: rootPlayName,
|
|
16448
|
-
artifactHash
|
|
16574
|
+
artifactHash
|
|
16449
16575
|
},
|
|
16450
16576
|
() => client2.registerPlayArtifact({
|
|
16451
16577
|
name: rootPlayName,
|
|
@@ -16457,17 +16583,63 @@ async function handlePlayPublish(args) {
|
|
|
16457
16583
|
publish: true
|
|
16458
16584
|
})
|
|
16459
16585
|
);
|
|
16460
|
-
|
|
16461
|
-
|
|
16462
|
-
|
|
16463
|
-
|
|
16464
|
-
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16469
|
-
|
|
16470
|
-
|
|
16586
|
+
const result2 = {
|
|
16587
|
+
success: true,
|
|
16588
|
+
name: rootPlayName,
|
|
16589
|
+
sourcePath: graph.root.filePath,
|
|
16590
|
+
sourceHash: graph.root.artifact.sourceHash,
|
|
16591
|
+
artifactHash,
|
|
16592
|
+
liveVersion: published.version ?? null,
|
|
16593
|
+
revisionId: published.revisionId ?? null,
|
|
16594
|
+
previousLiveVersion: previousLive?.version ?? null,
|
|
16595
|
+
previousArtifactHash: previousLive?.artifactHash ?? null,
|
|
16596
|
+
sourceBytes: {
|
|
16597
|
+
before: previousLive?.sourceCode ? Buffer.byteLength(previousLive.sourceCode, "utf8") : null,
|
|
16598
|
+
after: Buffer.byteLength(graph.root.sourceCode, "utf8")
|
|
16599
|
+
},
|
|
16600
|
+
triggerMetadata: published.triggerMetadata ?? null,
|
|
16601
|
+
triggerBindings: published.triggerBindings ?? []
|
|
16602
|
+
};
|
|
16603
|
+
if (options.jsonOutput) {
|
|
16604
|
+
process.stdout.write(`${JSON.stringify(result2)}
|
|
16605
|
+
`);
|
|
16606
|
+
} else {
|
|
16607
|
+
console.log(
|
|
16608
|
+
`\u2713 Published ${rootPlayName} as v${result2.liveVersion ?? "?"}`
|
|
16609
|
+
);
|
|
16610
|
+
console.log(` source: ${result2.sourceHash.slice(0, 12)}`);
|
|
16611
|
+
console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
|
|
16612
|
+
if (result2.previousLiveVersion !== null) {
|
|
16613
|
+
console.log(
|
|
16614
|
+
` previous live: v${result2.previousLiveVersion} (${result2.previousArtifactHash?.slice(0, 12) ?? "unknown artifact"})`
|
|
16615
|
+
);
|
|
16616
|
+
}
|
|
16617
|
+
if (result2.sourceBytes.before !== null) {
|
|
16618
|
+
console.log(
|
|
16619
|
+
` source bytes: ${result2.sourceBytes.before.toLocaleString()} \u2192 ${result2.sourceBytes.after.toLocaleString()}`
|
|
16620
|
+
);
|
|
16621
|
+
}
|
|
16622
|
+
console.log(
|
|
16623
|
+
` revisions: deepline plays versions --name ${rootPlayName}`
|
|
16624
|
+
);
|
|
16625
|
+
}
|
|
16626
|
+
return 0;
|
|
16627
|
+
}
|
|
16628
|
+
if (options.expectedArtifactHash) {
|
|
16629
|
+
console.error("--expected-artifact only applies to a local play file.");
|
|
16630
|
+
return 2;
|
|
16631
|
+
}
|
|
16632
|
+
if (options.dryRun) {
|
|
16633
|
+
const result2 = {
|
|
16634
|
+
ok: true,
|
|
16635
|
+
dryRun: true,
|
|
16636
|
+
name: playName,
|
|
16637
|
+
plannedMutation: "promote a saved revision live",
|
|
16638
|
+
revisionId: revisionId ?? null,
|
|
16639
|
+
latest: useLatest
|
|
16640
|
+
};
|
|
16641
|
+
process.stdout.write(`${JSON.stringify(result2)}
|
|
16642
|
+
`);
|
|
16471
16643
|
return 0;
|
|
16472
16644
|
}
|
|
16473
16645
|
const resolvedName = parseReferencedPlayTarget2(playName).playName;
|
|
@@ -16831,7 +17003,9 @@ Notes:
|
|
|
16831
17003
|
Mutates cloud state. For a saved play, --latest or --revision-id promotes an
|
|
16832
17004
|
existing saved revision live.
|
|
16833
17005
|
Local .play.ts publishing bundles the file, validates it, saves a revision,
|
|
16834
|
-
and promotes that revision live.
|
|
17006
|
+
and promotes that revision live. Use --expected-artifact with the hash from
|
|
17007
|
+
plays check to refuse publishing bytes that changed after validation.
|
|
17008
|
+
--dry-run bundles only and prints the exact artifact that would be published.
|
|
16835
17009
|
Running a local file with plays run does not publish it.
|
|
16836
17010
|
|
|
16837
17011
|
Examples:
|
|
@@ -16839,27 +17013,40 @@ Examples:
|
|
|
16839
17013
|
deepline plays set-live my-play --revision-id <revision-id> --json
|
|
16840
17014
|
deepline plays set-live my.play.ts --json
|
|
16841
17015
|
deepline plays publish my.play.ts --json
|
|
17016
|
+
deepline plays check my.play.ts
|
|
17017
|
+
deepline plays publish my.play.ts --expected-artifact <artifact-hash>
|
|
17018
|
+
deepline plays publish my.play.ts --dry-run --json
|
|
16842
17019
|
`
|
|
16843
17020
|
);
|
|
16844
17021
|
addPublishHelp(
|
|
16845
17022
|
play.command("publish <target>").description(
|
|
16846
17023
|
"Promote a saved play revision or publish a local play file."
|
|
16847
17024
|
)
|
|
16848
|
-
).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
|
|
17025
|
+
).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
|
|
17026
|
+
"--expected-artifact <hash>",
|
|
17027
|
+
"Require the local file to produce this checked artifact hash"
|
|
17028
|
+
).option("--dry-run", "Bundle and show the planned publish without mutation").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
|
|
16849
17029
|
process.exitCode = await handlePlayPublish([
|
|
16850
17030
|
target,
|
|
16851
17031
|
...options.latest ? ["--latest"] : [],
|
|
16852
17032
|
...options.revisionId ? ["--revision-id", options.revisionId] : [],
|
|
17033
|
+
...options.expectedArtifact ? ["--expected-artifact", options.expectedArtifact] : [],
|
|
17034
|
+
...options.dryRun ? ["--dry-run"] : [],
|
|
16853
17035
|
...options.json ? ["--json"] : []
|
|
16854
17036
|
]);
|
|
16855
17037
|
});
|
|
16856
17038
|
addPublishHelp(
|
|
16857
17039
|
play.command("set-live <target>").description("Promote a saved revision or publish a local play file.")
|
|
16858
|
-
).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
|
|
17040
|
+
).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option(
|
|
17041
|
+
"--expected-artifact <hash>",
|
|
17042
|
+
"Require the local file to produce this checked artifact hash"
|
|
17043
|
+
).option("--dry-run", "Bundle and show the planned publish without mutation").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
|
|
16859
17044
|
process.exitCode = await handlePlayPublish([
|
|
16860
17045
|
target,
|
|
16861
17046
|
...options.latest ? ["--latest"] : [],
|
|
16862
17047
|
...options.revisionId ? ["--revision-id", options.revisionId] : [],
|
|
17048
|
+
...options.expectedArtifact ? ["--expected-artifact", options.expectedArtifact] : [],
|
|
17049
|
+
...options.dryRun ? ["--dry-run"] : [],
|
|
16863
17050
|
...options.json ? ["--json"] : []
|
|
16864
17051
|
]);
|
|
16865
17052
|
});
|
package/dist/index.d.mts
CHANGED
|
@@ -945,6 +945,27 @@ interface PlayCheckResult {
|
|
|
945
945
|
summary?: string;
|
|
946
946
|
artifactHash?: string | null;
|
|
947
947
|
graphHash?: string | null;
|
|
948
|
+
/** SHA-256 of the exact source bytes checked by Deepline. */
|
|
949
|
+
sourceHash?: string | null;
|
|
950
|
+
/** Enforceable byte budgets measured during cloud preflight. */
|
|
951
|
+
limits?: {
|
|
952
|
+
revisionStorage: {
|
|
953
|
+
usedBytes: number;
|
|
954
|
+
limitBytes: number;
|
|
955
|
+
withinLimit: boolean;
|
|
956
|
+
breakdown: {
|
|
957
|
+
sourceCodeBytes: number;
|
|
958
|
+
staticPipelineBytes: number;
|
|
959
|
+
bindingsBytes: number;
|
|
960
|
+
descriptionBytes: number;
|
|
961
|
+
};
|
|
962
|
+
};
|
|
963
|
+
bundle: {
|
|
964
|
+
usedBytes: number;
|
|
965
|
+
limitBytes: number;
|
|
966
|
+
withinLimit: boolean;
|
|
967
|
+
};
|
|
968
|
+
};
|
|
948
969
|
}
|
|
949
970
|
/** Severity of a {@link PlayCheckIssue}. `error` fails the check; `warning` does not. */
|
|
950
971
|
type PlayCheckIssueSeverity = 'error' | 'warning';
|
package/dist/index.d.ts
CHANGED
|
@@ -945,6 +945,27 @@ interface PlayCheckResult {
|
|
|
945
945
|
summary?: string;
|
|
946
946
|
artifactHash?: string | null;
|
|
947
947
|
graphHash?: string | null;
|
|
948
|
+
/** SHA-256 of the exact source bytes checked by Deepline. */
|
|
949
|
+
sourceHash?: string | null;
|
|
950
|
+
/** Enforceable byte budgets measured during cloud preflight. */
|
|
951
|
+
limits?: {
|
|
952
|
+
revisionStorage: {
|
|
953
|
+
usedBytes: number;
|
|
954
|
+
limitBytes: number;
|
|
955
|
+
withinLimit: boolean;
|
|
956
|
+
breakdown: {
|
|
957
|
+
sourceCodeBytes: number;
|
|
958
|
+
staticPipelineBytes: number;
|
|
959
|
+
bindingsBytes: number;
|
|
960
|
+
descriptionBytes: number;
|
|
961
|
+
};
|
|
962
|
+
};
|
|
963
|
+
bundle: {
|
|
964
|
+
usedBytes: number;
|
|
965
|
+
limitBytes: number;
|
|
966
|
+
withinLimit: boolean;
|
|
967
|
+
};
|
|
968
|
+
};
|
|
948
969
|
}
|
|
949
970
|
/** Severity of a {@link PlayCheckIssue}. `error` fails the check; `warning` does not. */
|
|
950
971
|
type PlayCheckIssueSeverity = 'error' | 'warning';
|
package/dist/index.js
CHANGED
|
@@ -424,10 +424,10 @@ var SDK_RELEASE = {
|
|
|
424
424
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
425
425
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
426
426
|
// automatically without blocking their current command.
|
|
427
|
-
version: "0.1.
|
|
427
|
+
version: "0.1.221",
|
|
428
428
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
429
429
|
supportPolicy: {
|
|
430
|
-
latest: "0.1.
|
|
430
|
+
latest: "0.1.221",
|
|
431
431
|
minimumSupported: "0.1.53",
|
|
432
432
|
deprecatedBelow: "0.1.219",
|
|
433
433
|
commandMinimumSupported: [
|
package/dist/index.mjs
CHANGED
|
@@ -354,10 +354,10 @@ var SDK_RELEASE = {
|
|
|
354
354
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
355
355
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
356
356
|
// automatically without blocking their current command.
|
|
357
|
-
version: "0.1.
|
|
357
|
+
version: "0.1.221",
|
|
358
358
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
359
359
|
supportPolicy: {
|
|
360
|
-
latest: "0.1.
|
|
360
|
+
latest: "0.1.221",
|
|
361
361
|
minimumSupported: "0.1.53",
|
|
362
362
|
deprecatedBelow: "0.1.219",
|
|
363
363
|
commandMinimumSupported: [
|
package/dist/viewer/viewer.css
CHANGED
|
@@ -353,6 +353,17 @@ body {
|
|
|
353
353
|
border: 1px solid var(--red);
|
|
354
354
|
background: rgba(248,81,73,0.06);
|
|
355
355
|
}
|
|
356
|
+
.tool-detail-toggle {
|
|
357
|
+
margin-top: 8px;
|
|
358
|
+
background: var(--bg-tertiary);
|
|
359
|
+
border: 1px solid var(--border);
|
|
360
|
+
border-radius: 4px;
|
|
361
|
+
color: var(--blue);
|
|
362
|
+
cursor: pointer;
|
|
363
|
+
font-size: 12px;
|
|
364
|
+
padding: 4px 8px;
|
|
365
|
+
}
|
|
366
|
+
.tool-detail-toggle:hover { border-color: var(--blue); }
|
|
356
367
|
|
|
357
368
|
/* Result card */
|
|
358
369
|
.result-card {
|
package/dist/viewer/viewer.js
CHANGED
|
@@ -1334,7 +1334,7 @@ function renderTimeline(timeline) {
|
|
|
1334
1334
|
<div class="tool-detail" id="detail-${i}">
|
|
1335
1335
|
<div class="tool-detail-section">
|
|
1336
1336
|
<div class="label">Input</div>
|
|
1337
|
-
|
|
1337
|
+
${formatToolInputHtml(e.tool, e.input, i)}
|
|
1338
1338
|
</div>
|
|
1339
1339
|
${e.result_content != null ? `<div class="tool-detail-section">
|
|
1340
1340
|
<div class="label">Result</div>
|
|
@@ -1347,8 +1347,17 @@ function renderTimeline(timeline) {
|
|
|
1347
1347
|
return html;
|
|
1348
1348
|
}
|
|
1349
1349
|
|
|
1350
|
+
function isShellCommandTool(tool) {
|
|
1351
|
+
const normalized = String(tool || '').toLowerCase().replace(/[^a-z]/g, '');
|
|
1352
|
+
return ['bash', 'exec', 'execcommand', 'runcommand', 'shell'].includes(normalized);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
function shellCommand(input) {
|
|
1356
|
+
return input.command || input.cmd || input.CommandLine || null;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1350
1359
|
function formatInput(tool, input) {
|
|
1351
|
-
if (tool
|
|
1360
|
+
if (isShellCommandTool(tool)) return shellCommand(input) || JSON.stringify(input, null, 2);
|
|
1352
1361
|
if (tool === 'Write') {
|
|
1353
1362
|
let s = 'file: ' + (input.file_path || input.path || '') + '\n\n';
|
|
1354
1363
|
if (input.content != null) s += '--- content ---\n' + String(input.content);
|
|
@@ -1364,6 +1373,39 @@ function formatInput(tool, input) {
|
|
|
1364
1373
|
return JSON.stringify(input, null, 2);
|
|
1365
1374
|
}
|
|
1366
1375
|
|
|
1376
|
+
function isLongShellCommand(command) {
|
|
1377
|
+
return command.length > 600 || command.split('\n').length > 10;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
function shellCommandPreview(command) {
|
|
1381
|
+
const lines = command.split('\n');
|
|
1382
|
+
let preview = lines.slice(0, 10).join('\n');
|
|
1383
|
+
if (preview.length > 600) preview = preview.slice(0, 600);
|
|
1384
|
+
return preview + '\n...[full command hidden]';
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
function formatToolInputHtml(tool, input, entryIndex) {
|
|
1388
|
+
const formatted = formatInput(tool, input);
|
|
1389
|
+
if (!isShellCommandTool(tool) || !isLongShellCommand(formatted)) {
|
|
1390
|
+
return `<pre>${esc(formatted)}</pre>`;
|
|
1391
|
+
}
|
|
1392
|
+
const previewId = `command-preview-${entryIndex}`;
|
|
1393
|
+
const fullId = `command-full-${entryIndex}`;
|
|
1394
|
+
return `<pre id="${previewId}">${esc(shellCommandPreview(formatted))}</pre>
|
|
1395
|
+
<button class="tool-detail-toggle" onclick="toggleCommandInput('${previewId}', '${fullId}', this)">Show full command</button>
|
|
1396
|
+
<pre id="${fullId}" hidden>${esc(formatted)}</pre>`;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
function toggleCommandInput(previewId, fullId, toggle) {
|
|
1400
|
+
const preview = document.getElementById(previewId);
|
|
1401
|
+
const full = document.getElementById(fullId);
|
|
1402
|
+
if (!preview || !full) return;
|
|
1403
|
+
const showFull = full.hidden;
|
|
1404
|
+
full.hidden = !showFull;
|
|
1405
|
+
preview.hidden = showFull;
|
|
1406
|
+
toggle.textContent = showFull ? 'Show less' : 'Show full command';
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1367
1409
|
function toggleReasoning(id, toggle) {
|
|
1368
1410
|
const el = document.getElementById(id);
|
|
1369
1411
|
const collapsed = el.classList.toggle('collapsed');
|