@spira-lab/cli 0.0.6 → 0.0.7

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.
Files changed (2) hide show
  1. package/dist/index.cjs +118 -101
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -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",
@@ -18083,16 +18084,13 @@ var cliResultSchema = external_exports.object({
18083
18084
  // src/ai/model-docs.ts
18084
18085
  var cliAiModelDocs = [
18085
18086
  {
18086
- "id": "higgsfield-ai/soul/standard",
18087
+ "id": "ai-influencer-image",
18087
18088
  "name": "AI Influencer",
18088
18089
  "mediaType": "image",
18089
18090
  "description": "AI Influencer image generation model.",
18090
18091
  "capabilities": [
18091
18092
  "text-to-image"
18092
18093
  ],
18093
- "providers": [
18094
- "image-higgsfield"
18095
- ],
18096
18094
  "variants": [
18097
18095
  {
18098
18096
  "key": "default",
@@ -18155,9 +18153,6 @@ var cliAiModelDocs = [
18155
18153
  "text-to-image",
18156
18154
  "image-to-image"
18157
18155
  ],
18158
- "providers": [
18159
- "socheap-media"
18160
- ],
18161
18156
  "variants": [
18162
18157
  {
18163
18158
  "key": "1k",
@@ -18248,9 +18243,6 @@ var cliAiModelDocs = [
18248
18243
  "text-to-image",
18249
18244
  "image-to-image"
18250
18245
  ],
18251
- "providers": [
18252
- "socheap-media"
18253
- ],
18254
18246
  "variants": [
18255
18247
  {
18256
18248
  "key": "standard",
@@ -18331,9 +18323,6 @@ var cliAiModelDocs = [
18331
18323
  "text-to-image",
18332
18324
  "image-to-image"
18333
18325
  ],
18334
- "providers": [
18335
- "socheap-media"
18336
- ],
18337
18326
  "variants": [
18338
18327
  {
18339
18328
  "key": "1k",
@@ -18430,9 +18419,6 @@ var cliAiModelDocs = [
18430
18419
  "text-to-image",
18431
18420
  "image-to-image"
18432
18421
  ],
18433
- "providers": [
18434
- "image-kie"
18435
- ],
18436
18422
  "variants": [
18437
18423
  {
18438
18424
  "key": "default",
@@ -18505,9 +18491,6 @@ var cliAiModelDocs = [
18505
18491
  "text-to-image",
18506
18492
  "image-to-image"
18507
18493
  ],
18508
- "providers": [
18509
- "socheap-media"
18510
- ],
18511
18494
  "variants": [
18512
18495
  {
18513
18496
  "key": "1k",
@@ -18598,9 +18581,6 @@ var cliAiModelDocs = [
18598
18581
  "text-to-video",
18599
18582
  "image-to-video"
18600
18583
  ],
18601
- "providers": [
18602
- "socheap-media"
18603
- ],
18604
18584
  "variants": [
18605
18585
  {
18606
18586
  "key": "4s-720p",
@@ -18860,9 +18840,6 @@ var cliAiModelDocs = [
18860
18840
  "text-to-video",
18861
18841
  "image-to-video"
18862
18842
  ],
18863
- "providers": [
18864
- "socheap-media"
18865
- ],
18866
18843
  "variants": [
18867
18844
  {
18868
18845
  "key": "480p",
@@ -18974,9 +18951,6 @@ var cliAiModelDocs = [
18974
18951
  "text-to-video",
18975
18952
  "image-to-video"
18976
18953
  ],
18977
- "providers": [
18978
- "hailuo-comfly"
18979
- ],
18980
18954
  "variants": [
18981
18955
  {
18982
18956
  "key": "default",
@@ -19030,8 +19004,8 @@ var cliAiModelDocs = [
19030
19004
  }
19031
19005
  ],
19032
19006
  "notes": [
19033
- "referenceVideo and generationType are not supported by the Hailuo Comfly provider.",
19034
- "duration=10 with resolution=1080P is rejected by the provider validator."
19007
+ "referenceVideo and generationType are not supported for this model.",
19008
+ "duration=10 with resolution=1080P is not supported."
19035
19009
  ]
19036
19010
  },
19037
19011
  {
@@ -19043,9 +19017,6 @@ var cliAiModelDocs = [
19043
19017
  "text-to-video",
19044
19018
  "image-to-video"
19045
19019
  ],
19046
- "providers": [
19047
- "socheap-media"
19048
- ],
19049
19020
  "variants": [
19050
19021
  {
19051
19022
  "key": "720p",
@@ -19169,9 +19140,6 @@ var cliAiModelDocs = [
19169
19140
  "capabilities": [
19170
19141
  "image-to-video"
19171
19142
  ],
19172
- "providers": [
19173
- "fal-ai"
19174
- ],
19175
19143
  "variants": [
19176
19144
  {
19177
19145
  "key": "default",
@@ -19286,9 +19254,6 @@ var cliAiModelDocs = [
19286
19254
  "text-to-video",
19287
19255
  "image-to-video"
19288
19256
  ],
19289
- "providers": [
19290
- "socheap-media"
19291
- ],
19292
19257
  "variants": [
19293
19258
  {
19294
19259
  "key": "standard-no-sound",
@@ -19431,7 +19396,7 @@ var cliAiModelDocs = [
19431
19396
  ],
19432
19397
  "notes": [
19433
19398
  "generation type is derived from referenceImage count: 0 text, 1 reference, 2 first/last frames.",
19434
- "referenceVideo, negativePrompt, and elements are not supported on the SoCheap Kling 3.0 path.",
19399
+ "referenceVideo, negativePrompt, and elements are not supported for Kling 3.0.",
19435
19400
  "Use mode=4K for 4K generation; do not pass resolution."
19436
19401
  ]
19437
19402
  },
@@ -19443,9 +19408,6 @@ var cliAiModelDocs = [
19443
19408
  "capabilities": [
19444
19409
  "image-to-video"
19445
19410
  ],
19446
- "providers": [
19447
- "socheap-media"
19448
- ],
19449
19411
  "variants": [
19450
19412
  {
19451
19413
  "key": "standard",
@@ -19566,9 +19528,6 @@ var cliAiModelDocs = [
19566
19528
  "text-to-video",
19567
19529
  "image-to-video"
19568
19530
  ],
19569
- "providers": [
19570
- "socheap-media"
19571
- ],
19572
19531
  "variants": [
19573
19532
  {
19574
19533
  "key": "standard-480p-no-sound",
@@ -19730,9 +19689,6 @@ var cliAiModelDocs = [
19730
19689
  "text-to-video",
19731
19690
  "image-to-video"
19732
19691
  ],
19733
- "providers": [
19734
- "socheap-media"
19735
- ],
19736
19692
  "variants": [
19737
19693
  {
19738
19694
  "key": "standard-480p",
@@ -20016,9 +19972,6 @@ var cliAiModelDocs = [
20016
19972
  "text-to-video",
20017
19973
  "image-to-video"
20018
19974
  ],
20019
- "providers": [
20020
- "socheap-media"
20021
- ],
20022
19975
  "variants": [
20023
19976
  {
20024
19977
  "key": "fast-720p",
@@ -20133,8 +20086,7 @@ var cliAiModelDocs = [
20133
20086
  "description": "Output aspect ratio.",
20134
20087
  "values": [
20135
20088
  "16:9",
20136
- "9:16",
20137
- "1:1"
20089
+ "9:16"
20138
20090
  ],
20139
20091
  "defaultValue": "16:9"
20140
20092
  },
@@ -20170,14 +20122,9 @@ var cliAiModelDocs = [
20170
20122
  "description": "Whether the generated video should be extendable when supported."
20171
20123
  },
20172
20124
  {
20173
- "name": "extendFromTaskId",
20174
- "type": "string",
20175
- "description": "Provider task id of an existing Veo generation to extend."
20176
- },
20177
- {
20178
- "name": "extendProviderId",
20125
+ "name": "extendFromJobId",
20179
20126
  "type": "string",
20180
- "description": "Original provider id for extension lineage. Must be socheap-media for this CLI path."
20127
+ "description": "Spira job ID of an extendable Veo generation owned by the current user."
20181
20128
  }
20182
20129
  ],
20183
20130
  "examples": [
@@ -20193,10 +20140,11 @@ var cliAiModelDocs = [
20193
20140
  }
20194
20141
  ],
20195
20142
  "notes": [
20143
+ "Veo 3.1 supports 16:9 and 9:16 output aspect ratios.",
20196
20144
  "referenceVideo and referenceAudio are not supported.",
20197
20145
  "image_to_video is not a supported generationType; use reference_to_video with one or more referenceImage URLs.",
20198
20146
  "first_last_frames_to_video requires exactly 2 referenceImage URLs.",
20199
- "extendFromTaskId requires extendProviderId=socheap-media and resolution=720p."
20147
+ "extendFromJobId requires resolution=720p."
20200
20148
  ]
20201
20149
  },
20202
20150
  {
@@ -20205,7 +20153,6 @@ var cliAiModelDocs = [
20205
20153
  mediaType: "audio",
20206
20154
  description: "Suno music generation with simple prompt and custom song modes.",
20207
20155
  capabilities: ["text-to-audio"],
20208
- providers: ["socheap-media"],
20209
20156
  variants: [
20210
20157
  {
20211
20158
  key: "v5_5",
@@ -20270,7 +20217,6 @@ var cliAiModelDocs = [
20270
20217
  mediaType: "audio",
20271
20218
  description: "ElevenLabs prompt-to-music generation.",
20272
20219
  capabilities: ["text-to-audio"],
20273
- providers: ["elevenlabs-audio"],
20274
20220
  variants: [
20275
20221
  {
20276
20222
  key: "default",
@@ -20310,7 +20256,6 @@ var cliAiModelDocs = [
20310
20256
  mediaType: "audio",
20311
20257
  description: "ElevenLabs sound-effect generation from an English prompt.",
20312
20258
  capabilities: ["text-to-audio"],
20313
- providers: ["elevenlabs-audio"],
20314
20259
  variants: [
20315
20260
  {
20316
20261
  key: "default",
@@ -20350,7 +20295,6 @@ var cliAiModelDocs = [
20350
20295
  mediaType: "audio",
20351
20296
  description: "ElevenLabs text-to-speech using an existing voice ID.",
20352
20297
  capabilities: ["text-to-speech"],
20353
- providers: ["elevenlabs-audio"],
20354
20298
  variants: [
20355
20299
  {
20356
20300
  key: "multilingual-v2-v3",
@@ -20398,7 +20342,6 @@ var cliAiModelDocs = [
20398
20342
  mediaType: "audio",
20399
20343
  description: "Text-to-speech using either an existing voice ID or one reference audio URL.",
20400
20344
  capabilities: ["text-to-speech", "audio-to-speech"],
20401
- providers: ["elevenlabs-audio"],
20402
20345
  variants: [
20403
20346
  {
20404
20347
  key: "voice-clone-tts",
@@ -20462,6 +20405,7 @@ function getCliAiModelDoc(modelId) {
20462
20405
  }
20463
20406
 
20464
20407
  // src/ai/wait.ts
20408
+ var DEFAULT_WAIT_COMMAND = "spira ai jobs create";
20465
20409
  function wait(durationMs) {
20466
20410
  return new Promise((resolve) => setTimeout(resolve, durationMs));
20467
20411
  }
@@ -20481,27 +20425,27 @@ function providerError(result) {
20481
20425
  }
20482
20426
  return void 0;
20483
20427
  }
20484
- function completedResult(result) {
20428
+ function completedResult(result, command) {
20485
20429
  return cliResultSchema.parse({
20486
20430
  ...result,
20487
20431
  resultType: "aiJobStatus",
20488
- command: "spira ai jobs create",
20432
+ command,
20489
20433
  nextActions: []
20490
20434
  });
20491
20435
  }
20492
- function pollingError(result) {
20436
+ function pollingError(result, command) {
20493
20437
  return cliResultSchema.parse({
20494
20438
  ...result,
20495
- command: "spira ai jobs create"
20439
+ command
20496
20440
  });
20497
20441
  }
20498
- function terminalError(result, jobId, status) {
20442
+ function terminalError(result, jobId, status, command) {
20499
20443
  const message = providerError(result) ?? `AI job ${jobId} ended with status ${status}.`;
20500
20444
  return cliResultSchema.parse({
20501
20445
  ...result,
20502
20446
  status: "error",
20503
20447
  resultType: "aiJobStatus",
20504
- command: "spira ai jobs create",
20448
+ command,
20505
20449
  error: {
20506
20450
  code: status === "FAILED" ? "PROVIDER_FAILED" : "ASYNC_STATE",
20507
20451
  message,
@@ -20511,12 +20455,12 @@ function terminalError(result, jobId, status) {
20511
20455
  }
20512
20456
  });
20513
20457
  }
20514
- function timeoutResult(jobId, timeoutMs, lastResult) {
20458
+ function timeoutResult(jobId, timeoutMs, command, lastResult) {
20515
20459
  return cliResultSchema.parse({
20516
20460
  object: "cli.result",
20517
20461
  status: "error",
20518
20462
  resultType: "aiJobStatus",
20519
- command: "spira ai jobs create",
20463
+ command,
20520
20464
  data: lastResult?.data,
20521
20465
  ids: { ...lastResult?.ids, jobId },
20522
20466
  error: {
@@ -20524,16 +20468,19 @@ function timeoutResult(jobId, timeoutMs, lastResult) {
20524
20468
  message: `AI job ${jobId} did not finish within ${timeoutMs}ms.`,
20525
20469
  resource: { type: "aiJob", id: jobId },
20526
20470
  retryable: true,
20527
- nextActions: [{ label: "Show job status", command: `spira ai jobs status ${jobId}` }]
20471
+ nextActions: [
20472
+ { label: "Keep waiting", command: `spira ai jobs wait ${jobId}` },
20473
+ { label: "Show job status", command: `spira ai jobs status ${jobId}` }
20474
+ ]
20528
20475
  }
20529
20476
  });
20530
20477
  }
20531
- function contractError(result, jobId) {
20478
+ function contractError(result, jobId, command) {
20532
20479
  return cliResultSchema.parse({
20533
20480
  ...result,
20534
20481
  status: "error",
20535
20482
  resultType: "aiJobStatus",
20536
- command: "spira ai jobs create",
20483
+ command,
20537
20484
  error: {
20538
20485
  code: "CONTRACT_MISMATCH",
20539
20486
  message: `AI job ${jobId} status response did not contain a recognized status.`,
@@ -20546,6 +20493,7 @@ function contractError(result, jobId) {
20546
20493
  async function waitForAiJob(input) {
20547
20494
  const now = input.now ?? Date.now;
20548
20495
  const sleep = input.sleep ?? wait;
20496
+ const command = input.command ?? DEFAULT_WAIT_COMMAND;
20549
20497
  const deadline = now() + input.timeoutMs;
20550
20498
  let lastResult;
20551
20499
  let lastStatus;
@@ -20553,19 +20501,19 @@ async function waitForAiJob(input) {
20553
20501
  const result = await input.poll();
20554
20502
  lastResult = result;
20555
20503
  if (result.status === "error") {
20556
- if (!result.error?.retryable) return pollingError(result);
20504
+ if (!result.error?.retryable) return pollingError(result, command);
20557
20505
  } else {
20558
20506
  const status = jobStatus(result);
20559
20507
  if (status && status !== lastStatus) {
20560
20508
  input.onStatus?.(status);
20561
20509
  lastStatus = status;
20562
20510
  }
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);
20511
+ if (status === "COMPLETED") return completedResult(result, command);
20512
+ if (status === "FAILED" || status === "CANCELED") return terminalError(result, input.jobId, status, command);
20513
+ if (status !== "QUEUED" && status !== "RUNNING") return contractError(result, input.jobId, command);
20566
20514
  }
20567
20515
  const remainingMs = deadline - now();
20568
- if (remainingMs <= 0) return timeoutResult(input.jobId, input.timeoutMs, lastResult);
20516
+ if (remainingMs <= 0) return timeoutResult(input.jobId, input.timeoutMs, command, lastResult);
20569
20517
  await sleep(Math.min(input.intervalMs, remainingMs));
20570
20518
  }
20571
20519
  }
@@ -20895,7 +20843,33 @@ var commandDefinitions = [
20895
20843
  },
20896
20844
  resultType: "aiJobStatus",
20897
20845
  examples: ["spira ai jobs status job_123"],
20898
- relatedCommands: []
20846
+ relatedCommands: ["spira ai jobs wait <jobId>"]
20847
+ },
20848
+ {
20849
+ path: ["ai", "jobs", "wait"],
20850
+ purpose: "Block until an AI job finishes, then print the final result.",
20851
+ arguments: [{ name: "jobId", description: "AI job ID.", required: true }],
20852
+ options: [
20853
+ {
20854
+ name: "timeout",
20855
+ flag: "--timeout <duration>",
20856
+ description: "Maximum wait duration.",
20857
+ type: "duration",
20858
+ defaultValue: 6e5,
20859
+ minimum: 1e3
20860
+ },
20861
+ {
20862
+ name: "interval",
20863
+ flag: "--interval <duration>",
20864
+ description: "Polling interval while waiting.",
20865
+ type: "duration",
20866
+ defaultValue: 3e3,
20867
+ minimum: 1e3
20868
+ }
20869
+ ],
20870
+ resultType: "aiJobStatus",
20871
+ examples: ["spira ai jobs wait job_123", "spira ai jobs wait job_123 --timeout 20m --interval 5s"],
20872
+ relatedCommands: ["spira ai jobs status <jobId>", "spira ai jobs create"]
20899
20873
  },
20900
20874
  {
20901
20875
  path: ["ai", "models", "list"],
@@ -20921,6 +20895,21 @@ var commandDefinitions = [
20921
20895
  examples: ["spira ai models get nano-banana-2", "spira ai models get seedance-2.0"],
20922
20896
  relatedCommands: ["spira ai jobs create"]
20923
20897
  },
20898
+ {
20899
+ path: ["ai", "models", "providers"],
20900
+ purpose: "List provider routing IDs for AI models; requires admin.access.",
20901
+ permissions: ["admin.access"],
20902
+ arguments: [{ name: "modelId", description: "Optional AI model ID.", required: false }],
20903
+ http: {
20904
+ method: "GET",
20905
+ path: "/api/cli/v1/ai/models/providers",
20906
+ authRequired: true,
20907
+ query: [{ name: "modelId", source: "argument", from: "modelId" }]
20908
+ },
20909
+ resultType: "aiModelProviderList",
20910
+ examples: ["spira ai models providers", "spira ai models providers veo3.1 --json"],
20911
+ relatedCommands: ["spira ai models list", "spira ai jobs create"]
20912
+ },
20924
20913
  {
20925
20914
  path: ["ai", "jobs", "create"],
20926
20915
  purpose: "Create an AI image, video, or audio generation job.",
@@ -20929,9 +20918,8 @@ var commandDefinitions = [
20929
20918
  {
20930
20919
  name: "providerId",
20931
20920
  flag: "--provider-id <providerId>",
20932
- description: "Advanced routing override.",
20933
- type: "string",
20934
- hidden: true
20921
+ description: "Explicit provider routing override; requires admin.access.",
20922
+ type: "string"
20935
20923
  },
20936
20924
  {
20937
20925
  name: "inputsJson",
@@ -20978,7 +20966,12 @@ var commandDefinitions = [
20978
20966
  `spira ai jobs create --model-id nano-banana-2 --inputs-json '{"prompt":"A product photo"}'`,
20979
20967
  `spira ai jobs create --model-id nano-banana-2 --inputs-json '{"prompt":"A product photo"}' --wait`
20980
20968
  ],
20981
- relatedCommands: ["spira ai jobs status <jobId>", "spira ai models get <modelId>", "spira credits balance"]
20969
+ relatedCommands: [
20970
+ "spira ai jobs wait <jobId>",
20971
+ "spira ai jobs status <jobId>",
20972
+ "spira ai models get <modelId>",
20973
+ "spira credits balance"
20974
+ ]
20982
20975
  },
20983
20976
  {
20984
20977
  path: ["workflows", "list"],
@@ -22110,20 +22103,18 @@ function dataArray(data) {
22110
22103
  }
22111
22104
  function renderRows(rows, columns) {
22112
22105
  if (rows.length === 0) return "No results.\n";
22113
- const records = rows.map((row) => row && typeof row === "object" ? row : { value: row });
22106
+ const records = rows.map(
22107
+ (row) => row && typeof row === "object" ? row : { value: row }
22108
+ );
22114
22109
  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
- )
22110
+ (column) => Math.min(42, Math.max(column.length, ...records.map((record2) => valueToText(record2[column]).slice(0, 42).length)))
22122
22111
  );
22123
22112
  const header = columns.map((column, index) => column.padEnd(widths[index])).join(" ");
22124
22113
  const divider = widths.map((width) => "-".repeat(width)).join(" ");
22125
22114
  const body = records.map(
22126
- (record2) => columns.map((column, index) => valueToText(record2[column]).replace(/\s+/g, " ").slice(0, widths[index]).padEnd(widths[index])).join(" ")
22115
+ (record2) => columns.map(
22116
+ (column, index) => valueToText(record2[column]).replace(/\s+/g, " ").slice(0, widths[index]).padEnd(widths[index])
22117
+ ).join(" ")
22127
22118
  ).join("\n");
22128
22119
  return `${header}
22129
22120
  ${divider}
@@ -22293,6 +22284,9 @@ function renderAiJobStatus(data) {
22293
22284
  function renderAiModelList(data) {
22294
22285
  return renderRows(dataArray(data), ["id", "mediaType", "creditOptions", "name", "capabilities"]);
22295
22286
  }
22287
+ function renderAiModelProviderList(data) {
22288
+ return renderRows(dataArray(data), ["modelId", "name", "providers", "hidden"]);
22289
+ }
22296
22290
  function renderAiModelDetail(data) {
22297
22291
  const record2 = objectRecord2(data);
22298
22292
  const fields = Array.isArray(record2.fields) ? record2.fields.map(objectRecord2) : [];
@@ -22512,6 +22506,8 @@ function renderSuccess(result) {
22512
22506
  return renderAiModelList(result.data);
22513
22507
  case "aiModelDetail":
22514
22508
  return renderAiModelDetail(result.data);
22509
+ case "aiModelProviderList":
22510
+ return renderAiModelProviderList(result.data);
22515
22511
  case "workflowList":
22516
22512
  return renderRows(dataArray(result.data), ["id", "name", "creatorName", "nodeTypes", "updatedAt"]);
22517
22513
  case "workflowNodeCatalog":
@@ -22527,7 +22523,14 @@ function renderSuccess(result) {
22527
22523
  case "scheduleOccurrenceList":
22528
22524
  return renderRows(dataArray(result.data), ["id", "scheduleId", "scheduledAt", "status", "executorType"]);
22529
22525
  case "scheduleExecutionList":
22530
- return renderRows(dataArray(result.data), ["id", "scheduleId", "status", "originalScheduledAt", "startedAt", "finishedAt"]);
22526
+ return renderRows(dataArray(result.data), [
22527
+ "id",
22528
+ "scheduleId",
22529
+ "status",
22530
+ "originalScheduledAt",
22531
+ "startedAt",
22532
+ "finishedAt"
22533
+ ]);
22531
22534
  case "dataBaseGlob":
22532
22535
  return renderDataBaseGlob(result.data);
22533
22536
  case "dataBaseRead":
@@ -22865,8 +22868,8 @@ function renderCommandIndex() {
22865
22868
  `;
22866
22869
  }
22867
22870
  async function packageVersion() {
22868
- if ("0.0.6".length > 0) {
22869
- return "0.0.6";
22871
+ if ("0.0.7".length > 0) {
22872
+ return "0.0.7";
22870
22873
  }
22871
22874
  try {
22872
22875
  const packageUrl = new URL("../package.json", import_meta.url);
@@ -23069,6 +23072,20 @@ ${url2}
23069
23072
  timeoutMs: options.waitTimeout,
23070
23073
  intervalMs: options.waitInterval,
23071
23074
  onStatus: (status) => output.error(`AI job ${jobId}: ${status}
23075
+ `),
23076
+ poll: () => executeHttpCommand({ command: statusCommand, args: { jobId }, options: {} })
23077
+ });
23078
+ }
23079
+ if (commandKey(definition.path) === "ai jobs wait") {
23080
+ const statusCommand = findCommandDefinition(["ai", "jobs", "status"]);
23081
+ if (!statusCommand) throw new Error("AI job status command is not registered.");
23082
+ const jobId = args.jobId;
23083
+ return waitForAiJob({
23084
+ jobId,
23085
+ command: "spira ai jobs wait",
23086
+ timeoutMs: options.timeout,
23087
+ intervalMs: options.interval,
23088
+ onStatus: (status) => output.error(`AI job ${jobId}: ${status}
23072
23089
  `),
23073
23090
  poll: () => executeHttpCommand({ command: statusCommand, args: { jobId }, options: {} })
23074
23091
  });
@@ -23098,7 +23115,7 @@ function addLeafCommand(root, definition, output) {
23098
23115
  output.write(
23099
23116
  renderResult(result, {
23100
23117
  json: Boolean(options.json),
23101
- compactAiJobWait: commandKey(definition.path) === "ai jobs create" && options.wait === true
23118
+ compactAiJobWait: commandKey(definition.path) === "ai jobs create" && options.wait === true || commandKey(definition.path) === "ai jobs wait"
23102
23119
  })
23103
23120
  );
23104
23121
  if (result.status === "error") process.exitCode = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spira-lab/cli",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Command line client for Spira.",
5
5
  "type": "module",
6
6
  "bin": {