@spira-lab/cli 0.0.8 → 0.0.10
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 +8 -0
- package/dist/index.cjs +335 -107
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Command line client for Spira.
|
|
4
4
|
|
|
5
|
+
> ⚠️ **Database feature is deprecated.** The Brand Machine "Database" subsystem
|
|
6
|
+
> has been retired from the product. The `spira dataBase glob | read | grep |
|
|
7
|
+
> search` commands still exist for backward compatibility only — do NOT build
|
|
8
|
+
> new workflows against them. Use the Brand Machine web dashboard or the
|
|
9
|
+
> per-user cc-engine workspace (`spira workspace` / dashboard Workspace tab)
|
|
10
|
+
> instead. The bundled `spira-database` skill has been removed so agents are
|
|
11
|
+
> no longer guided toward these commands.
|
|
12
|
+
|
|
5
13
|
## Install
|
|
6
14
|
|
|
7
15
|
```bash
|
package/dist/index.cjs
CHANGED
|
@@ -3457,7 +3457,7 @@ var require_commander = __commonJS({
|
|
|
3457
3457
|
});
|
|
3458
3458
|
|
|
3459
3459
|
// src/program.ts
|
|
3460
|
-
var
|
|
3460
|
+
var import_promises5 = require("node:fs/promises");
|
|
3461
3461
|
|
|
3462
3462
|
// ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/esm.mjs
|
|
3463
3463
|
var import_index = __toESM(require_commander(), 1);
|
|
@@ -18048,6 +18048,7 @@ var cliResultTypeSchema = external_exports.enum([
|
|
|
18048
18048
|
"aiJobAction",
|
|
18049
18049
|
"aiModelList",
|
|
18050
18050
|
"aiModelDetail",
|
|
18051
|
+
"aiModelProviderList",
|
|
18051
18052
|
"workflowList",
|
|
18052
18053
|
"workflowNodeCatalog",
|
|
18053
18054
|
"workflowDetail",
|
|
@@ -18059,6 +18060,8 @@ var cliResultTypeSchema = external_exports.enum([
|
|
|
18059
18060
|
"scheduleOccurrenceList",
|
|
18060
18061
|
"scheduleExecutionList",
|
|
18061
18062
|
"scheduleAction",
|
|
18063
|
+
"mediaInfo",
|
|
18064
|
+
"mediaAsset",
|
|
18062
18065
|
"dataBaseGlob",
|
|
18063
18066
|
"dataBaseRead",
|
|
18064
18067
|
"dataBaseGrep",
|
|
@@ -18083,16 +18086,13 @@ var cliResultSchema = external_exports.object({
|
|
|
18083
18086
|
// src/ai/model-docs.ts
|
|
18084
18087
|
var cliAiModelDocs = [
|
|
18085
18088
|
{
|
|
18086
|
-
"id": "
|
|
18089
|
+
"id": "ai-influencer-image",
|
|
18087
18090
|
"name": "AI Influencer",
|
|
18088
18091
|
"mediaType": "image",
|
|
18089
18092
|
"description": "AI Influencer image generation model.",
|
|
18090
18093
|
"capabilities": [
|
|
18091
18094
|
"text-to-image"
|
|
18092
18095
|
],
|
|
18093
|
-
"providers": [
|
|
18094
|
-
"image-higgsfield"
|
|
18095
|
-
],
|
|
18096
18096
|
"variants": [
|
|
18097
18097
|
{
|
|
18098
18098
|
"key": "default",
|
|
@@ -18155,9 +18155,6 @@ var cliAiModelDocs = [
|
|
|
18155
18155
|
"text-to-image",
|
|
18156
18156
|
"image-to-image"
|
|
18157
18157
|
],
|
|
18158
|
-
"providers": [
|
|
18159
|
-
"socheap-media"
|
|
18160
|
-
],
|
|
18161
18158
|
"variants": [
|
|
18162
18159
|
{
|
|
18163
18160
|
"key": "1k",
|
|
@@ -18248,9 +18245,6 @@ var cliAiModelDocs = [
|
|
|
18248
18245
|
"text-to-image",
|
|
18249
18246
|
"image-to-image"
|
|
18250
18247
|
],
|
|
18251
|
-
"providers": [
|
|
18252
|
-
"socheap-media"
|
|
18253
|
-
],
|
|
18254
18248
|
"variants": [
|
|
18255
18249
|
{
|
|
18256
18250
|
"key": "standard",
|
|
@@ -18331,9 +18325,6 @@ var cliAiModelDocs = [
|
|
|
18331
18325
|
"text-to-image",
|
|
18332
18326
|
"image-to-image"
|
|
18333
18327
|
],
|
|
18334
|
-
"providers": [
|
|
18335
|
-
"socheap-media"
|
|
18336
|
-
],
|
|
18337
18328
|
"variants": [
|
|
18338
18329
|
{
|
|
18339
18330
|
"key": "1k",
|
|
@@ -18430,9 +18421,6 @@ var cliAiModelDocs = [
|
|
|
18430
18421
|
"text-to-image",
|
|
18431
18422
|
"image-to-image"
|
|
18432
18423
|
],
|
|
18433
|
-
"providers": [
|
|
18434
|
-
"image-kie"
|
|
18435
|
-
],
|
|
18436
18424
|
"variants": [
|
|
18437
18425
|
{
|
|
18438
18426
|
"key": "default",
|
|
@@ -18505,9 +18493,6 @@ var cliAiModelDocs = [
|
|
|
18505
18493
|
"text-to-image",
|
|
18506
18494
|
"image-to-image"
|
|
18507
18495
|
],
|
|
18508
|
-
"providers": [
|
|
18509
|
-
"socheap-media"
|
|
18510
|
-
],
|
|
18511
18496
|
"variants": [
|
|
18512
18497
|
{
|
|
18513
18498
|
"key": "1k",
|
|
@@ -18598,9 +18583,6 @@ var cliAiModelDocs = [
|
|
|
18598
18583
|
"text-to-video",
|
|
18599
18584
|
"image-to-video"
|
|
18600
18585
|
],
|
|
18601
|
-
"providers": [
|
|
18602
|
-
"socheap-media"
|
|
18603
|
-
],
|
|
18604
18586
|
"variants": [
|
|
18605
18587
|
{
|
|
18606
18588
|
"key": "4s-720p",
|
|
@@ -18860,9 +18842,6 @@ var cliAiModelDocs = [
|
|
|
18860
18842
|
"text-to-video",
|
|
18861
18843
|
"image-to-video"
|
|
18862
18844
|
],
|
|
18863
|
-
"providers": [
|
|
18864
|
-
"socheap-media"
|
|
18865
|
-
],
|
|
18866
18845
|
"variants": [
|
|
18867
18846
|
{
|
|
18868
18847
|
"key": "480p",
|
|
@@ -18974,9 +18953,6 @@ var cliAiModelDocs = [
|
|
|
18974
18953
|
"text-to-video",
|
|
18975
18954
|
"image-to-video"
|
|
18976
18955
|
],
|
|
18977
|
-
"providers": [
|
|
18978
|
-
"hailuo-comfly"
|
|
18979
|
-
],
|
|
18980
18956
|
"variants": [
|
|
18981
18957
|
{
|
|
18982
18958
|
"key": "default",
|
|
@@ -19030,8 +19006,8 @@ var cliAiModelDocs = [
|
|
|
19030
19006
|
}
|
|
19031
19007
|
],
|
|
19032
19008
|
"notes": [
|
|
19033
|
-
"referenceVideo and generationType are not supported
|
|
19034
|
-
"duration=10 with resolution=1080P is
|
|
19009
|
+
"referenceVideo and generationType are not supported for this model.",
|
|
19010
|
+
"duration=10 with resolution=1080P is not supported."
|
|
19035
19011
|
]
|
|
19036
19012
|
},
|
|
19037
19013
|
{
|
|
@@ -19043,9 +19019,6 @@ var cliAiModelDocs = [
|
|
|
19043
19019
|
"text-to-video",
|
|
19044
19020
|
"image-to-video"
|
|
19045
19021
|
],
|
|
19046
|
-
"providers": [
|
|
19047
|
-
"socheap-media"
|
|
19048
|
-
],
|
|
19049
19022
|
"variants": [
|
|
19050
19023
|
{
|
|
19051
19024
|
"key": "720p",
|
|
@@ -19169,9 +19142,6 @@ var cliAiModelDocs = [
|
|
|
19169
19142
|
"capabilities": [
|
|
19170
19143
|
"image-to-video"
|
|
19171
19144
|
],
|
|
19172
|
-
"providers": [
|
|
19173
|
-
"fal-ai"
|
|
19174
|
-
],
|
|
19175
19145
|
"variants": [
|
|
19176
19146
|
{
|
|
19177
19147
|
"key": "default",
|
|
@@ -19286,9 +19256,6 @@ var cliAiModelDocs = [
|
|
|
19286
19256
|
"text-to-video",
|
|
19287
19257
|
"image-to-video"
|
|
19288
19258
|
],
|
|
19289
|
-
"providers": [
|
|
19290
|
-
"socheap-media"
|
|
19291
|
-
],
|
|
19292
19259
|
"variants": [
|
|
19293
19260
|
{
|
|
19294
19261
|
"key": "standard-no-sound",
|
|
@@ -19431,7 +19398,7 @@ var cliAiModelDocs = [
|
|
|
19431
19398
|
],
|
|
19432
19399
|
"notes": [
|
|
19433
19400
|
"generation type is derived from referenceImage count: 0 text, 1 reference, 2 first/last frames.",
|
|
19434
|
-
"referenceVideo, negativePrompt, and elements are not supported
|
|
19401
|
+
"referenceVideo, negativePrompt, and elements are not supported for Kling 3.0.",
|
|
19435
19402
|
"Use mode=4K for 4K generation; do not pass resolution."
|
|
19436
19403
|
]
|
|
19437
19404
|
},
|
|
@@ -19443,9 +19410,6 @@ var cliAiModelDocs = [
|
|
|
19443
19410
|
"capabilities": [
|
|
19444
19411
|
"image-to-video"
|
|
19445
19412
|
],
|
|
19446
|
-
"providers": [
|
|
19447
|
-
"socheap-media"
|
|
19448
|
-
],
|
|
19449
19413
|
"variants": [
|
|
19450
19414
|
{
|
|
19451
19415
|
"key": "standard",
|
|
@@ -19566,9 +19530,6 @@ var cliAiModelDocs = [
|
|
|
19566
19530
|
"text-to-video",
|
|
19567
19531
|
"image-to-video"
|
|
19568
19532
|
],
|
|
19569
|
-
"providers": [
|
|
19570
|
-
"socheap-media"
|
|
19571
|
-
],
|
|
19572
19533
|
"variants": [
|
|
19573
19534
|
{
|
|
19574
19535
|
"key": "standard-480p-no-sound",
|
|
@@ -19730,9 +19691,6 @@ var cliAiModelDocs = [
|
|
|
19730
19691
|
"text-to-video",
|
|
19731
19692
|
"image-to-video"
|
|
19732
19693
|
],
|
|
19733
|
-
"providers": [
|
|
19734
|
-
"socheap-media"
|
|
19735
|
-
],
|
|
19736
19694
|
"variants": [
|
|
19737
19695
|
{
|
|
19738
19696
|
"key": "standard-480p",
|
|
@@ -20016,9 +19974,6 @@ var cliAiModelDocs = [
|
|
|
20016
19974
|
"text-to-video",
|
|
20017
19975
|
"image-to-video"
|
|
20018
19976
|
],
|
|
20019
|
-
"providers": [
|
|
20020
|
-
"socheap-media"
|
|
20021
|
-
],
|
|
20022
19977
|
"variants": [
|
|
20023
19978
|
{
|
|
20024
19979
|
"key": "fast-720p",
|
|
@@ -20169,14 +20124,9 @@ var cliAiModelDocs = [
|
|
|
20169
20124
|
"description": "Whether the generated video should be extendable when supported."
|
|
20170
20125
|
},
|
|
20171
20126
|
{
|
|
20172
|
-
"name": "
|
|
20127
|
+
"name": "extendFromJobId",
|
|
20173
20128
|
"type": "string",
|
|
20174
|
-
"description": "
|
|
20175
|
-
},
|
|
20176
|
-
{
|
|
20177
|
-
"name": "extendProviderId",
|
|
20178
|
-
"type": "string",
|
|
20179
|
-
"description": "Original provider id for extension lineage. Must be socheap-media for this CLI path."
|
|
20129
|
+
"description": "Spira job ID of an extendable Veo generation owned by the current user."
|
|
20180
20130
|
}
|
|
20181
20131
|
],
|
|
20182
20132
|
"examples": [
|
|
@@ -20196,7 +20146,7 @@ var cliAiModelDocs = [
|
|
|
20196
20146
|
"referenceVideo and referenceAudio are not supported.",
|
|
20197
20147
|
"image_to_video is not a supported generationType; use reference_to_video with one or more referenceImage URLs.",
|
|
20198
20148
|
"first_last_frames_to_video requires exactly 2 referenceImage URLs.",
|
|
20199
|
-
"
|
|
20149
|
+
"extendFromJobId requires resolution=720p."
|
|
20200
20150
|
]
|
|
20201
20151
|
},
|
|
20202
20152
|
{
|
|
@@ -20205,7 +20155,6 @@ var cliAiModelDocs = [
|
|
|
20205
20155
|
mediaType: "audio",
|
|
20206
20156
|
description: "Suno music generation with simple prompt and custom song modes.",
|
|
20207
20157
|
capabilities: ["text-to-audio"],
|
|
20208
|
-
providers: ["socheap-media"],
|
|
20209
20158
|
variants: [
|
|
20210
20159
|
{
|
|
20211
20160
|
key: "v5_5",
|
|
@@ -20270,7 +20219,6 @@ var cliAiModelDocs = [
|
|
|
20270
20219
|
mediaType: "audio",
|
|
20271
20220
|
description: "ElevenLabs prompt-to-music generation.",
|
|
20272
20221
|
capabilities: ["text-to-audio"],
|
|
20273
|
-
providers: ["elevenlabs-audio"],
|
|
20274
20222
|
variants: [
|
|
20275
20223
|
{
|
|
20276
20224
|
key: "default",
|
|
@@ -20310,7 +20258,6 @@ var cliAiModelDocs = [
|
|
|
20310
20258
|
mediaType: "audio",
|
|
20311
20259
|
description: "ElevenLabs sound-effect generation from an English prompt.",
|
|
20312
20260
|
capabilities: ["text-to-audio"],
|
|
20313
|
-
providers: ["elevenlabs-audio"],
|
|
20314
20261
|
variants: [
|
|
20315
20262
|
{
|
|
20316
20263
|
key: "default",
|
|
@@ -20350,7 +20297,6 @@ var cliAiModelDocs = [
|
|
|
20350
20297
|
mediaType: "audio",
|
|
20351
20298
|
description: "ElevenLabs text-to-speech using an existing voice ID.",
|
|
20352
20299
|
capabilities: ["text-to-speech"],
|
|
20353
|
-
providers: ["elevenlabs-audio"],
|
|
20354
20300
|
variants: [
|
|
20355
20301
|
{
|
|
20356
20302
|
key: "multilingual-v2-v3",
|
|
@@ -20398,7 +20344,6 @@ var cliAiModelDocs = [
|
|
|
20398
20344
|
mediaType: "audio",
|
|
20399
20345
|
description: "Text-to-speech using either an existing voice ID or one reference audio URL.",
|
|
20400
20346
|
capabilities: ["text-to-speech", "audio-to-speech"],
|
|
20401
|
-
providers: ["elevenlabs-audio"],
|
|
20402
20347
|
variants: [
|
|
20403
20348
|
{
|
|
20404
20349
|
key: "voice-clone-tts",
|
|
@@ -20462,8 +20407,9 @@ function getCliAiModelDoc(modelId) {
|
|
|
20462
20407
|
}
|
|
20463
20408
|
|
|
20464
20409
|
// src/ai/wait.ts
|
|
20410
|
+
var DEFAULT_WAIT_COMMAND = "spira ai jobs create";
|
|
20465
20411
|
function wait(durationMs) {
|
|
20466
|
-
return new Promise((
|
|
20412
|
+
return new Promise((resolve2) => setTimeout(resolve2, durationMs));
|
|
20467
20413
|
}
|
|
20468
20414
|
function jobStatus(result) {
|
|
20469
20415
|
if (!result.data || typeof result.data !== "object") return void 0;
|
|
@@ -20481,27 +20427,27 @@ function providerError(result) {
|
|
|
20481
20427
|
}
|
|
20482
20428
|
return void 0;
|
|
20483
20429
|
}
|
|
20484
|
-
function completedResult(result) {
|
|
20430
|
+
function completedResult(result, command) {
|
|
20485
20431
|
return cliResultSchema.parse({
|
|
20486
20432
|
...result,
|
|
20487
20433
|
resultType: "aiJobStatus",
|
|
20488
|
-
command
|
|
20434
|
+
command,
|
|
20489
20435
|
nextActions: []
|
|
20490
20436
|
});
|
|
20491
20437
|
}
|
|
20492
|
-
function pollingError(result) {
|
|
20438
|
+
function pollingError(result, command) {
|
|
20493
20439
|
return cliResultSchema.parse({
|
|
20494
20440
|
...result,
|
|
20495
|
-
command
|
|
20441
|
+
command
|
|
20496
20442
|
});
|
|
20497
20443
|
}
|
|
20498
|
-
function terminalError(result, jobId, status) {
|
|
20444
|
+
function terminalError(result, jobId, status, command) {
|
|
20499
20445
|
const message = providerError(result) ?? `AI job ${jobId} ended with status ${status}.`;
|
|
20500
20446
|
return cliResultSchema.parse({
|
|
20501
20447
|
...result,
|
|
20502
20448
|
status: "error",
|
|
20503
20449
|
resultType: "aiJobStatus",
|
|
20504
|
-
command
|
|
20450
|
+
command,
|
|
20505
20451
|
error: {
|
|
20506
20452
|
code: status === "FAILED" ? "PROVIDER_FAILED" : "ASYNC_STATE",
|
|
20507
20453
|
message,
|
|
@@ -20511,12 +20457,12 @@ function terminalError(result, jobId, status) {
|
|
|
20511
20457
|
}
|
|
20512
20458
|
});
|
|
20513
20459
|
}
|
|
20514
|
-
function timeoutResult(jobId, timeoutMs, lastResult) {
|
|
20460
|
+
function timeoutResult(jobId, timeoutMs, command, lastResult) {
|
|
20515
20461
|
return cliResultSchema.parse({
|
|
20516
20462
|
object: "cli.result",
|
|
20517
20463
|
status: "error",
|
|
20518
20464
|
resultType: "aiJobStatus",
|
|
20519
|
-
command
|
|
20465
|
+
command,
|
|
20520
20466
|
data: lastResult?.data,
|
|
20521
20467
|
ids: { ...lastResult?.ids, jobId },
|
|
20522
20468
|
error: {
|
|
@@ -20524,16 +20470,19 @@ function timeoutResult(jobId, timeoutMs, lastResult) {
|
|
|
20524
20470
|
message: `AI job ${jobId} did not finish within ${timeoutMs}ms.`,
|
|
20525
20471
|
resource: { type: "aiJob", id: jobId },
|
|
20526
20472
|
retryable: true,
|
|
20527
|
-
nextActions: [
|
|
20473
|
+
nextActions: [
|
|
20474
|
+
{ label: "Keep waiting", command: `spira ai jobs wait ${jobId}` },
|
|
20475
|
+
{ label: "Show job status", command: `spira ai jobs status ${jobId}` }
|
|
20476
|
+
]
|
|
20528
20477
|
}
|
|
20529
20478
|
});
|
|
20530
20479
|
}
|
|
20531
|
-
function contractError(result, jobId) {
|
|
20480
|
+
function contractError(result, jobId, command) {
|
|
20532
20481
|
return cliResultSchema.parse({
|
|
20533
20482
|
...result,
|
|
20534
20483
|
status: "error",
|
|
20535
20484
|
resultType: "aiJobStatus",
|
|
20536
|
-
command
|
|
20485
|
+
command,
|
|
20537
20486
|
error: {
|
|
20538
20487
|
code: "CONTRACT_MISMATCH",
|
|
20539
20488
|
message: `AI job ${jobId} status response did not contain a recognized status.`,
|
|
@@ -20546,6 +20495,7 @@ function contractError(result, jobId) {
|
|
|
20546
20495
|
async function waitForAiJob(input) {
|
|
20547
20496
|
const now = input.now ?? Date.now;
|
|
20548
20497
|
const sleep = input.sleep ?? wait;
|
|
20498
|
+
const command = input.command ?? DEFAULT_WAIT_COMMAND;
|
|
20549
20499
|
const deadline = now() + input.timeoutMs;
|
|
20550
20500
|
let lastResult;
|
|
20551
20501
|
let lastStatus;
|
|
@@ -20553,19 +20503,19 @@ async function waitForAiJob(input) {
|
|
|
20553
20503
|
const result = await input.poll();
|
|
20554
20504
|
lastResult = result;
|
|
20555
20505
|
if (result.status === "error") {
|
|
20556
|
-
if (!result.error?.retryable) return pollingError(result);
|
|
20506
|
+
if (!result.error?.retryable) return pollingError(result, command);
|
|
20557
20507
|
} else {
|
|
20558
20508
|
const status = jobStatus(result);
|
|
20559
20509
|
if (status && status !== lastStatus) {
|
|
20560
20510
|
input.onStatus?.(status);
|
|
20561
20511
|
lastStatus = status;
|
|
20562
20512
|
}
|
|
20563
|
-
if (status === "COMPLETED") return completedResult(result);
|
|
20564
|
-
if (status === "FAILED" || status === "CANCELED") return terminalError(result, input.jobId, status);
|
|
20565
|
-
if (status !== "QUEUED" && status !== "RUNNING") return contractError(result, input.jobId);
|
|
20513
|
+
if (status === "COMPLETED") return completedResult(result, command);
|
|
20514
|
+
if (status === "FAILED" || status === "CANCELED") return terminalError(result, input.jobId, status, command);
|
|
20515
|
+
if (status !== "QUEUED" && status !== "RUNNING") return contractError(result, input.jobId, command);
|
|
20566
20516
|
}
|
|
20567
20517
|
const remainingMs = deadline - now();
|
|
20568
|
-
if (remainingMs <= 0) return timeoutResult(input.jobId, input.timeoutMs, lastResult);
|
|
20518
|
+
if (remainingMs <= 0) return timeoutResult(input.jobId, input.timeoutMs, command, lastResult);
|
|
20569
20519
|
await sleep(Math.min(input.intervalMs, remainingMs));
|
|
20570
20520
|
}
|
|
20571
20521
|
}
|
|
@@ -20895,7 +20845,33 @@ var commandDefinitions = [
|
|
|
20895
20845
|
},
|
|
20896
20846
|
resultType: "aiJobStatus",
|
|
20897
20847
|
examples: ["spira ai jobs status job_123"],
|
|
20898
|
-
relatedCommands: []
|
|
20848
|
+
relatedCommands: ["spira ai jobs wait <jobId>"]
|
|
20849
|
+
},
|
|
20850
|
+
{
|
|
20851
|
+
path: ["ai", "jobs", "wait"],
|
|
20852
|
+
purpose: "Block until an AI job finishes, then print the final result.",
|
|
20853
|
+
arguments: [{ name: "jobId", description: "AI job ID.", required: true }],
|
|
20854
|
+
options: [
|
|
20855
|
+
{
|
|
20856
|
+
name: "timeout",
|
|
20857
|
+
flag: "--timeout <duration>",
|
|
20858
|
+
description: "Maximum wait duration.",
|
|
20859
|
+
type: "duration",
|
|
20860
|
+
defaultValue: 6e5,
|
|
20861
|
+
minimum: 1e3
|
|
20862
|
+
},
|
|
20863
|
+
{
|
|
20864
|
+
name: "interval",
|
|
20865
|
+
flag: "--interval <duration>",
|
|
20866
|
+
description: "Polling interval while waiting.",
|
|
20867
|
+
type: "duration",
|
|
20868
|
+
defaultValue: 3e3,
|
|
20869
|
+
minimum: 1e3
|
|
20870
|
+
}
|
|
20871
|
+
],
|
|
20872
|
+
resultType: "aiJobStatus",
|
|
20873
|
+
examples: ["spira ai jobs wait job_123", "spira ai jobs wait job_123 --timeout 20m --interval 5s"],
|
|
20874
|
+
relatedCommands: ["spira ai jobs status <jobId>", "spira ai jobs create"]
|
|
20899
20875
|
},
|
|
20900
20876
|
{
|
|
20901
20877
|
path: ["ai", "models", "list"],
|
|
@@ -20921,6 +20897,21 @@ var commandDefinitions = [
|
|
|
20921
20897
|
examples: ["spira ai models get nano-banana-2", "spira ai models get seedance-2.0"],
|
|
20922
20898
|
relatedCommands: ["spira ai jobs create"]
|
|
20923
20899
|
},
|
|
20900
|
+
{
|
|
20901
|
+
path: ["ai", "models", "providers"],
|
|
20902
|
+
purpose: "List provider routing IDs for AI models; requires admin.access.",
|
|
20903
|
+
permissions: ["admin.access"],
|
|
20904
|
+
arguments: [{ name: "modelId", description: "Optional AI model ID.", required: false }],
|
|
20905
|
+
http: {
|
|
20906
|
+
method: "GET",
|
|
20907
|
+
path: "/api/cli/v1/ai/models/providers",
|
|
20908
|
+
authRequired: true,
|
|
20909
|
+
query: [{ name: "modelId", source: "argument", from: "modelId" }]
|
|
20910
|
+
},
|
|
20911
|
+
resultType: "aiModelProviderList",
|
|
20912
|
+
examples: ["spira ai models providers", "spira ai models providers veo3.1 --json"],
|
|
20913
|
+
relatedCommands: ["spira ai models list", "spira ai jobs create"]
|
|
20914
|
+
},
|
|
20924
20915
|
{
|
|
20925
20916
|
path: ["ai", "jobs", "create"],
|
|
20926
20917
|
purpose: "Create an AI image, video, or audio generation job.",
|
|
@@ -20929,9 +20920,8 @@ var commandDefinitions = [
|
|
|
20929
20920
|
{
|
|
20930
20921
|
name: "providerId",
|
|
20931
20922
|
flag: "--provider-id <providerId>",
|
|
20932
|
-
description: "
|
|
20933
|
-
type: "string"
|
|
20934
|
-
hidden: true
|
|
20923
|
+
description: "Explicit provider routing override; requires admin.access.",
|
|
20924
|
+
type: "string"
|
|
20935
20925
|
},
|
|
20936
20926
|
{
|
|
20937
20927
|
name: "inputsJson",
|
|
@@ -20978,7 +20968,12 @@ var commandDefinitions = [
|
|
|
20978
20968
|
`spira ai jobs create --model-id nano-banana-2 --inputs-json '{"prompt":"A product photo"}'`,
|
|
20979
20969
|
`spira ai jobs create --model-id nano-banana-2 --inputs-json '{"prompt":"A product photo"}' --wait`
|
|
20980
20970
|
],
|
|
20981
|
-
relatedCommands: [
|
|
20971
|
+
relatedCommands: [
|
|
20972
|
+
"spira ai jobs wait <jobId>",
|
|
20973
|
+
"spira ai jobs status <jobId>",
|
|
20974
|
+
"spira ai models get <modelId>",
|
|
20975
|
+
"spira credits balance"
|
|
20976
|
+
]
|
|
20982
20977
|
},
|
|
20983
20978
|
{
|
|
20984
20979
|
path: ["workflows", "list"],
|
|
@@ -21347,6 +21342,92 @@ var commandDefinitions = [
|
|
|
21347
21342
|
examples: ["spira schedules calendar --from 1781884800000 --to 1782490000000"],
|
|
21348
21343
|
relatedCommands: ["spira schedules get <scheduleId>"]
|
|
21349
21344
|
},
|
|
21345
|
+
{
|
|
21346
|
+
path: ["media", "info"],
|
|
21347
|
+
purpose: "Resolve a TikTok / YouTube / Instagram / X / Facebook link and show its metadata.",
|
|
21348
|
+
arguments: [{ name: "url", description: "Public post or video URL.", required: true }],
|
|
21349
|
+
http: {
|
|
21350
|
+
method: "POST",
|
|
21351
|
+
path: "/api/tools/media/info",
|
|
21352
|
+
authRequired: true,
|
|
21353
|
+
body: [{ name: "url", source: "argument", from: "url" }]
|
|
21354
|
+
},
|
|
21355
|
+
resultType: "mediaInfo",
|
|
21356
|
+
examples: [
|
|
21357
|
+
"spira media info https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
21358
|
+
"spira media info https://www.tiktok.com/@user/video/123 --json"
|
|
21359
|
+
],
|
|
21360
|
+
relatedCommands: ["spira media video <url>", "spira media thumbnail <url>", "spira credits balance"]
|
|
21361
|
+
},
|
|
21362
|
+
{
|
|
21363
|
+
path: ["media", "video"],
|
|
21364
|
+
purpose: "Download the video file behind a social link.",
|
|
21365
|
+
arguments: [{ name: "url", description: "Public post or video URL.", required: true }],
|
|
21366
|
+
options: [
|
|
21367
|
+
{
|
|
21368
|
+
name: "maxHeight",
|
|
21369
|
+
flag: "--max-height <pixels>",
|
|
21370
|
+
description: "Cap the selected format height. Omit for the best available.",
|
|
21371
|
+
type: "number",
|
|
21372
|
+
minimum: 144
|
|
21373
|
+
},
|
|
21374
|
+
{
|
|
21375
|
+
name: "quality",
|
|
21376
|
+
flag: "--quality <mode>",
|
|
21377
|
+
description: "compatible (default) grabs H.264 MP4 that any editor opens; best grabs the highest resolution, which may be WebM/AV1.",
|
|
21378
|
+
type: "string",
|
|
21379
|
+
enumValues: ["compatible", "best"]
|
|
21380
|
+
},
|
|
21381
|
+
{
|
|
21382
|
+
name: "output",
|
|
21383
|
+
flag: "--output <path>",
|
|
21384
|
+
description: "Also save a local copy from the returned URL.",
|
|
21385
|
+
type: "string"
|
|
21386
|
+
}
|
|
21387
|
+
],
|
|
21388
|
+
http: {
|
|
21389
|
+
method: "POST",
|
|
21390
|
+
path: "/api/tools/media/video",
|
|
21391
|
+
authRequired: true,
|
|
21392
|
+
body: [
|
|
21393
|
+
{ name: "url", source: "argument", from: "url" },
|
|
21394
|
+
{ name: "maxHeight", source: "option", from: "maxHeight" },
|
|
21395
|
+
{ name: "quality", source: "option", from: "quality" }
|
|
21396
|
+
]
|
|
21397
|
+
},
|
|
21398
|
+
resultType: "mediaAsset",
|
|
21399
|
+
examples: [
|
|
21400
|
+
"spira media video https://www.tiktok.com/@user/video/123",
|
|
21401
|
+
"spira media video https://www.youtube.com/watch?v=dQw4w9WgXcQ --max-height 1080 --output ./downloads/",
|
|
21402
|
+
"spira media video https://www.youtube.com/shorts/abc123 --quality best"
|
|
21403
|
+
],
|
|
21404
|
+
relatedCommands: ["spira media info <url>", "spira media thumbnail <url>"]
|
|
21405
|
+
},
|
|
21406
|
+
{
|
|
21407
|
+
path: ["media", "thumbnail"],
|
|
21408
|
+
purpose: "Download the cover image behind a social link.",
|
|
21409
|
+
arguments: [{ name: "url", description: "Public post or video URL.", required: true }],
|
|
21410
|
+
options: [
|
|
21411
|
+
{
|
|
21412
|
+
name: "output",
|
|
21413
|
+
flag: "--output <path>",
|
|
21414
|
+
description: "Also save a local copy from the returned URL.",
|
|
21415
|
+
type: "string"
|
|
21416
|
+
}
|
|
21417
|
+
],
|
|
21418
|
+
http: {
|
|
21419
|
+
method: "POST",
|
|
21420
|
+
path: "/api/tools/media/thumbnail",
|
|
21421
|
+
authRequired: true,
|
|
21422
|
+
body: [{ name: "url", source: "argument", from: "url" }]
|
|
21423
|
+
},
|
|
21424
|
+
resultType: "mediaAsset",
|
|
21425
|
+
examples: [
|
|
21426
|
+
"spira media thumbnail https://x.com/user/status/123",
|
|
21427
|
+
"spira media thumbnail https://www.facebook.com/watch/?v=123 --output ./cover.jpg"
|
|
21428
|
+
],
|
|
21429
|
+
relatedCommands: ["spira media info <url>", "spira media video <url>"]
|
|
21430
|
+
},
|
|
21350
21431
|
{
|
|
21351
21432
|
path: ["dataBase", "glob"],
|
|
21352
21433
|
purpose: "List files and folders in your Brand database matching a glob pattern.",
|
|
@@ -21878,6 +21959,69 @@ async function executeHttpCommand(input) {
|
|
|
21878
21959
|
return successResult(input.command, response.body, response.requestId);
|
|
21879
21960
|
}
|
|
21880
21961
|
|
|
21962
|
+
// src/media/save.ts
|
|
21963
|
+
var import_node_fs = require("node:fs");
|
|
21964
|
+
var import_promises3 = require("node:fs/promises");
|
|
21965
|
+
var import_node_path3 = require("node:path");
|
|
21966
|
+
var import_node_stream = require("node:stream");
|
|
21967
|
+
var import_promises4 = require("node:stream/promises");
|
|
21968
|
+
function assetRecord(data) {
|
|
21969
|
+
if (!data || typeof data !== "object") return null;
|
|
21970
|
+
const asset = data.asset;
|
|
21971
|
+
return asset && typeof asset === "object" ? asset : null;
|
|
21972
|
+
}
|
|
21973
|
+
function fileNameFor(asset) {
|
|
21974
|
+
const key = typeof asset.key === "string" ? (0, import_node_path3.basename)(asset.key) : "";
|
|
21975
|
+
if (key) return key;
|
|
21976
|
+
const fromUrl = typeof asset.url === "string" ? (0, import_node_path3.basename)(new URL(asset.url).pathname) : "";
|
|
21977
|
+
return fromUrl || "download";
|
|
21978
|
+
}
|
|
21979
|
+
async function isDirectory(path) {
|
|
21980
|
+
return (0, import_promises3.stat)(path).then((entry) => entry.isDirectory()).catch(() => false);
|
|
21981
|
+
}
|
|
21982
|
+
async function resolveTargetPath(output, asset) {
|
|
21983
|
+
const target = (0, import_node_path3.resolve)(output);
|
|
21984
|
+
const looksLikeDirectory = /[\\/]$/.test(output) || await isDirectory(target);
|
|
21985
|
+
return looksLikeDirectory ? (0, import_node_path3.join)(target, fileNameFor(asset)) : target;
|
|
21986
|
+
}
|
|
21987
|
+
async function writeAssetToDisk(url2, targetPath) {
|
|
21988
|
+
const response = await fetch(url2);
|
|
21989
|
+
if (!response.ok || !response.body) {
|
|
21990
|
+
throw new Error(`Download failed with status ${response.status}.`);
|
|
21991
|
+
}
|
|
21992
|
+
await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(targetPath), { recursive: true });
|
|
21993
|
+
await (0, import_promises4.pipeline)(
|
|
21994
|
+
import_node_stream.Readable.fromWeb(response.body),
|
|
21995
|
+
(0, import_node_fs.createWriteStream)(targetPath)
|
|
21996
|
+
);
|
|
21997
|
+
}
|
|
21998
|
+
async function saveMediaAsset(result, output) {
|
|
21999
|
+
if (result.status !== "success") return result;
|
|
22000
|
+
const asset = assetRecord(result.data);
|
|
22001
|
+
const url2 = asset && typeof asset.url === "string" ? asset.url : null;
|
|
22002
|
+
if (!asset || !url2) {
|
|
22003
|
+
return cliResultSchema.parse({
|
|
22004
|
+
...result,
|
|
22005
|
+
warnings: [...result.warnings, "Response contained no downloadable asset URL; nothing was saved locally."]
|
|
22006
|
+
});
|
|
22007
|
+
}
|
|
22008
|
+
try {
|
|
22009
|
+
const targetPath = await resolveTargetPath(output, asset);
|
|
22010
|
+
await writeAssetToDisk(url2, targetPath);
|
|
22011
|
+
return cliResultSchema.parse({
|
|
22012
|
+
...result,
|
|
22013
|
+
data: { ...result.data, asset: { ...asset, savedTo: targetPath } },
|
|
22014
|
+
impact: [...result.impact, `Saved to ${targetPath}`]
|
|
22015
|
+
});
|
|
22016
|
+
} catch (error51) {
|
|
22017
|
+
const message = error51 instanceof Error && error51.message ? error51.message : "Unknown error.";
|
|
22018
|
+
return cliResultSchema.parse({
|
|
22019
|
+
...result,
|
|
22020
|
+
warnings: [...result.warnings, `Could not save the file locally: ${message} The asset URL is still available.`]
|
|
22021
|
+
});
|
|
22022
|
+
}
|
|
22023
|
+
}
|
|
22024
|
+
|
|
21881
22025
|
// src/auth/login.ts
|
|
21882
22026
|
var import_node_crypto = require("node:crypto");
|
|
21883
22027
|
|
|
@@ -21891,19 +22035,19 @@ function openBrowser(url2) {
|
|
|
21891
22035
|
child.unref();
|
|
21892
22036
|
}
|
|
21893
22037
|
function closeServer(server) {
|
|
21894
|
-
return new Promise((
|
|
22038
|
+
return new Promise((resolve2) => server.close(() => resolve2()));
|
|
21895
22039
|
}
|
|
21896
22040
|
function startLoopbackCallbackServer(input) {
|
|
21897
22041
|
const server = (0, import_node_http.createServer)();
|
|
21898
22042
|
let timer = null;
|
|
21899
22043
|
let resolveCallback = null;
|
|
21900
22044
|
let rejectCallback = null;
|
|
21901
|
-
const callback = new Promise((
|
|
21902
|
-
resolveCallback =
|
|
22045
|
+
const callback = new Promise((resolve2, reject) => {
|
|
22046
|
+
resolveCallback = resolve2;
|
|
21903
22047
|
rejectCallback = reject;
|
|
21904
22048
|
});
|
|
21905
22049
|
const ready = new Promise(
|
|
21906
|
-
(
|
|
22050
|
+
(resolve2, reject) => {
|
|
21907
22051
|
timer = setTimeout(() => {
|
|
21908
22052
|
rejectCallback?.(new Error("Timed out waiting for browser callback."));
|
|
21909
22053
|
void closeServer(server);
|
|
@@ -21930,7 +22074,7 @@ function startLoopbackCallbackServer(input) {
|
|
|
21930
22074
|
reject(new Error("Failed to start local CLI callback server."));
|
|
21931
22075
|
return;
|
|
21932
22076
|
}
|
|
21933
|
-
|
|
22077
|
+
resolve2({
|
|
21934
22078
|
redirectUri: `http://127.0.0.1:${address.port}/callback`,
|
|
21935
22079
|
waitForCallback: () => callback,
|
|
21936
22080
|
close: () => closeServer(server)
|
|
@@ -22110,20 +22254,18 @@ function dataArray(data) {
|
|
|
22110
22254
|
}
|
|
22111
22255
|
function renderRows(rows, columns) {
|
|
22112
22256
|
if (rows.length === 0) return "No results.\n";
|
|
22113
|
-
const records = rows.map(
|
|
22257
|
+
const records = rows.map(
|
|
22258
|
+
(row) => row && typeof row === "object" ? row : { value: row }
|
|
22259
|
+
);
|
|
22114
22260
|
const widths = columns.map(
|
|
22115
|
-
(column) => Math.min(
|
|
22116
|
-
42,
|
|
22117
|
-
Math.max(
|
|
22118
|
-
column.length,
|
|
22119
|
-
...records.map((record2) => valueToText(record2[column]).slice(0, 42).length)
|
|
22120
|
-
)
|
|
22121
|
-
)
|
|
22261
|
+
(column) => Math.min(42, Math.max(column.length, ...records.map((record2) => valueToText(record2[column]).slice(0, 42).length)))
|
|
22122
22262
|
);
|
|
22123
22263
|
const header = columns.map((column, index) => column.padEnd(widths[index])).join(" ");
|
|
22124
22264
|
const divider = widths.map((width) => "-".repeat(width)).join(" ");
|
|
22125
22265
|
const body = records.map(
|
|
22126
|
-
(record2) => columns.map(
|
|
22266
|
+
(record2) => columns.map(
|
|
22267
|
+
(column, index) => valueToText(record2[column]).replace(/\s+/g, " ").slice(0, widths[index]).padEnd(widths[index])
|
|
22268
|
+
).join(" ")
|
|
22127
22269
|
).join("\n");
|
|
22128
22270
|
return `${header}
|
|
22129
22271
|
${divider}
|
|
@@ -22293,6 +22435,9 @@ function renderAiJobStatus(data) {
|
|
|
22293
22435
|
function renderAiModelList(data) {
|
|
22294
22436
|
return renderRows(dataArray(data), ["id", "mediaType", "creditOptions", "name", "capabilities"]);
|
|
22295
22437
|
}
|
|
22438
|
+
function renderAiModelProviderList(data) {
|
|
22439
|
+
return renderRows(dataArray(data), ["modelId", "name", "providers", "hidden"]);
|
|
22440
|
+
}
|
|
22296
22441
|
function renderAiModelDetail(data) {
|
|
22297
22442
|
const record2 = objectRecord2(data);
|
|
22298
22443
|
const fields = Array.isArray(record2.fields) ? record2.fields.map(objectRecord2) : [];
|
|
@@ -22393,6 +22538,57 @@ function formatAnchor(anchor) {
|
|
|
22393
22538
|
}
|
|
22394
22539
|
return parts.join(", ") || "-";
|
|
22395
22540
|
}
|
|
22541
|
+
function formatMediaDuration(value) {
|
|
22542
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return "-";
|
|
22543
|
+
const total = Math.round(value);
|
|
22544
|
+
const hours = Math.floor(total / 3600);
|
|
22545
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
22546
|
+
const seconds = total % 60;
|
|
22547
|
+
const padded = `${String(minutes).padStart(hours ? 2 : 1, "0")}:${String(seconds).padStart(2, "0")}`;
|
|
22548
|
+
return hours ? `${hours}:${padded}` : padded;
|
|
22549
|
+
}
|
|
22550
|
+
function renderMediaInfo(data) {
|
|
22551
|
+
const info = objectRecord2(objectRecord2(data).info);
|
|
22552
|
+
const formats = Array.isArray(info.formats) ? info.formats.map(objectRecord2) : [];
|
|
22553
|
+
const lines = [
|
|
22554
|
+
`platform: ${valueToText(info.platform)}`,
|
|
22555
|
+
`title: ${compactText(info.title)}`,
|
|
22556
|
+
`uploader: ${valueToText(info.uploader ?? info.channel)}`,
|
|
22557
|
+
`duration: ${formatMediaDuration(info.duration)}`,
|
|
22558
|
+
`views: ${valueToText(info.view_count)}`,
|
|
22559
|
+
`likes: ${valueToText(info.like_count)}`,
|
|
22560
|
+
`uploadDate: ${valueToText(info.upload_date)}`,
|
|
22561
|
+
`url: ${valueToText(info.webpage_url ?? info.url)}`,
|
|
22562
|
+
`suggestedFilename: ${valueToText(info.suggested_filename)}`
|
|
22563
|
+
];
|
|
22564
|
+
if (formats.length === 0) return `${lines.join("\n")}
|
|
22565
|
+
`;
|
|
22566
|
+
const rows = formats.filter((format) => format.has_video === true).map((format) => ({
|
|
22567
|
+
format_id: format.format_id,
|
|
22568
|
+
ext: format.ext,
|
|
22569
|
+
resolution: format.resolution,
|
|
22570
|
+
fps: format.fps,
|
|
22571
|
+
size: formatByteSize(format.filesize),
|
|
22572
|
+
audio: format.has_audio === true ? "yes" : "no"
|
|
22573
|
+
}));
|
|
22574
|
+
if (rows.length === 0) return `${lines.join("\n")}
|
|
22575
|
+
`;
|
|
22576
|
+
return `${lines.join("\n")}
|
|
22577
|
+
videoFormats (${rows.length}):
|
|
22578
|
+
${renderRows(rows, ["format_id", "ext", "resolution", "fps", "size", "audio"])}`;
|
|
22579
|
+
}
|
|
22580
|
+
function renderMediaAsset(data) {
|
|
22581
|
+
const asset = objectRecord2(objectRecord2(data).asset);
|
|
22582
|
+
const lines = [
|
|
22583
|
+
`title: ${compactText(asset.title)}`,
|
|
22584
|
+
`size: ${formatByteSize(asset.size)}`,
|
|
22585
|
+
`contentType: ${valueToText(asset.contentType)}`,
|
|
22586
|
+
`url: ${valueToText(asset.url)}`
|
|
22587
|
+
];
|
|
22588
|
+
if (asset.savedTo) lines.push(`savedTo: ${valueToText(asset.savedTo)}`);
|
|
22589
|
+
return `${lines.join("\n")}
|
|
22590
|
+
`;
|
|
22591
|
+
}
|
|
22396
22592
|
function renderDataBaseGlob(data) {
|
|
22397
22593
|
const record2 = objectRecord2(data);
|
|
22398
22594
|
const pattern = valueToText(record2.pattern);
|
|
@@ -22512,6 +22708,8 @@ function renderSuccess(result) {
|
|
|
22512
22708
|
return renderAiModelList(result.data);
|
|
22513
22709
|
case "aiModelDetail":
|
|
22514
22710
|
return renderAiModelDetail(result.data);
|
|
22711
|
+
case "aiModelProviderList":
|
|
22712
|
+
return renderAiModelProviderList(result.data);
|
|
22515
22713
|
case "workflowList":
|
|
22516
22714
|
return renderRows(dataArray(result.data), ["id", "name", "creatorName", "nodeTypes", "updatedAt"]);
|
|
22517
22715
|
case "workflowNodeCatalog":
|
|
@@ -22527,7 +22725,18 @@ function renderSuccess(result) {
|
|
|
22527
22725
|
case "scheduleOccurrenceList":
|
|
22528
22726
|
return renderRows(dataArray(result.data), ["id", "scheduleId", "scheduledAt", "status", "executorType"]);
|
|
22529
22727
|
case "scheduleExecutionList":
|
|
22530
|
-
return renderRows(dataArray(result.data), [
|
|
22728
|
+
return renderRows(dataArray(result.data), [
|
|
22729
|
+
"id",
|
|
22730
|
+
"scheduleId",
|
|
22731
|
+
"status",
|
|
22732
|
+
"originalScheduledAt",
|
|
22733
|
+
"startedAt",
|
|
22734
|
+
"finishedAt"
|
|
22735
|
+
]);
|
|
22736
|
+
case "mediaInfo":
|
|
22737
|
+
return renderMediaInfo(result.data);
|
|
22738
|
+
case "mediaAsset":
|
|
22739
|
+
return renderMediaAsset(result.data);
|
|
22531
22740
|
case "dataBaseGlob":
|
|
22532
22741
|
return renderDataBaseGlob(result.data);
|
|
22533
22742
|
case "dataBaseRead":
|
|
@@ -22865,12 +23074,12 @@ function renderCommandIndex() {
|
|
|
22865
23074
|
`;
|
|
22866
23075
|
}
|
|
22867
23076
|
async function packageVersion() {
|
|
22868
|
-
if ("0.0.
|
|
22869
|
-
return "0.0.
|
|
23077
|
+
if ("0.0.10".length > 0) {
|
|
23078
|
+
return "0.0.10";
|
|
22870
23079
|
}
|
|
22871
23080
|
try {
|
|
22872
23081
|
const packageUrl = new URL("../package.json", import_meta.url);
|
|
22873
|
-
const pkg = JSON.parse(await (0,
|
|
23082
|
+
const pkg = JSON.parse(await (0, import_promises5.readFile)(packageUrl, "utf8"));
|
|
22874
23083
|
return pkg.version ?? "0.0.0";
|
|
22875
23084
|
} catch {
|
|
22876
23085
|
return "0.0.0";
|
|
@@ -23073,6 +23282,25 @@ ${url2}
|
|
|
23073
23282
|
poll: () => executeHttpCommand({ command: statusCommand, args: { jobId }, options: {} })
|
|
23074
23283
|
});
|
|
23075
23284
|
}
|
|
23285
|
+
if (commandKey(definition.path) === "ai jobs wait") {
|
|
23286
|
+
const statusCommand = findCommandDefinition(["ai", "jobs", "status"]);
|
|
23287
|
+
if (!statusCommand) throw new Error("AI job status command is not registered.");
|
|
23288
|
+
const jobId = args.jobId;
|
|
23289
|
+
return waitForAiJob({
|
|
23290
|
+
jobId,
|
|
23291
|
+
command: "spira ai jobs wait",
|
|
23292
|
+
timeoutMs: options.timeout,
|
|
23293
|
+
intervalMs: options.interval,
|
|
23294
|
+
onStatus: (status) => output.error(`AI job ${jobId}: ${status}
|
|
23295
|
+
`),
|
|
23296
|
+
poll: () => executeHttpCommand({ command: statusCommand, args: { jobId }, options: {} })
|
|
23297
|
+
});
|
|
23298
|
+
}
|
|
23299
|
+
if (commandKey(definition.path) === "media video" || commandKey(definition.path) === "media thumbnail") {
|
|
23300
|
+
const downloaded = await executeHttpCommand({ command: definition, args, options });
|
|
23301
|
+
if (typeof options.output !== "string" || options.output.length === 0) return downloaded;
|
|
23302
|
+
return saveMediaAsset(downloaded, options.output);
|
|
23303
|
+
}
|
|
23076
23304
|
return executeHttpCommand({ command: definition, args, options });
|
|
23077
23305
|
}
|
|
23078
23306
|
function addLeafCommand(root, definition, output) {
|
|
@@ -23098,7 +23326,7 @@ function addLeafCommand(root, definition, output) {
|
|
|
23098
23326
|
output.write(
|
|
23099
23327
|
renderResult(result, {
|
|
23100
23328
|
json: Boolean(options.json),
|
|
23101
|
-
compactAiJobWait: commandKey(definition.path) === "ai jobs create" && options.wait === true
|
|
23329
|
+
compactAiJobWait: commandKey(definition.path) === "ai jobs create" && options.wait === true || commandKey(definition.path) === "ai jobs wait"
|
|
23102
23330
|
})
|
|
23103
23331
|
);
|
|
23104
23332
|
if (result.status === "error") process.exitCode = 1;
|