@promptdriven/pds 0.1.2 → 0.1.4
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/README.md +150 -0
- package/dist/args/parser.js +14 -0
- package/dist/args/parser.js.map +1 -1
- package/dist/client/api-client.d.ts +20 -4
- package/dist/client/api-client.js +98 -6
- package/dist/client/api-client.js.map +1 -1
- package/dist/client/types.d.ts +112 -3
- package/dist/commands/registry.d.ts +1 -0
- package/dist/commands/registry.js +1181 -36
- package/dist/commands/registry.js.map +1 -1
- package/dist/main.js +62 -1
- package/dist/main.js.map +1 -1
- package/dist/output/output.js +62 -0
- package/dist/output/output.js.map +1 -1
- package/dist/utils/repo-url.d.ts +1 -0
- package/dist/utils/repo-url.js +36 -0
- package/dist/utils/repo-url.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
9
9
|
const node_util_1 = require("node:util");
|
|
10
10
|
const config_1 = require("../config/config");
|
|
11
11
|
const errors_1 = require("../errors/errors");
|
|
12
|
+
const repo_url_1 = require("../utils/repo-url");
|
|
12
13
|
const DEFAULT_LOGIN_SCOPES = [
|
|
13
14
|
"project:create",
|
|
14
15
|
"project:read",
|
|
@@ -24,7 +25,26 @@ const RELEASE_VIDEO_REATTACH_INITIAL_DELAY_MS = 250;
|
|
|
24
25
|
const RELEASE_VIDEO_REATTACH_MAX_DELAY_MS = 5_000;
|
|
25
26
|
const RELEASE_VIDEO_WAIT_MAX_CONFIRMATIONS = 60;
|
|
26
27
|
const RELEASE_VIDEO_WAIT_POLL_MS = 1_000;
|
|
28
|
+
const PIPELINE_REATTACH_MAX_ATTEMPTS = 8;
|
|
29
|
+
const PIPELINE_REATTACH_INITIAL_DELAY_MS = 250;
|
|
30
|
+
const PIPELINE_REATTACH_MAX_DELAY_MS = 5_000;
|
|
31
|
+
const PIPELINE_WAIT_MAX_CONFIRMATIONS = 60;
|
|
32
|
+
const PIPELINE_WAIT_POLL_MS = 1_000;
|
|
33
|
+
const JOBS_WATCH_MAX_CONFIRMATIONS = 60;
|
|
34
|
+
const JOBS_WATCH_POLL_MS = 1_000;
|
|
35
|
+
const DISTRIBUTION_PUBLISH_REATTACH_MAX_ATTEMPTS = 8;
|
|
36
|
+
const DISTRIBUTION_PUBLISH_REATTACH_INITIAL_DELAY_MS = 250;
|
|
37
|
+
const DISTRIBUTION_PUBLISH_REATTACH_MAX_DELAY_MS = 5_000;
|
|
38
|
+
const DISTRIBUTION_PUBLISH_WAIT_MAX_CONFIRMATIONS = 60;
|
|
39
|
+
const DISTRIBUTION_PUBLISH_WAIT_POLL_MS = 1_000;
|
|
27
40
|
const PRIVATE_INTERNAL_ERROR_DETAIL_KEY_PATTERN = /^(?:errorName|rootCause|raw(?:Provider)?(?:Payload|Response|Error|Message)?|providerRaw(?:Payload|Response|Error|Message)?)$/i;
|
|
41
|
+
const REFERENCE_LIBRARY_SCOPES = ["user", "workspace", "organization"];
|
|
42
|
+
const REFERENCE_LIBRARY_UPDATE_POLICIES = ["notify", "pinned"];
|
|
43
|
+
const REFERENCE_LIBRARY_CAPTURE_POLICIES = ["off", "private-draft"];
|
|
44
|
+
const REFERENCE_LIBRARY_GRANT_PRESETS = {
|
|
45
|
+
"can-use": ["library:view", "library:use"],
|
|
46
|
+
admin: ["library:view", "library:use", "library:admin"]
|
|
47
|
+
};
|
|
28
48
|
async function dispatchCommand(commandPath, commandArgs, context) {
|
|
29
49
|
const command = commandPath.join(" ");
|
|
30
50
|
switch (command) {
|
|
@@ -75,28 +95,40 @@ async function dispatchCommand(commandPath, commandArgs, context) {
|
|
|
75
95
|
"dry-run": "boolean",
|
|
76
96
|
"force-regenerate": "boolean",
|
|
77
97
|
wait: "boolean",
|
|
78
|
-
"idempotency-key": "string"
|
|
98
|
+
"idempotency-key": "string",
|
|
99
|
+
sections: "string[]",
|
|
100
|
+
files: "string[]",
|
|
101
|
+
segments: "string[]",
|
|
102
|
+
clips: "string[]"
|
|
79
103
|
});
|
|
80
104
|
const dryRun = values["dry-run"] === true;
|
|
81
|
-
const
|
|
105
|
+
const wait = !dryRun;
|
|
106
|
+
const requestPayload = compact({
|
|
82
107
|
projectId: requireProject(context.config),
|
|
83
108
|
to: optionalString(values.to),
|
|
84
109
|
stage: optionalString(values.stage),
|
|
85
110
|
mode: optionalString(values.mode),
|
|
111
|
+
sections: optionalStringList(values.sections),
|
|
112
|
+
files: optionalStringList(values.files),
|
|
113
|
+
segments: optionalStringList(values.segments),
|
|
114
|
+
clips: optionalStringList(values.clips),
|
|
86
115
|
dryRun: dryRun || undefined,
|
|
87
116
|
forceRegenerate: values["force-regenerate"] === true || undefined,
|
|
88
117
|
wait: values.wait === true || undefined,
|
|
89
118
|
idempotencyKey: mutationIdempotencyKey(values["idempotency-key"], dryRun)
|
|
90
|
-
})
|
|
119
|
+
});
|
|
120
|
+
const response = wait
|
|
121
|
+
? await createPipelineRunWithReattach(requestPayload, context)
|
|
122
|
+
: await context.client.runPipeline(requestPayload);
|
|
123
|
+
if (wait) {
|
|
124
|
+
return await waitForPipelineRun(response, context);
|
|
125
|
+
}
|
|
91
126
|
await followAgentRunEvents(response, context);
|
|
92
127
|
return response;
|
|
93
128
|
}
|
|
94
129
|
case "jobs watch": {
|
|
95
130
|
const values = parse(commandArgs, { "job-id": "string", "run-id": "string" });
|
|
96
|
-
return await context
|
|
97
|
-
jobId: requiredRunOrJobId(values),
|
|
98
|
-
onEvent: context.emitJsonlEvent
|
|
99
|
-
});
|
|
131
|
+
return await watchJobUntilTerminal(requiredRunOrJobId(values), context);
|
|
100
132
|
}
|
|
101
133
|
case "jobs cancel": {
|
|
102
134
|
const values = parse(commandArgs, { "job-id": "string" });
|
|
@@ -121,6 +153,8 @@ async function dispatchCommand(commandPath, commandArgs, context) {
|
|
|
121
153
|
platform: optionalString(values.platform)
|
|
122
154
|
}));
|
|
123
155
|
}
|
|
156
|
+
case "distribution thumbnails upload":
|
|
157
|
+
return await distributionThumbnailsUpload(commandArgs, context);
|
|
124
158
|
case "distribution connections list": {
|
|
125
159
|
const values = parse(commandArgs, { platform: "string" });
|
|
126
160
|
return await context.client.listDistributionConnections(compact({
|
|
@@ -149,7 +183,8 @@ async function dispatchCommand(commandPath, commandArgs, context) {
|
|
|
149
183
|
"idempotency-key": "string"
|
|
150
184
|
});
|
|
151
185
|
const dryRun = values["dry-run"] === true;
|
|
152
|
-
const
|
|
186
|
+
const wait = !dryRun;
|
|
187
|
+
const requestPayload = compact({
|
|
153
188
|
projectId: requireProject(context.config),
|
|
154
189
|
platform: optionalString(values.platform),
|
|
155
190
|
privacy: optionalString(values.privacy),
|
|
@@ -160,12 +195,75 @@ async function dispatchCommand(commandPath, commandArgs, context) {
|
|
|
160
195
|
auditOverrideRationale: optionalString(values["audit-override-rationale"]),
|
|
161
196
|
wait: values.wait === true || undefined,
|
|
162
197
|
idempotencyKey: mutationIdempotencyKey(values["idempotency-key"], dryRun)
|
|
163
|
-
})
|
|
198
|
+
});
|
|
199
|
+
const response = wait
|
|
200
|
+
? await publishDistributionWithReattach(requestPayload, context)
|
|
201
|
+
: await context.client.publishDistribution(requestPayload);
|
|
202
|
+
if (wait) {
|
|
203
|
+
return await waitForDistributionPublishRun(response, context);
|
|
204
|
+
}
|
|
164
205
|
await followAgentRunEvents(response, context);
|
|
165
206
|
return response;
|
|
166
207
|
}
|
|
208
|
+
case "reference-library list": {
|
|
209
|
+
const values = parse(commandArgs, { scope: "string" });
|
|
210
|
+
return await context.client.listReferenceLibraries(compact({
|
|
211
|
+
projectId: context.config.projectId,
|
|
212
|
+
scope: referenceLibraryScope(values.scope)
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
case "reference-library create": {
|
|
216
|
+
const values = parse(commandArgs, {
|
|
217
|
+
label: "string",
|
|
218
|
+
slug: "string",
|
|
219
|
+
"library-id": "string",
|
|
220
|
+
"idempotency-key": "string"
|
|
221
|
+
});
|
|
222
|
+
return await context.client.createReferenceLibrary(compact({
|
|
223
|
+
projectId: requireProject(context.config),
|
|
224
|
+
label: requiredString(values.label, "--label"),
|
|
225
|
+
slug: optionalString(values.slug),
|
|
226
|
+
libraryId: optionalString(values["library-id"]),
|
|
227
|
+
idempotencyKey: requiredString(values["idempotency-key"], "--idempotency-key")
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
case "reference-library items list": {
|
|
231
|
+
const values = parse(commandArgs, { "library-id": "string" });
|
|
232
|
+
return await context.client.listReferenceLibraryItems(compact({
|
|
233
|
+
projectId: context.config.projectId,
|
|
234
|
+
libraryId: requiredString(values["library-id"], "--library-id")
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
case "reference-library bindings list": {
|
|
238
|
+
ensureNoCommandArgs(commandArgs);
|
|
239
|
+
return await context.client.listReferenceLibraryProjectBindings({
|
|
240
|
+
projectId: requireProject(context.config)
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
case "reference-library policy get": {
|
|
244
|
+
ensureNoCommandArgs(commandArgs);
|
|
245
|
+
return await context.client.getReferenceLibraryProjectPolicy({
|
|
246
|
+
projectId: requireProject(context.config)
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
case "reference-library policy set":
|
|
250
|
+
return await referenceLibraryPolicySet(commandArgs, context);
|
|
251
|
+
case "reference-library bindings attach":
|
|
252
|
+
return await referenceLibraryBindingsAttach(commandArgs, context);
|
|
253
|
+
case "reference-library bindings update":
|
|
254
|
+
return await referenceLibraryBindingsUpdate(commandArgs, context);
|
|
255
|
+
case "reference-library bindings detach":
|
|
256
|
+
return await referenceLibraryBindingsDetach(commandArgs, context);
|
|
257
|
+
case "reference-library grants list":
|
|
258
|
+
return await referenceLibraryGrantsList(commandArgs, context);
|
|
259
|
+
case "reference-library grants add":
|
|
260
|
+
return await referenceLibraryGrantsAdd(commandArgs, context);
|
|
261
|
+
case "reference-library grants revoke":
|
|
262
|
+
return await referenceLibraryGrantsRevoke(commandArgs, context);
|
|
167
263
|
case "release-video create":
|
|
168
264
|
return await releaseVideoCreate(commandArgs, context);
|
|
265
|
+
case "release-video preflight":
|
|
266
|
+
return await releaseVideoPreflight(commandArgs, context);
|
|
169
267
|
case "release-video status":
|
|
170
268
|
return await releaseVideoStatus(commandArgs, context);
|
|
171
269
|
default:
|
|
@@ -397,6 +495,366 @@ async function projectResetPipeline(commandArgs, context) {
|
|
|
397
495
|
idempotencyKey: requiredString(values["idempotency-key"], "--idempotency-key"),
|
|
398
496
|
}));
|
|
399
497
|
}
|
|
498
|
+
async function distributionThumbnailsUpload(commandArgs, context) {
|
|
499
|
+
const values = parse(commandArgs, {
|
|
500
|
+
platform: "string",
|
|
501
|
+
file: "string",
|
|
502
|
+
"target-candidate": "string",
|
|
503
|
+
"target-candidate-id": "string",
|
|
504
|
+
"confirm-rights": "boolean",
|
|
505
|
+
"rights-confirmed": "boolean",
|
|
506
|
+
"replace-live": "boolean",
|
|
507
|
+
"idempotency-key": "string"
|
|
508
|
+
});
|
|
509
|
+
const idempotencyKey = requiredString(values["idempotency-key"], "--idempotency-key");
|
|
510
|
+
if (values["confirm-rights"] !== true && values["rights-confirmed"] !== true) {
|
|
511
|
+
throw new errors_1.CliError("Missing required option --confirm-rights", {
|
|
512
|
+
code: "missing_required_option",
|
|
513
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
const sourcePath = requiredString(values.file, "--file");
|
|
517
|
+
const targetCandidateId = targetCandidateOption(values);
|
|
518
|
+
const fileRequest = await thumbnailFileRequest(sourcePath);
|
|
519
|
+
return await context.client.uploadDistributionThumbnail(compact({
|
|
520
|
+
projectId: requireProject(context.config),
|
|
521
|
+
platform: optionalString(values.platform),
|
|
522
|
+
targetCandidateId,
|
|
523
|
+
rightsConfirmed: true,
|
|
524
|
+
replaceLive: values["replace-live"] === true || undefined,
|
|
525
|
+
file: fileRequest.file,
|
|
526
|
+
fileName: fileRequest.fileName,
|
|
527
|
+
idempotencyKey
|
|
528
|
+
}));
|
|
529
|
+
}
|
|
530
|
+
function targetCandidateOption(values) {
|
|
531
|
+
const shortFlag = optionalString(values["target-candidate"]);
|
|
532
|
+
const longFlag = optionalString(values["target-candidate-id"]);
|
|
533
|
+
if (shortFlag && longFlag && shortFlag !== longFlag) {
|
|
534
|
+
throw new errors_1.CliError("Use only one of --target-candidate or --target-candidate-id", {
|
|
535
|
+
code: "invalid_arguments",
|
|
536
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
return shortFlag ?? longFlag;
|
|
540
|
+
}
|
|
541
|
+
async function thumbnailFileRequest(sourcePath) {
|
|
542
|
+
try {
|
|
543
|
+
const fileBytes = new Uint8Array(await (0, promises_1.readFile)(sourcePath));
|
|
544
|
+
return {
|
|
545
|
+
file: new Blob([fileBytes]),
|
|
546
|
+
fileName: node_path_1.default.basename(sourcePath)
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
if (isNodeFileNotFound(error)) {
|
|
551
|
+
throw new errors_1.CliError(`Thumbnail file not found: ${sourcePath}`, {
|
|
552
|
+
code: "file_not_found",
|
|
553
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
throw error;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function isNodeFileNotFound(error) {
|
|
560
|
+
return Boolean(error) &&
|
|
561
|
+
typeof error === "object" &&
|
|
562
|
+
error.code === "ENOENT";
|
|
563
|
+
}
|
|
564
|
+
async function referenceLibraryPolicySet(commandArgs, context) {
|
|
565
|
+
const values = parse(commandArgs, {
|
|
566
|
+
"capture-policy": "string",
|
|
567
|
+
"default-capture-library-id": "string",
|
|
568
|
+
"auto-select-from-library": "boolean",
|
|
569
|
+
"no-auto-select-from-library": "boolean",
|
|
570
|
+
"auto-attach-confidence": "string",
|
|
571
|
+
"suggest-confidence": "string",
|
|
572
|
+
"idempotency-key": "string"
|
|
573
|
+
});
|
|
574
|
+
const capturePolicy = referenceLibraryCapturePolicy(values["capture-policy"]);
|
|
575
|
+
const defaultCaptureLibraryId = optionalString(values["default-capture-library-id"]);
|
|
576
|
+
if (capturePolicy === "private-draft" && !defaultCaptureLibraryId) {
|
|
577
|
+
throw new errors_1.CliError("Missing required option --default-capture-library-id when --capture-policy private-draft", { code: "missing_required_option", exitCode: errors_1.EXIT_CODES.usage });
|
|
578
|
+
}
|
|
579
|
+
return await context.client.setReferenceLibraryProjectPolicy(compact({
|
|
580
|
+
projectId: requireProject(context.config),
|
|
581
|
+
capturePolicy,
|
|
582
|
+
defaultCaptureLibraryId,
|
|
583
|
+
autoSelectFromLibrary: referenceLibraryAutoSelectFromFlags(values),
|
|
584
|
+
autoAttachConfidence: optionalReferenceLibraryConfidence(values["auto-attach-confidence"], "--auto-attach-confidence"),
|
|
585
|
+
suggestConfidence: optionalReferenceLibraryConfidence(values["suggest-confidence"], "--suggest-confidence"),
|
|
586
|
+
idempotencyKey: requiredString(values["idempotency-key"], "--idempotency-key")
|
|
587
|
+
}));
|
|
588
|
+
}
|
|
589
|
+
async function referenceLibraryGrantsList(commandArgs, context) {
|
|
590
|
+
const values = parse(commandArgs, { "library-id": "string" });
|
|
591
|
+
return await context.client.listReferenceLibraryGrants({
|
|
592
|
+
projectId: requireProject(context.config),
|
|
593
|
+
libraryId: requiredString(values["library-id"], "--library-id")
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
async function referenceLibraryGrantsAdd(commandArgs, context) {
|
|
597
|
+
const values = parse(commandArgs, {
|
|
598
|
+
"library-id": "string",
|
|
599
|
+
"user-id": "string",
|
|
600
|
+
permission: "string",
|
|
601
|
+
"expires-at": "string",
|
|
602
|
+
"idempotency-key": "string"
|
|
603
|
+
});
|
|
604
|
+
return await context.client.addReferenceLibraryGrant(compact({
|
|
605
|
+
projectId: requireProject(context.config),
|
|
606
|
+
libraryId: requiredString(values["library-id"], "--library-id"),
|
|
607
|
+
userId: requiredString(values["user-id"], "--user-id"),
|
|
608
|
+
permissions: referenceLibraryGrantPermissions(values.permission),
|
|
609
|
+
expiresAt: optionalString(values["expires-at"]),
|
|
610
|
+
idempotencyKey: requiredString(values["idempotency-key"], "--idempotency-key")
|
|
611
|
+
}));
|
|
612
|
+
}
|
|
613
|
+
async function referenceLibraryGrantsRevoke(commandArgs, context) {
|
|
614
|
+
const values = parse(commandArgs, {
|
|
615
|
+
"library-id": "string",
|
|
616
|
+
"grant-id": "string",
|
|
617
|
+
"idempotency-key": "string"
|
|
618
|
+
});
|
|
619
|
+
return await context.client.revokeReferenceLibraryGrant({
|
|
620
|
+
projectId: requireProject(context.config),
|
|
621
|
+
libraryId: requiredString(values["library-id"], "--library-id"),
|
|
622
|
+
grantId: requiredString(values["grant-id"], "--grant-id"),
|
|
623
|
+
idempotencyKey: requiredString(values["idempotency-key"], "--idempotency-key")
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
async function referenceLibraryBindingsAttach(commandArgs, context) {
|
|
627
|
+
const values = parse(commandArgs, {
|
|
628
|
+
"library-id": "string",
|
|
629
|
+
"item-id": "string",
|
|
630
|
+
"version-id": "string",
|
|
631
|
+
"binding-id": "string",
|
|
632
|
+
"reference-id": "string",
|
|
633
|
+
"target-json": "string",
|
|
634
|
+
"update-policy": "string",
|
|
635
|
+
"expected-reference-fingerprint": "string",
|
|
636
|
+
"idempotency-key": "string"
|
|
637
|
+
});
|
|
638
|
+
return await context.client.attachReferenceLibraryProjectBinding(compact({
|
|
639
|
+
projectId: requireProject(context.config),
|
|
640
|
+
libraryId: requiredString(values["library-id"], "--library-id"),
|
|
641
|
+
itemId: requiredString(values["item-id"], "--item-id"),
|
|
642
|
+
versionId: optionalString(values["version-id"]),
|
|
643
|
+
bindingId: optionalString(values["binding-id"]),
|
|
644
|
+
target: referenceLibraryBindingTarget(values),
|
|
645
|
+
updatePolicy: referenceLibraryUpdatePolicy(values["update-policy"]),
|
|
646
|
+
expectedReferenceFingerprint: optionalString(values["expected-reference-fingerprint"]),
|
|
647
|
+
idempotencyKey: requiredString(values["idempotency-key"], "--idempotency-key")
|
|
648
|
+
}));
|
|
649
|
+
}
|
|
650
|
+
async function referenceLibraryBindingsUpdate(commandArgs, context) {
|
|
651
|
+
const values = parse(commandArgs, {
|
|
652
|
+
"binding-id": "string",
|
|
653
|
+
"to-version-id": "string",
|
|
654
|
+
"dry-run": "boolean",
|
|
655
|
+
"idempotency-key": "string"
|
|
656
|
+
});
|
|
657
|
+
const dryRun = values["dry-run"] === true;
|
|
658
|
+
return await context.client.updateReferenceLibraryProjectBinding(compact({
|
|
659
|
+
projectId: requireProject(context.config),
|
|
660
|
+
bindingId: requiredString(values["binding-id"], "--binding-id"),
|
|
661
|
+
toVersionId: optionalString(values["to-version-id"]),
|
|
662
|
+
dryRun: dryRun || undefined,
|
|
663
|
+
idempotencyKey: mutationIdempotencyKey(values["idempotency-key"], dryRun)
|
|
664
|
+
}));
|
|
665
|
+
}
|
|
666
|
+
async function referenceLibraryBindingsDetach(commandArgs, context) {
|
|
667
|
+
const values = parse(commandArgs, {
|
|
668
|
+
"binding-id": "string",
|
|
669
|
+
"delete-materialized-assets": "boolean",
|
|
670
|
+
"idempotency-key": "string"
|
|
671
|
+
});
|
|
672
|
+
return await context.client.detachReferenceLibraryProjectBinding(compact({
|
|
673
|
+
projectId: requireProject(context.config),
|
|
674
|
+
bindingId: requiredString(values["binding-id"], "--binding-id"),
|
|
675
|
+
deleteMaterializedAssets: values["delete-materialized-assets"] === true || undefined,
|
|
676
|
+
idempotencyKey: requiredString(values["idempotency-key"], "--idempotency-key")
|
|
677
|
+
}));
|
|
678
|
+
}
|
|
679
|
+
function referenceLibraryBindingTarget(values) {
|
|
680
|
+
const referenceId = optionalString(values["reference-id"]);
|
|
681
|
+
const targetJson = optionalString(values["target-json"]);
|
|
682
|
+
if (referenceId && targetJson) {
|
|
683
|
+
throw new errors_1.CliError("Use only one of --reference-id or --target-json", {
|
|
684
|
+
code: "invalid_arguments",
|
|
685
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
if (referenceId) {
|
|
689
|
+
return {
|
|
690
|
+
artifact: "references",
|
|
691
|
+
referenceId
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
if (!targetJson) {
|
|
695
|
+
throw new errors_1.CliError("Missing binding target. Pass --reference-id or --target-json.", {
|
|
696
|
+
code: "missing_required_option",
|
|
697
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
let parsed;
|
|
701
|
+
try {
|
|
702
|
+
parsed = JSON.parse(targetJson);
|
|
703
|
+
}
|
|
704
|
+
catch {
|
|
705
|
+
throw new errors_1.CliError("Invalid --target-json: expected a JSON object", {
|
|
706
|
+
code: "invalid_arguments",
|
|
707
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
if (!isRecord(parsed)) {
|
|
711
|
+
throw new errors_1.CliError("Invalid --target-json: expected a JSON object", {
|
|
712
|
+
code: "invalid_arguments",
|
|
713
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
return validateReferenceLibraryTargetJson(parsed);
|
|
717
|
+
}
|
|
718
|
+
function referenceLibraryScope(value) {
|
|
719
|
+
const scope = optionalString(value);
|
|
720
|
+
if (!scope) {
|
|
721
|
+
return undefined;
|
|
722
|
+
}
|
|
723
|
+
if (REFERENCE_LIBRARY_SCOPES.includes(scope)) {
|
|
724
|
+
return scope;
|
|
725
|
+
}
|
|
726
|
+
throw new errors_1.CliError(`Unsupported reference library scope: ${scope}`, {
|
|
727
|
+
code: "invalid_arguments",
|
|
728
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
function referenceLibraryCapturePolicy(value) {
|
|
732
|
+
const capturePolicy = optionalString(value);
|
|
733
|
+
if (!capturePolicy) {
|
|
734
|
+
throw new errors_1.CliError("Missing required option --capture-policy", {
|
|
735
|
+
code: "missing_required_option",
|
|
736
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
if (REFERENCE_LIBRARY_CAPTURE_POLICIES.includes(capturePolicy)) {
|
|
740
|
+
return capturePolicy;
|
|
741
|
+
}
|
|
742
|
+
throw new errors_1.CliError(`Unsupported --capture-policy: ${capturePolicy}`, {
|
|
743
|
+
code: "invalid_arguments",
|
|
744
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
function referenceLibraryAutoSelectFromFlags(values) {
|
|
748
|
+
const enable = values["auto-select-from-library"] === true;
|
|
749
|
+
const disable = values["no-auto-select-from-library"] === true;
|
|
750
|
+
if (enable && disable) {
|
|
751
|
+
throw new errors_1.CliError("Use only one of --auto-select-from-library or --no-auto-select-from-library", { code: "invalid_arguments", exitCode: errors_1.EXIT_CODES.usage });
|
|
752
|
+
}
|
|
753
|
+
if (disable) {
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
if (enable) {
|
|
757
|
+
return true;
|
|
758
|
+
}
|
|
759
|
+
return undefined;
|
|
760
|
+
}
|
|
761
|
+
function referenceLibraryUpdatePolicy(value) {
|
|
762
|
+
const updatePolicy = optionalString(value);
|
|
763
|
+
if (!updatePolicy) {
|
|
764
|
+
return undefined;
|
|
765
|
+
}
|
|
766
|
+
if (REFERENCE_LIBRARY_UPDATE_POLICIES.includes(updatePolicy)) {
|
|
767
|
+
return updatePolicy;
|
|
768
|
+
}
|
|
769
|
+
throw new errors_1.CliError(`Unsupported --update-policy: ${updatePolicy}`, {
|
|
770
|
+
code: "invalid_arguments",
|
|
771
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
function referenceLibraryGrantPermissions(value) {
|
|
775
|
+
const permission = optionalString(value);
|
|
776
|
+
if (!permission) {
|
|
777
|
+
throw new errors_1.CliError("Missing required option --permission", {
|
|
778
|
+
code: "missing_required_option",
|
|
779
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
if (permission === "can-use" || permission === "admin") {
|
|
783
|
+
return [...REFERENCE_LIBRARY_GRANT_PRESETS[permission]];
|
|
784
|
+
}
|
|
785
|
+
throw new errors_1.CliError(`Unsupported --permission: ${permission}`, {
|
|
786
|
+
code: "invalid_arguments",
|
|
787
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
function optionalReferenceLibraryConfidence(value, flag) {
|
|
791
|
+
const raw = optionalString(value);
|
|
792
|
+
if (!raw) {
|
|
793
|
+
return undefined;
|
|
794
|
+
}
|
|
795
|
+
const confidence = Number(raw);
|
|
796
|
+
if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
|
|
797
|
+
throw new errors_1.CliError(`${flag} must be a number between 0 and 1`, {
|
|
798
|
+
code: "invalid_arguments",
|
|
799
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
return confidence;
|
|
803
|
+
}
|
|
804
|
+
function validateReferenceLibraryTargetJson(target) {
|
|
805
|
+
const artifact = optionalString(target.artifact);
|
|
806
|
+
if (!artifact) {
|
|
807
|
+
throw new errors_1.CliError('Invalid --target-json: missing "artifact"', {
|
|
808
|
+
code: "invalid_arguments",
|
|
809
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
if (artifact === "references") {
|
|
813
|
+
requireTargetString(target, "referenceId", artifact);
|
|
814
|
+
assertReferenceLibraryTargetKeys(target, artifact, ["artifact", "referenceId"]);
|
|
815
|
+
return target;
|
|
816
|
+
}
|
|
817
|
+
if (artifact === "style-guide") {
|
|
818
|
+
requireTargetString(target, "styleGuideId", artifact);
|
|
819
|
+
if (target.variantId !== undefined) {
|
|
820
|
+
requireTargetString(target, "variantId", artifact);
|
|
821
|
+
}
|
|
822
|
+
assertReferenceLibraryTargetKeys(target, artifact, ["artifact", "styleGuideId", "variantId"]);
|
|
823
|
+
return target;
|
|
824
|
+
}
|
|
825
|
+
if (artifact === "distribution") {
|
|
826
|
+
if (target.platform !== undefined) {
|
|
827
|
+
requireTargetString(target, "platform", artifact);
|
|
828
|
+
}
|
|
829
|
+
assertReferenceLibraryTargetKeys(target, artifact, ["artifact", "platform"]);
|
|
830
|
+
return target;
|
|
831
|
+
}
|
|
832
|
+
throw new errors_1.CliError(`Invalid --target-json: unsupported artifact "${artifact}"`, {
|
|
833
|
+
code: "invalid_arguments",
|
|
834
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
function requireTargetString(target, key, artifact) {
|
|
838
|
+
const value = optionalString(target[key]);
|
|
839
|
+
if (!value) {
|
|
840
|
+
throw new errors_1.CliError(`Invalid --target-json: ${artifact}.${key} is required`, {
|
|
841
|
+
code: "invalid_arguments",
|
|
842
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
return value;
|
|
846
|
+
}
|
|
847
|
+
function assertReferenceLibraryTargetKeys(target, artifact, allowedKeys) {
|
|
848
|
+
const allowed = new Set(allowedKeys);
|
|
849
|
+
for (const key of Object.keys(target)) {
|
|
850
|
+
if (!allowed.has(key)) {
|
|
851
|
+
throw new errors_1.CliError(`Invalid --target-json: unsupported ${artifact}.${key}`, {
|
|
852
|
+
code: "invalid_arguments",
|
|
853
|
+
exitCode: errors_1.EXIT_CODES.usage
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
400
858
|
async function releaseVideoCreate(commandArgs, context) {
|
|
401
859
|
const values = parse(commandArgs, {
|
|
402
860
|
"project-name": "string",
|
|
@@ -407,6 +865,7 @@ async function releaseVideoCreate(commandArgs, context) {
|
|
|
407
865
|
"repo-name": "string",
|
|
408
866
|
"git-sha": "string",
|
|
409
867
|
"release-tag": "string",
|
|
868
|
+
"bootstrap-selected-project": "boolean",
|
|
410
869
|
preset: "string",
|
|
411
870
|
target: "string",
|
|
412
871
|
platform: "string",
|
|
@@ -430,10 +889,11 @@ async function releaseVideoCreate(commandArgs, context) {
|
|
|
430
889
|
script,
|
|
431
890
|
releaseNotes,
|
|
432
891
|
changelog,
|
|
433
|
-
repoUrl: optionalString(values["repo-url"]),
|
|
892
|
+
repoUrl: (0, repo_url_1.canonicalizeRepoUrl)(optionalString(values["repo-url"])),
|
|
434
893
|
repoName: optionalString(values["repo-name"]),
|
|
435
894
|
gitSha: optionalString(values["git-sha"]),
|
|
436
895
|
releaseTag: optionalString(values["release-tag"]),
|
|
896
|
+
bootstrapSelectedProject: values["bootstrap-selected-project"] === true || undefined,
|
|
437
897
|
preset: optionalString(values.preset),
|
|
438
898
|
target: optionalString(values.target),
|
|
439
899
|
platform: optionalString(values.platform),
|
|
@@ -449,24 +909,94 @@ async function releaseVideoCreate(commandArgs, context) {
|
|
|
449
909
|
? await createReleaseVideoWithReattach(requestPayload, context)
|
|
450
910
|
: await context.client.createReleaseVideo(requestPayload);
|
|
451
911
|
if (wait && !dryRun) {
|
|
912
|
+
emitReleaseVideoWaitHandle(response, context);
|
|
452
913
|
return await waitForReleaseVideoRun(response, context);
|
|
453
914
|
}
|
|
454
915
|
await followAgentRunEvents(response, context);
|
|
455
916
|
return response;
|
|
456
917
|
}
|
|
918
|
+
async function releaseVideoPreflight(commandArgs, context) {
|
|
919
|
+
const values = parse(commandArgs, {
|
|
920
|
+
"project-name": "string",
|
|
921
|
+
script: "string",
|
|
922
|
+
"release-notes": "string",
|
|
923
|
+
changelog: "string",
|
|
924
|
+
"repo-url": "string",
|
|
925
|
+
"repo-name": "string",
|
|
926
|
+
"git-sha": "string",
|
|
927
|
+
"release-tag": "string",
|
|
928
|
+
"bootstrap-selected-project": "boolean",
|
|
929
|
+
preset: "string",
|
|
930
|
+
target: "string",
|
|
931
|
+
platform: "string",
|
|
932
|
+
privacy: "string",
|
|
933
|
+
"force-regenerate": "boolean",
|
|
934
|
+
"override-audit": "boolean",
|
|
935
|
+
"audit-override-rationale": "string",
|
|
936
|
+
"idempotency-key": "string"
|
|
937
|
+
});
|
|
938
|
+
const script = await fileContentRequest(optionalString(values.script));
|
|
939
|
+
const releaseNotes = await fileContentRequest(optionalString(values["release-notes"]));
|
|
940
|
+
const changelog = await fileContentRequest(optionalString(values.changelog));
|
|
941
|
+
return await context.client.preflightReleaseVideo(compact({
|
|
942
|
+
projectName: optionalString(values["project-name"]),
|
|
943
|
+
projectId: context.config.projectIdSource === "flag" || context.config.projectIdSource === "env" ? context.config.projectId : undefined,
|
|
944
|
+
script,
|
|
945
|
+
releaseNotes,
|
|
946
|
+
changelog,
|
|
947
|
+
repoUrl: (0, repo_url_1.canonicalizeRepoUrl)(optionalString(values["repo-url"])),
|
|
948
|
+
repoName: optionalString(values["repo-name"]),
|
|
949
|
+
gitSha: optionalString(values["git-sha"]),
|
|
950
|
+
releaseTag: optionalString(values["release-tag"]),
|
|
951
|
+
bootstrapSelectedProject: values["bootstrap-selected-project"] === true || undefined,
|
|
952
|
+
preset: optionalString(values.preset),
|
|
953
|
+
target: optionalString(values.target),
|
|
954
|
+
platform: optionalString(values.platform),
|
|
955
|
+
privacy: optionalString(values.privacy),
|
|
956
|
+
forceRegenerate: values["force-regenerate"] === true || undefined,
|
|
957
|
+
overrideAudit: values["override-audit"] === true || undefined,
|
|
958
|
+
auditOverrideRationale: optionalString(values["audit-override-rationale"]),
|
|
959
|
+
idempotencyKey: optionalString(values["idempotency-key"]),
|
|
960
|
+
wait: true
|
|
961
|
+
}));
|
|
962
|
+
}
|
|
457
963
|
async function releaseVideoStatus(commandArgs, context) {
|
|
458
964
|
const values = parse(commandArgs, { "run-id": "string" });
|
|
459
965
|
const runId = requiredString(values["run-id"], "--run-id");
|
|
460
966
|
const jobResponse = await context.client.getJob({ jobId: runId });
|
|
461
967
|
return releaseVideoStatusFromJob(jobResponse, runId);
|
|
462
968
|
}
|
|
969
|
+
function emitReleaseVideoWaitHandle(response, context) {
|
|
970
|
+
const writer = context.waitNotice ?? context.notice;
|
|
971
|
+
if (!writer) {
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
const runId = agentRunIdFromResponse(response);
|
|
975
|
+
if (!runId) {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
const data = readEnvelopeData(response);
|
|
979
|
+
const project = isRecord(data.project) ? data.project : {};
|
|
980
|
+
const projectId = optionalString(project.id) ??
|
|
981
|
+
optionalString(data.projectId) ??
|
|
982
|
+
readNestedString(data, ["pipeline", "run", "projectId"]);
|
|
983
|
+
const status = optionalString(data.status) ??
|
|
984
|
+
readNestedString(data, ["pipeline", "run", "status"]);
|
|
985
|
+
writer(`[pds] release-video run handle: runId=${runId}` +
|
|
986
|
+
`${projectId ? ` projectId=${projectId}` : ""}` +
|
|
987
|
+
`${status ? ` status=${status}` : ""}`);
|
|
988
|
+
writer(`[pds] recover: pds release-video status --run-id ${runId} --json`);
|
|
989
|
+
writer(`[pds] watch: pds jobs watch --run-id ${runId} --jsonl`);
|
|
990
|
+
}
|
|
463
991
|
async function createReleaseVideoWithReattach(request, context) {
|
|
464
992
|
let attempt = 0;
|
|
465
993
|
let delayMs = RELEASE_VIDEO_REATTACH_INITIAL_DELAY_MS;
|
|
466
994
|
let sawDroppedCreate = false;
|
|
467
995
|
while (true) {
|
|
468
996
|
try {
|
|
469
|
-
return await context.client.createReleaseVideo(request
|
|
997
|
+
return await context.client.createReleaseVideo(request, {
|
|
998
|
+
timeoutMs: context.config.timeoutMs
|
|
999
|
+
});
|
|
470
1000
|
}
|
|
471
1001
|
catch (error) {
|
|
472
1002
|
if (isRecoverableCreateWaitDrop(error)) {
|
|
@@ -523,7 +1053,12 @@ function parse(commandArgs, options) {
|
|
|
523
1053
|
args: commandArgs,
|
|
524
1054
|
allowPositionals: false,
|
|
525
1055
|
strict: true,
|
|
526
|
-
options: Object.fromEntries(Object.entries(options).map(([name, type]) => [
|
|
1056
|
+
options: Object.fromEntries(Object.entries(options).map(([name, type]) => [
|
|
1057
|
+
name,
|
|
1058
|
+
type === "string[]"
|
|
1059
|
+
? { type: "string", multiple: true }
|
|
1060
|
+
: { type }
|
|
1061
|
+
]))
|
|
527
1062
|
}).values;
|
|
528
1063
|
}
|
|
529
1064
|
catch (error) {
|
|
@@ -560,6 +1095,19 @@ function requiredRunOrJobId(values) {
|
|
|
560
1095
|
function optionalString(value) {
|
|
561
1096
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
562
1097
|
}
|
|
1098
|
+
function optionalStringList(value) {
|
|
1099
|
+
const values = Array.isArray(value)
|
|
1100
|
+
? value
|
|
1101
|
+
: typeof value === "string"
|
|
1102
|
+
? [value]
|
|
1103
|
+
: [];
|
|
1104
|
+
const items = values
|
|
1105
|
+
.filter((item) => typeof item === "string")
|
|
1106
|
+
.flatMap((item) => item.split(","))
|
|
1107
|
+
.map((item) => item.trim())
|
|
1108
|
+
.filter(Boolean);
|
|
1109
|
+
return items.length > 0 ? items : undefined;
|
|
1110
|
+
}
|
|
563
1111
|
function mutationIdempotencyKey(value, dryRun) {
|
|
564
1112
|
if (dryRun) {
|
|
565
1113
|
return optionalString(value);
|
|
@@ -597,28 +1145,349 @@ async function followAgentRunEvents(response, context) {
|
|
|
597
1145
|
onEvent: context.emitJsonlEvent
|
|
598
1146
|
});
|
|
599
1147
|
}
|
|
600
|
-
async function
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
605
|
-
let confirmations = 0;
|
|
606
|
-
const seenEvents = new Set();
|
|
607
|
-
const emitRunEvent = context.emitJsonlEvent
|
|
608
|
-
? (event) => {
|
|
609
|
-
const eventKey = JSON.stringify(event);
|
|
610
|
-
if (seenEvents.has(eventKey)) {
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
context.emitJsonlEvent?.(event);
|
|
614
|
-
seenEvents.add(eventKey);
|
|
615
|
-
}
|
|
616
|
-
: undefined;
|
|
1148
|
+
async function publishDistributionWithReattach(request, context) {
|
|
1149
|
+
let attempt = 0;
|
|
1150
|
+
let delayMs = DISTRIBUTION_PUBLISH_REATTACH_INITIAL_DELAY_MS;
|
|
1151
|
+
let sawDroppedCreate = false;
|
|
617
1152
|
while (true) {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
1153
|
+
try {
|
|
1154
|
+
return await context.client.publishDistribution(request, {
|
|
1155
|
+
timeoutMs: context.config.timeoutMs
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
catch (error) {
|
|
1159
|
+
if (isRecoverableCreateWaitDrop(error)) {
|
|
1160
|
+
if (attempt >= DISTRIBUTION_PUBLISH_REATTACH_MAX_ATTEMPTS) {
|
|
1161
|
+
throw error;
|
|
1162
|
+
}
|
|
1163
|
+
attempt += 1;
|
|
1164
|
+
sawDroppedCreate = true;
|
|
1165
|
+
context.emitJsonlEvent?.({
|
|
1166
|
+
type: "wait_reconnect",
|
|
1167
|
+
command: "distribution.publish",
|
|
1168
|
+
reason: "create_request_dropped"
|
|
1169
|
+
});
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
if (sawDroppedCreate && isPendingIdempotencyReplayWithoutRunId(error)) {
|
|
1173
|
+
if (attempt >= DISTRIBUTION_PUBLISH_REATTACH_MAX_ATTEMPTS) {
|
|
1174
|
+
throw new errors_1.CliError("Distribution publish run handle was not available before retry limit", {
|
|
1175
|
+
code: "distribution_publish_reattach_timeout",
|
|
1176
|
+
exitCode: errors_1.EXIT_CODES.timeout,
|
|
1177
|
+
details: {
|
|
1178
|
+
attempts: attempt,
|
|
1179
|
+
reason: "idempotency_in_progress_without_run_id"
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
context.emitJsonlEvent?.({
|
|
1184
|
+
type: "wait_reconnect",
|
|
1185
|
+
command: "distribution.publish",
|
|
1186
|
+
reason: "idempotency_in_progress_without_run_id",
|
|
1187
|
+
attempt: attempt + 1
|
|
1188
|
+
});
|
|
1189
|
+
await (context.sleep ?? defaultSleep)(delayMs);
|
|
1190
|
+
delayMs = Math.min(delayMs * 2, DISTRIBUTION_PUBLISH_REATTACH_MAX_DELAY_MS);
|
|
1191
|
+
attempt += 1;
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
throw error;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
async function waitForDistributionPublishRun(response, context) {
|
|
1199
|
+
const runId = agentRunIdFromResponse(response);
|
|
1200
|
+
if (!runId) {
|
|
1201
|
+
return response;
|
|
1202
|
+
}
|
|
1203
|
+
if (isTerminalDistributionPublishResponse(response)) {
|
|
1204
|
+
await followAgentRunEvents(response, context);
|
|
1205
|
+
return response;
|
|
1206
|
+
}
|
|
1207
|
+
let confirmations = 0;
|
|
1208
|
+
const seenEvents = new Set();
|
|
1209
|
+
const emitRunEvent = context.emitJsonlEvent
|
|
1210
|
+
? (event) => {
|
|
1211
|
+
const eventKey = JSON.stringify(event);
|
|
1212
|
+
if (seenEvents.has(eventKey)) {
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
context.emitJsonlEvent?.(event);
|
|
1216
|
+
seenEvents.add(eventKey);
|
|
1217
|
+
}
|
|
1218
|
+
: undefined;
|
|
1219
|
+
while (true) {
|
|
1220
|
+
try {
|
|
1221
|
+
await context.client.watchJob({
|
|
1222
|
+
jobId: runId,
|
|
1223
|
+
onEvent: emitRunEvent
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
catch (error) {
|
|
1227
|
+
if (!isRecoverableWatchDrop(error)) {
|
|
1228
|
+
throw error;
|
|
1229
|
+
}
|
|
1230
|
+
context.emitJsonlEvent?.({
|
|
1231
|
+
type: "wait_reconnect",
|
|
1232
|
+
command: "distribution.publish",
|
|
1233
|
+
reason: "event_stream_dropped",
|
|
1234
|
+
runId,
|
|
1235
|
+
attempt: confirmations + 1
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
const jobResponse = await context.client.getJob({ jobId: runId });
|
|
1239
|
+
const run = readJobRun(jobResponse);
|
|
1240
|
+
assertTerminalDistributionPublishRunSucceeded(run, runId);
|
|
1241
|
+
const merged = mergeDistributionPublishRun(response, jobResponse);
|
|
1242
|
+
if (isTerminalDistributionPublishRun(run)) {
|
|
1243
|
+
return merged;
|
|
1244
|
+
}
|
|
1245
|
+
if (confirmations >= DISTRIBUTION_PUBLISH_WAIT_MAX_CONFIRMATIONS) {
|
|
1246
|
+
throw new errors_1.CliError("Distribution publish run did not reach a terminal state before wait limit", {
|
|
1247
|
+
code: "distribution_publish_wait_timeout",
|
|
1248
|
+
exitCode: errors_1.EXIT_CODES.timeout,
|
|
1249
|
+
details: {
|
|
1250
|
+
runId,
|
|
1251
|
+
status: run ? optionalString(run.status) ?? null : null,
|
|
1252
|
+
confirmations
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
context.emitJsonlEvent?.({
|
|
1257
|
+
type: "wait_reconnect",
|
|
1258
|
+
command: "distribution.publish",
|
|
1259
|
+
reason: "event_stream_closed_before_terminal",
|
|
1260
|
+
runId,
|
|
1261
|
+
status: run ? optionalString(run.status) ?? null : null,
|
|
1262
|
+
attempt: confirmations + 1
|
|
1263
|
+
});
|
|
1264
|
+
await (context.sleep ?? defaultSleep)(DISTRIBUTION_PUBLISH_WAIT_POLL_MS);
|
|
1265
|
+
confirmations += 1;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
async function createPipelineRunWithReattach(request, context) {
|
|
1269
|
+
let attempt = 0;
|
|
1270
|
+
let delayMs = PIPELINE_REATTACH_INITIAL_DELAY_MS;
|
|
1271
|
+
let sawDroppedCreate = false;
|
|
1272
|
+
while (true) {
|
|
1273
|
+
try {
|
|
1274
|
+
return await context.client.runPipeline(request, {
|
|
1275
|
+
timeoutMs: context.config.timeoutMs
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
catch (error) {
|
|
1279
|
+
if (isRecoverableCreateWaitDrop(error)) {
|
|
1280
|
+
if (attempt >= PIPELINE_REATTACH_MAX_ATTEMPTS) {
|
|
1281
|
+
throw error;
|
|
1282
|
+
}
|
|
1283
|
+
attempt += 1;
|
|
1284
|
+
sawDroppedCreate = true;
|
|
1285
|
+
context.emitJsonlEvent?.({
|
|
1286
|
+
type: "wait_reconnect",
|
|
1287
|
+
command: "pipeline.run",
|
|
1288
|
+
reason: "create_request_dropped"
|
|
1289
|
+
});
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
if (sawDroppedCreate && isPendingIdempotencyReplayWithoutRunId(error)) {
|
|
1293
|
+
if (attempt >= PIPELINE_REATTACH_MAX_ATTEMPTS) {
|
|
1294
|
+
throw new errors_1.CliError("Pipeline run handle was not available before retry limit", {
|
|
1295
|
+
code: "pipeline_reattach_timeout",
|
|
1296
|
+
exitCode: errors_1.EXIT_CODES.timeout,
|
|
1297
|
+
details: {
|
|
1298
|
+
attempts: attempt,
|
|
1299
|
+
reason: "idempotency_in_progress_without_run_id"
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
context.emitJsonlEvent?.({
|
|
1304
|
+
type: "wait_reconnect",
|
|
1305
|
+
command: "pipeline.run",
|
|
1306
|
+
reason: "idempotency_in_progress_without_run_id",
|
|
1307
|
+
attempt: attempt + 1
|
|
1308
|
+
});
|
|
1309
|
+
await (context.sleep ?? defaultSleep)(delayMs);
|
|
1310
|
+
delayMs = Math.min(delayMs * 2, PIPELINE_REATTACH_MAX_DELAY_MS);
|
|
1311
|
+
attempt += 1;
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1314
|
+
throw error;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
async function waitForPipelineRun(response, context) {
|
|
1319
|
+
const runId = agentRunIdFromResponse(response);
|
|
1320
|
+
if (!runId) {
|
|
1321
|
+
return response;
|
|
1322
|
+
}
|
|
1323
|
+
let confirmations = 0;
|
|
1324
|
+
const seenEvents = new Set();
|
|
1325
|
+
const emitRunEvent = context.emitJsonlEvent
|
|
1326
|
+
? (event) => {
|
|
1327
|
+
const eventKey = JSON.stringify(event);
|
|
1328
|
+
if (seenEvents.has(eventKey)) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
context.emitJsonlEvent?.(event);
|
|
1332
|
+
seenEvents.add(eventKey);
|
|
1333
|
+
}
|
|
1334
|
+
: undefined;
|
|
1335
|
+
while (true) {
|
|
1336
|
+
try {
|
|
1337
|
+
await context.client.watchJob({
|
|
1338
|
+
jobId: runId,
|
|
1339
|
+
onEvent: emitRunEvent
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
catch (error) {
|
|
1343
|
+
if (!isRecoverableWatchDrop(error)) {
|
|
1344
|
+
throw error;
|
|
1345
|
+
}
|
|
1346
|
+
context.emitJsonlEvent?.({
|
|
1347
|
+
type: "wait_reconnect",
|
|
1348
|
+
command: "pipeline.run",
|
|
1349
|
+
reason: "event_stream_dropped",
|
|
1350
|
+
runId,
|
|
1351
|
+
attempt: confirmations + 1
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
const jobResponse = await context.client.getJob({ jobId: runId });
|
|
1355
|
+
const run = readJobRun(jobResponse);
|
|
1356
|
+
assertTerminalPipelineRunSucceeded(run, runId);
|
|
1357
|
+
const merged = mergePipelineRun(response, jobResponse);
|
|
1358
|
+
if (isTerminalJobRun(run)) {
|
|
1359
|
+
return merged;
|
|
1360
|
+
}
|
|
1361
|
+
if (confirmations >= PIPELINE_WAIT_MAX_CONFIRMATIONS) {
|
|
1362
|
+
throw new errors_1.CliError("Pipeline run did not reach a terminal state before wait limit", {
|
|
1363
|
+
code: "pipeline_wait_timeout",
|
|
1364
|
+
exitCode: errors_1.EXIT_CODES.timeout,
|
|
1365
|
+
details: {
|
|
1366
|
+
runId,
|
|
1367
|
+
status: run ? optionalString(run.status) ?? null : null,
|
|
1368
|
+
confirmations
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
context.emitJsonlEvent?.({
|
|
1373
|
+
type: "wait_reconnect",
|
|
1374
|
+
command: "pipeline.run",
|
|
1375
|
+
reason: "event_stream_closed_before_terminal",
|
|
1376
|
+
runId,
|
|
1377
|
+
status: run ? optionalString(run.status) ?? null : null,
|
|
1378
|
+
attempt: confirmations + 1
|
|
1379
|
+
});
|
|
1380
|
+
await (context.sleep ?? defaultSleep)(PIPELINE_WAIT_POLL_MS);
|
|
1381
|
+
confirmations += 1;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
async function watchJobUntilTerminal(jobId, context) {
|
|
1385
|
+
let confirmations = 0;
|
|
1386
|
+
const events = [];
|
|
1387
|
+
const seenEvents = new Set();
|
|
1388
|
+
let sawTerminalEvent = false;
|
|
1389
|
+
const emitJobEvent = (event) => {
|
|
1390
|
+
const eventKey = JSON.stringify(event);
|
|
1391
|
+
if (seenEvents.has(eventKey)) {
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
seenEvents.add(eventKey);
|
|
1395
|
+
if (isTerminalWatchEvent(event)) {
|
|
1396
|
+
sawTerminalEvent = true;
|
|
1397
|
+
}
|
|
1398
|
+
events.push(event);
|
|
1399
|
+
context.emitJsonlEvent?.(event);
|
|
1400
|
+
};
|
|
1401
|
+
while (true) {
|
|
1402
|
+
let streamDropped = false;
|
|
1403
|
+
try {
|
|
1404
|
+
const watchResponse = await context.client.watchJob({
|
|
1405
|
+
jobId,
|
|
1406
|
+
onEvent: emitJobEvent
|
|
1407
|
+
});
|
|
1408
|
+
collectWatchEvents(watchResponse, emitJobEvent);
|
|
1409
|
+
}
|
|
1410
|
+
catch (error) {
|
|
1411
|
+
if (!isRecoverableWatchDrop(error)) {
|
|
1412
|
+
throw error;
|
|
1413
|
+
}
|
|
1414
|
+
streamDropped = true;
|
|
1415
|
+
}
|
|
1416
|
+
const jobResponse = await context.client.getJob({ jobId });
|
|
1417
|
+
const run = readJobRun(jobResponse);
|
|
1418
|
+
if (isTerminalJobRun(run)) {
|
|
1419
|
+
if (!sawTerminalEvent) {
|
|
1420
|
+
emitJobEvent(terminalJobWatchEvent(run, jobId));
|
|
1421
|
+
}
|
|
1422
|
+
return context.emitJsonlEvent ? { events: [] } : { events };
|
|
1423
|
+
}
|
|
1424
|
+
if (confirmations >= JOBS_WATCH_MAX_CONFIRMATIONS) {
|
|
1425
|
+
throw new errors_1.CliError("Agent job did not reach a terminal state before watch limit", {
|
|
1426
|
+
code: "job_watch_timeout",
|
|
1427
|
+
exitCode: errors_1.EXIT_CODES.timeout,
|
|
1428
|
+
details: {
|
|
1429
|
+
runId: jobId,
|
|
1430
|
+
status: run ? optionalString(run.status) ?? null : null,
|
|
1431
|
+
confirmations
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
context.emitJsonlEvent?.({
|
|
1436
|
+
type: "wait_reconnect",
|
|
1437
|
+
command: "jobs.watch",
|
|
1438
|
+
reason: streamDropped
|
|
1439
|
+
? "event_stream_dropped"
|
|
1440
|
+
: "event_stream_closed_before_terminal",
|
|
1441
|
+
runId: jobId,
|
|
1442
|
+
status: run ? optionalString(run.status) ?? null : null,
|
|
1443
|
+
attempt: confirmations + 1
|
|
621
1444
|
});
|
|
1445
|
+
await (context.sleep ?? defaultSleep)(JOBS_WATCH_POLL_MS);
|
|
1446
|
+
confirmations += 1;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
async function waitForReleaseVideoRun(response, context) {
|
|
1450
|
+
const runId = agentRunIdFromResponse(response);
|
|
1451
|
+
if (!runId) {
|
|
1452
|
+
return response;
|
|
1453
|
+
}
|
|
1454
|
+
let confirmations = 0;
|
|
1455
|
+
const seenEvents = new Set();
|
|
1456
|
+
const emitRunEvent = context.emitJsonlEvent
|
|
1457
|
+
? (event) => {
|
|
1458
|
+
const eventKey = JSON.stringify(event);
|
|
1459
|
+
if (seenEvents.has(eventKey)) {
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
context.emitJsonlEvent?.(event);
|
|
1463
|
+
seenEvents.add(eventKey);
|
|
1464
|
+
}
|
|
1465
|
+
: undefined;
|
|
1466
|
+
while (true) {
|
|
1467
|
+
try {
|
|
1468
|
+
await context.client.watchJob({
|
|
1469
|
+
jobId: runId,
|
|
1470
|
+
onEvent: emitRunEvent
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
catch (error) {
|
|
1474
|
+
if (!isRecoverableWatchDrop(error)) {
|
|
1475
|
+
throw error;
|
|
1476
|
+
}
|
|
1477
|
+
context.emitJsonlEvent?.({
|
|
1478
|
+
type: "wait_reconnect",
|
|
1479
|
+
command: "release-video.create",
|
|
1480
|
+
reason: "event_stream_dropped",
|
|
1481
|
+
runId,
|
|
1482
|
+
attempt: confirmations + 1
|
|
1483
|
+
});
|
|
1484
|
+
emitWaitStatusNotice(context, {
|
|
1485
|
+
command: "release-video.create",
|
|
1486
|
+
runId,
|
|
1487
|
+
reason: "event stream dropped; polling durable status",
|
|
1488
|
+
attempt: confirmations + 1
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
622
1491
|
const jobResponse = await context.client.getJob({ jobId: runId });
|
|
623
1492
|
const run = readJobRun(jobResponse);
|
|
624
1493
|
assertTerminalReleaseVideoRunSucceeded(run, runId);
|
|
@@ -630,11 +1499,13 @@ async function waitForReleaseVideoRun(response, context) {
|
|
|
630
1499
|
throw new errors_1.CliError("Release video run did not reach a terminal state before wait limit", {
|
|
631
1500
|
code: "release_video_wait_timeout",
|
|
632
1501
|
exitCode: errors_1.EXIT_CODES.timeout,
|
|
633
|
-
details: {
|
|
1502
|
+
details: compact({
|
|
634
1503
|
runId,
|
|
1504
|
+
eventsUrl: releaseVideoEventsUrl(response, run),
|
|
635
1505
|
status: run ? optionalString(run.status) ?? null : null,
|
|
1506
|
+
activeStep: activeStepId(run),
|
|
636
1507
|
confirmations
|
|
637
|
-
}
|
|
1508
|
+
})
|
|
638
1509
|
});
|
|
639
1510
|
}
|
|
640
1511
|
context.emitJsonlEvent?.({
|
|
@@ -645,26 +1516,144 @@ async function waitForReleaseVideoRun(response, context) {
|
|
|
645
1516
|
status: run ? optionalString(run.status) ?? null : null,
|
|
646
1517
|
attempt: confirmations + 1
|
|
647
1518
|
});
|
|
1519
|
+
emitWaitStatusNotice(context, {
|
|
1520
|
+
command: "release-video.create",
|
|
1521
|
+
runId,
|
|
1522
|
+
reason: "event stream closed before terminal state",
|
|
1523
|
+
status: run ? optionalString(run.status) ?? null : null,
|
|
1524
|
+
activeStep: activeStepId(run),
|
|
1525
|
+
attempt: confirmations + 1
|
|
1526
|
+
});
|
|
648
1527
|
await (context.sleep ?? defaultSleep)(RELEASE_VIDEO_WAIT_POLL_MS);
|
|
649
1528
|
confirmations += 1;
|
|
650
1529
|
}
|
|
651
1530
|
}
|
|
1531
|
+
function emitWaitStatusNotice(context, input) {
|
|
1532
|
+
const writer = context.waitNotice ?? context.notice;
|
|
1533
|
+
if (!writer) {
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
writer(`[pds] ${input.command} wait: ${input.reason}; ` +
|
|
1537
|
+
`runId=${input.runId}` +
|
|
1538
|
+
`${input.status ? ` status=${input.status}` : ""}` +
|
|
1539
|
+
`${input.activeStep ? ` activeStep=${input.activeStep}` : ""}` +
|
|
1540
|
+
` attempt=${input.attempt}`);
|
|
1541
|
+
}
|
|
1542
|
+
function releaseVideoEventsUrl(response, run) {
|
|
1543
|
+
return optionalString(run?.eventsUrl) ??
|
|
1544
|
+
readNestedString(readEnvelopeData(response), ["pipeline", "run", "eventsUrl"]);
|
|
1545
|
+
}
|
|
1546
|
+
function activeStepId(run) {
|
|
1547
|
+
if (!run || !Array.isArray(run.steps)) {
|
|
1548
|
+
return undefined;
|
|
1549
|
+
}
|
|
1550
|
+
const activeStep = run.steps.find((step) => isRecord(step) && step.status === "running");
|
|
1551
|
+
return isRecord(activeStep)
|
|
1552
|
+
? optionalString(activeStep.id) ?? optionalString(activeStep.stage)
|
|
1553
|
+
: undefined;
|
|
1554
|
+
}
|
|
652
1555
|
function agentRunIdFromResponse(value) {
|
|
653
1556
|
const data = readEnvelopeData(value);
|
|
654
1557
|
return (optionalString(data.runId) ??
|
|
655
1558
|
runIdFromNested(data.agentRun) ??
|
|
656
1559
|
runIdFromNested(data.pipeline, "run"));
|
|
657
1560
|
}
|
|
658
|
-
function
|
|
1561
|
+
function isTerminalDistributionPublishResponse(response) {
|
|
1562
|
+
const data = readEnvelopeData(response);
|
|
1563
|
+
const status = optionalString(data.status);
|
|
1564
|
+
return isTerminalAgentRunStatus(status);
|
|
1565
|
+
}
|
|
1566
|
+
function mergeDistributionPublishRun(response, jobResponse) {
|
|
659
1567
|
const run = readJobRun(jobResponse);
|
|
660
1568
|
if (!run || !isRecord(response)) {
|
|
661
1569
|
return response;
|
|
662
1570
|
}
|
|
1571
|
+
const data = readEnvelopeData(response);
|
|
1572
|
+
const terminalResult = isRecord(run.terminalResult) ? run.terminalResult : {};
|
|
1573
|
+
const responseAgentRun = isRecord(data.agentRun) ? data.agentRun : {};
|
|
1574
|
+
const runId = optionalString(run.runId) ??
|
|
1575
|
+
optionalString(run.id) ??
|
|
1576
|
+
agentRunIdFromResponse(response);
|
|
1577
|
+
const normalizedAgentRun = {
|
|
1578
|
+
...responseAgentRun,
|
|
1579
|
+
...run,
|
|
1580
|
+
...(runId ? { runId } : {})
|
|
1581
|
+
};
|
|
1582
|
+
const responsePublishStatus = isRecord(data.publishStatus) ? data.publishStatus : {};
|
|
1583
|
+
const terminalPublishStatus = isRecord(terminalResult.publishStatus)
|
|
1584
|
+
? terminalResult.publishStatus
|
|
1585
|
+
: undefined;
|
|
1586
|
+
const mergedData = compact({
|
|
1587
|
+
...data,
|
|
1588
|
+
...terminalResult,
|
|
1589
|
+
status: optionalString(run.status) ??
|
|
1590
|
+
optionalString(terminalResult.status) ??
|
|
1591
|
+
optionalString(data.status) ??
|
|
1592
|
+
"running",
|
|
1593
|
+
publishStatus: terminalPublishStatus
|
|
1594
|
+
? {
|
|
1595
|
+
...responsePublishStatus,
|
|
1596
|
+
...terminalPublishStatus
|
|
1597
|
+
}
|
|
1598
|
+
: data.publishStatus,
|
|
1599
|
+
agentRun: normalizedAgentRun
|
|
1600
|
+
});
|
|
1601
|
+
if (isRecord(response.data)) {
|
|
1602
|
+
return {
|
|
1603
|
+
...response,
|
|
1604
|
+
data: mergedData
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
return mergedData;
|
|
1608
|
+
}
|
|
1609
|
+
function mergePipelineRun(response, jobResponse) {
|
|
1610
|
+
const run = readJobRun(jobResponse);
|
|
1611
|
+
if (!run || !isRecord(response)) {
|
|
1612
|
+
return response;
|
|
1613
|
+
}
|
|
1614
|
+
const runId = optionalString(run.runId) ??
|
|
1615
|
+
optionalString(run.id) ??
|
|
1616
|
+
agentRunIdFromResponse(response);
|
|
663
1617
|
const normalizedRun = {
|
|
664
1618
|
...run,
|
|
665
|
-
runId
|
|
1619
|
+
...(runId ? { runId } : {})
|
|
666
1620
|
};
|
|
667
1621
|
const data = readEnvelopeData(response);
|
|
1622
|
+
const mergedData = compact({
|
|
1623
|
+
...data,
|
|
1624
|
+
runId,
|
|
1625
|
+
projectId: optionalString(run.projectId) ?? optionalString(data.projectId),
|
|
1626
|
+
target: optionalString(run.target) ?? optionalString(data.target),
|
|
1627
|
+
status: optionalString(run.status) ?? optionalString(data.status) ?? "running",
|
|
1628
|
+
run: normalizedRun,
|
|
1629
|
+
steps: Array.isArray(run.steps) ? run.steps : data.steps,
|
|
1630
|
+
terminalResult: run.terminalResult !== undefined
|
|
1631
|
+
? run.terminalResult
|
|
1632
|
+
: data.terminalResult
|
|
1633
|
+
});
|
|
1634
|
+
if (isRecord(response.data)) {
|
|
1635
|
+
return {
|
|
1636
|
+
...response,
|
|
1637
|
+
data: mergedData
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
return mergedData;
|
|
1641
|
+
}
|
|
1642
|
+
function mergeReleaseVideoRun(response, jobResponse) {
|
|
1643
|
+
const run = readJobRun(jobResponse);
|
|
1644
|
+
if (!run || !isRecord(response)) {
|
|
1645
|
+
return response;
|
|
1646
|
+
}
|
|
1647
|
+
const data = readEnvelopeData(response);
|
|
1648
|
+
const responsePipeline = isRecord(data.pipeline) ? data.pipeline : {};
|
|
1649
|
+
const responseRun = isRecord(responsePipeline.run) ? responsePipeline.run : {};
|
|
1650
|
+
const normalizedRun = {
|
|
1651
|
+
...responseRun,
|
|
1652
|
+
...run,
|
|
1653
|
+
runId: optionalString(run.runId) ??
|
|
1654
|
+
optionalString(run.id) ??
|
|
1655
|
+
optionalString(responseRun.runId)
|
|
1656
|
+
};
|
|
668
1657
|
if (!isRecord(data.pipeline)) {
|
|
669
1658
|
return response;
|
|
670
1659
|
}
|
|
@@ -787,6 +1776,70 @@ function readJobRun(value) {
|
|
|
787
1776
|
}
|
|
788
1777
|
return null;
|
|
789
1778
|
}
|
|
1779
|
+
function collectWatchEvents(value, onEvent) {
|
|
1780
|
+
if (!isRecord(value) || !Array.isArray(value.events)) {
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
for (const event of value.events) {
|
|
1784
|
+
onEvent(event);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
function isTerminalWatchEvent(event) {
|
|
1788
|
+
if (!isRecord(event)) {
|
|
1789
|
+
return false;
|
|
1790
|
+
}
|
|
1791
|
+
const type = optionalString(event.type);
|
|
1792
|
+
return type === "run_succeeded" ||
|
|
1793
|
+
type === "run_failed" ||
|
|
1794
|
+
type === "run_cancelled" ||
|
|
1795
|
+
type === "job_terminal";
|
|
1796
|
+
}
|
|
1797
|
+
function terminalJobWatchEvent(run, fallbackRunId) {
|
|
1798
|
+
return {
|
|
1799
|
+
type: "job_terminal",
|
|
1800
|
+
command: "jobs.watch",
|
|
1801
|
+
runId: optionalString(run?.runId) ??
|
|
1802
|
+
optionalString(run?.id) ??
|
|
1803
|
+
fallbackRunId,
|
|
1804
|
+
status: run ? optionalString(run.status) ?? null : null
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
function assertTerminalDistributionPublishRunSucceeded(run, fallbackRunId) {
|
|
1808
|
+
if (!run) {
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
const status = optionalString(run.status);
|
|
1812
|
+
if (!status || status === "succeeded") {
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
if (isTerminalAgentRunStatus(status)) {
|
|
1816
|
+
const error = isRecord(run.error) ? run.error : null;
|
|
1817
|
+
const publicError = publicReleaseVideoFailureError(error);
|
|
1818
|
+
const terminalCode = status === "timed_out"
|
|
1819
|
+
? "timeout"
|
|
1820
|
+
: status === "cancelled"
|
|
1821
|
+
? "cancelled"
|
|
1822
|
+
: undefined;
|
|
1823
|
+
const errorCode = optionalString(error?.code);
|
|
1824
|
+
const code = terminalCode ??
|
|
1825
|
+
errorCode ??
|
|
1826
|
+
"agent_run_failed";
|
|
1827
|
+
const message = errorCode === code
|
|
1828
|
+
? optionalString(error?.message) ?? distributionPublishFailureMessage(status)
|
|
1829
|
+
: distributionPublishFailureMessage(status);
|
|
1830
|
+
throw new errors_1.CliError(message, {
|
|
1831
|
+
code,
|
|
1832
|
+
exitCode: (0, errors_1.mapApiErrorToExitCode)(code, undefined),
|
|
1833
|
+
details: compact({
|
|
1834
|
+
runId: optionalString(run.runId) ?? optionalString(run.id) ?? fallbackRunId,
|
|
1835
|
+
status,
|
|
1836
|
+
failedStage: failedStepStage(run),
|
|
1837
|
+
logRef: pipelineFailureLogRef(run),
|
|
1838
|
+
error: publicError
|
|
1839
|
+
})
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
790
1843
|
function assertTerminalReleaseVideoRunSucceeded(run, fallbackRunId) {
|
|
791
1844
|
if (!run) {
|
|
792
1845
|
return;
|
|
@@ -823,6 +1876,42 @@ function assertTerminalReleaseVideoRunSucceeded(run, fallbackRunId) {
|
|
|
823
1876
|
});
|
|
824
1877
|
}
|
|
825
1878
|
}
|
|
1879
|
+
function assertTerminalPipelineRunSucceeded(run, fallbackRunId) {
|
|
1880
|
+
if (!run) {
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
const status = optionalString(run.status);
|
|
1884
|
+
if (!status || status === "succeeded") {
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
if (status === "failed" || status === "cancelled" || status === "timed_out") {
|
|
1888
|
+
const error = isRecord(run.error) ? run.error : null;
|
|
1889
|
+
const publicError = publicReleaseVideoFailureError(error);
|
|
1890
|
+
const terminalCode = status === "timed_out"
|
|
1891
|
+
? "timeout"
|
|
1892
|
+
: status === "cancelled"
|
|
1893
|
+
? "cancelled"
|
|
1894
|
+
: undefined;
|
|
1895
|
+
const errorCode = optionalString(error?.code);
|
|
1896
|
+
const code = terminalCode ??
|
|
1897
|
+
errorCode ??
|
|
1898
|
+
"agent_run_failed";
|
|
1899
|
+
const message = errorCode === code
|
|
1900
|
+
? optionalString(error?.message) ?? pipelineRunFailureMessage(status)
|
|
1901
|
+
: pipelineRunFailureMessage(status);
|
|
1902
|
+
throw new errors_1.CliError(message, {
|
|
1903
|
+
code,
|
|
1904
|
+
exitCode: (0, errors_1.mapApiErrorToExitCode)(code, undefined),
|
|
1905
|
+
details: compact({
|
|
1906
|
+
runId: optionalString(run.runId) ?? optionalString(run.id) ?? fallbackRunId,
|
|
1907
|
+
status,
|
|
1908
|
+
failedStage: failedStepStage(run),
|
|
1909
|
+
logRef: pipelineFailureLogRef(run),
|
|
1910
|
+
error: publicError
|
|
1911
|
+
})
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
826
1915
|
function isTerminalReleaseVideoRun(run) {
|
|
827
1916
|
const status = run ? optionalString(run.status) : undefined;
|
|
828
1917
|
return status === "succeeded" ||
|
|
@@ -830,6 +1919,56 @@ function isTerminalReleaseVideoRun(run) {
|
|
|
830
1919
|
status === "cancelled" ||
|
|
831
1920
|
status === "timed_out";
|
|
832
1921
|
}
|
|
1922
|
+
function isTerminalPipelineRun(run) {
|
|
1923
|
+
return isTerminalJobRun(run);
|
|
1924
|
+
}
|
|
1925
|
+
function isTerminalDistributionPublishRun(run) {
|
|
1926
|
+
return isTerminalJobRun(run);
|
|
1927
|
+
}
|
|
1928
|
+
function isTerminalJobRun(run) {
|
|
1929
|
+
const status = run ? optionalString(run.status) : undefined;
|
|
1930
|
+
return isTerminalAgentRunStatus(status);
|
|
1931
|
+
}
|
|
1932
|
+
function isTerminalAgentRunStatus(status) {
|
|
1933
|
+
return status === "succeeded" ||
|
|
1934
|
+
status === "failed" ||
|
|
1935
|
+
status === "cancelled" ||
|
|
1936
|
+
status === "timed_out";
|
|
1937
|
+
}
|
|
1938
|
+
function distributionPublishFailureMessage(status) {
|
|
1939
|
+
if (status === "timed_out") {
|
|
1940
|
+
return "Distribution publish run timed out";
|
|
1941
|
+
}
|
|
1942
|
+
if (status === "cancelled") {
|
|
1943
|
+
return "Distribution publish run cancelled";
|
|
1944
|
+
}
|
|
1945
|
+
return "Distribution publish run failed";
|
|
1946
|
+
}
|
|
1947
|
+
function pipelineRunFailureMessage(status) {
|
|
1948
|
+
if (status === "timed_out") {
|
|
1949
|
+
return "Pipeline run timed out";
|
|
1950
|
+
}
|
|
1951
|
+
if (status === "cancelled") {
|
|
1952
|
+
return "Pipeline run cancelled";
|
|
1953
|
+
}
|
|
1954
|
+
return "Pipeline run failed";
|
|
1955
|
+
}
|
|
1956
|
+
function pipelineFailureLogRef(run) {
|
|
1957
|
+
const error = isRecord(run.error) ? run.error : null;
|
|
1958
|
+
const details = isRecord(error?.details) ? error.details : null;
|
|
1959
|
+
if (isRecord(details?.logRef)) {
|
|
1960
|
+
return details.logRef;
|
|
1961
|
+
}
|
|
1962
|
+
const failedStage = failedStepStage(run);
|
|
1963
|
+
const runId = optionalString(run.runId) ?? optionalString(run.id);
|
|
1964
|
+
if (!failedStage || !runId) {
|
|
1965
|
+
return undefined;
|
|
1966
|
+
}
|
|
1967
|
+
return {
|
|
1968
|
+
runId,
|
|
1969
|
+
stage: failedStage,
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
833
1972
|
function releaseRunFailureMessage(status) {
|
|
834
1973
|
if (status === "timed_out") {
|
|
835
1974
|
return "Release video run timed out";
|
|
@@ -910,6 +2049,12 @@ function isRecoverableCreateWaitDrop(error) {
|
|
|
910
2049
|
}
|
|
911
2050
|
return error instanceof TypeError || isTransportLikeError(error);
|
|
912
2051
|
}
|
|
2052
|
+
function isRecoverableWatchDrop(error) {
|
|
2053
|
+
if (error instanceof errors_1.CliError && error.code === "watch_reconnect_exhausted") {
|
|
2054
|
+
return true;
|
|
2055
|
+
}
|
|
2056
|
+
return isRecoverableCreateWaitDrop(error);
|
|
2057
|
+
}
|
|
913
2058
|
function isClientTimeoutDrop(error) {
|
|
914
2059
|
return error.code === "timeout" &&
|
|
915
2060
|
error.status === undefined &&
|