@spira-lab/cli 0.0.4 → 0.0.6
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 +6 -0
- package/dist/index.cjs +887 -50
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,8 +14,14 @@ npm install -g @spira-lab/cli
|
|
|
14
14
|
spira --help
|
|
15
15
|
spira auth login
|
|
16
16
|
spira ai models list
|
|
17
|
+
spira ai models list --media-type audio
|
|
18
|
+
spira ai jobs create --model-id nano-banana-2 --inputs-json '{"prompt":"A product photo"}' --wait
|
|
19
|
+
spira ai jobs create --model-id elevenlabs-music-generation --inputs-json '{"prompt":"A short ambient cue","duration":"10"}' --wait
|
|
20
|
+
spira workflows node-catalog --json
|
|
17
21
|
spira workflows create --name "Product launch workflow"
|
|
18
22
|
spira workflows update <workflowId> --workflow-definition-json '{"nodes":[],"edges":[]}'
|
|
19
23
|
```
|
|
20
24
|
|
|
21
25
|
Use `spira --json` when you need machine-readable output.
|
|
26
|
+
|
|
27
|
+
`ai jobs create --wait` prints the job ID and status changes to stderr while it waits. Its final stdout is a compact result containing the job ID, model, status, result type, and result URLs. Use `spira --json ai jobs status <jobId>` when you need the full job and provider execution details.
|
package/dist/index.cjs
CHANGED
|
@@ -18049,6 +18049,7 @@ var cliResultTypeSchema = external_exports.enum([
|
|
|
18049
18049
|
"aiModelList",
|
|
18050
18050
|
"aiModelDetail",
|
|
18051
18051
|
"workflowList",
|
|
18052
|
+
"workflowNodeCatalog",
|
|
18052
18053
|
"workflowDetail",
|
|
18053
18054
|
"workflowRunList",
|
|
18054
18055
|
"workflowRunDetail",
|
|
@@ -18058,6 +18059,10 @@ var cliResultTypeSchema = external_exports.enum([
|
|
|
18058
18059
|
"scheduleOccurrenceList",
|
|
18059
18060
|
"scheduleExecutionList",
|
|
18060
18061
|
"scheduleAction",
|
|
18062
|
+
"dataBaseGlob",
|
|
18063
|
+
"dataBaseRead",
|
|
18064
|
+
"dataBaseGrep",
|
|
18065
|
+
"dataBaseSearch",
|
|
18061
18066
|
"generic"
|
|
18062
18067
|
]);
|
|
18063
18068
|
var cliResultSchema = external_exports.object({
|
|
@@ -20193,6 +20198,260 @@ var cliAiModelDocs = [
|
|
|
20193
20198
|
"first_last_frames_to_video requires exactly 2 referenceImage URLs.",
|
|
20194
20199
|
"extendFromTaskId requires extendProviderId=socheap-media and resolution=720p."
|
|
20195
20200
|
]
|
|
20201
|
+
},
|
|
20202
|
+
{
|
|
20203
|
+
id: "suno-music",
|
|
20204
|
+
name: "Suno Music",
|
|
20205
|
+
mediaType: "audio",
|
|
20206
|
+
description: "Suno music generation with simple prompt and custom song modes.",
|
|
20207
|
+
capabilities: ["text-to-audio"],
|
|
20208
|
+
providers: ["socheap-media"],
|
|
20209
|
+
variants: [
|
|
20210
|
+
{
|
|
20211
|
+
key: "v5_5",
|
|
20212
|
+
display: "v5_5",
|
|
20213
|
+
conditions: {},
|
|
20214
|
+
billingUnit: "per_generation",
|
|
20215
|
+
creditsPerUnit: 6.6
|
|
20216
|
+
}
|
|
20217
|
+
],
|
|
20218
|
+
fields: [
|
|
20219
|
+
{
|
|
20220
|
+
name: "prompt",
|
|
20221
|
+
type: "string",
|
|
20222
|
+
description: "Song description in simple mode, or lyrics in custom vocal mode."
|
|
20223
|
+
},
|
|
20224
|
+
{
|
|
20225
|
+
name: "mode",
|
|
20226
|
+
type: "string",
|
|
20227
|
+
description: "Suno model version.",
|
|
20228
|
+
values: ["v5_5"],
|
|
20229
|
+
defaultValue: "v5_5"
|
|
20230
|
+
},
|
|
20231
|
+
{
|
|
20232
|
+
name: "customMode",
|
|
20233
|
+
type: "boolean",
|
|
20234
|
+
description: "Enable custom lyrics, style, and title inputs.",
|
|
20235
|
+
defaultValue: false
|
|
20236
|
+
},
|
|
20237
|
+
{
|
|
20238
|
+
name: "instrumental",
|
|
20239
|
+
type: "boolean",
|
|
20240
|
+
description: "Generate an instrumental track. Only used in custom mode.",
|
|
20241
|
+
defaultValue: false
|
|
20242
|
+
},
|
|
20243
|
+
{
|
|
20244
|
+
name: "style",
|
|
20245
|
+
type: "string",
|
|
20246
|
+
description: "Required music style in custom mode."
|
|
20247
|
+
},
|
|
20248
|
+
{
|
|
20249
|
+
name: "title",
|
|
20250
|
+
type: "string",
|
|
20251
|
+
description: "Required song title in custom mode."
|
|
20252
|
+
}
|
|
20253
|
+
],
|
|
20254
|
+
examples: [
|
|
20255
|
+
{
|
|
20256
|
+
label: "Generate music",
|
|
20257
|
+
inputs: {
|
|
20258
|
+
prompt: "A warm upbeat electronic instrumental for a product launch",
|
|
20259
|
+
mode: "v5_5"
|
|
20260
|
+
}
|
|
20261
|
+
}
|
|
20262
|
+
],
|
|
20263
|
+
notes: [
|
|
20264
|
+
"Simple mode requires prompt. Custom mode requires style and title; vocal custom mode also requires prompt as lyrics."
|
|
20265
|
+
]
|
|
20266
|
+
},
|
|
20267
|
+
{
|
|
20268
|
+
id: "elevenlabs-music-generation",
|
|
20269
|
+
name: "Eleven Music V1",
|
|
20270
|
+
mediaType: "audio",
|
|
20271
|
+
description: "ElevenLabs prompt-to-music generation.",
|
|
20272
|
+
capabilities: ["text-to-audio"],
|
|
20273
|
+
providers: ["elevenlabs-audio"],
|
|
20274
|
+
variants: [
|
|
20275
|
+
{
|
|
20276
|
+
key: "default",
|
|
20277
|
+
display: "Per second",
|
|
20278
|
+
conditions: {},
|
|
20279
|
+
billingUnit: "per_second",
|
|
20280
|
+
creditsPerUnit: 0.25
|
|
20281
|
+
}
|
|
20282
|
+
],
|
|
20283
|
+
fields: [
|
|
20284
|
+
{
|
|
20285
|
+
name: "prompt",
|
|
20286
|
+
type: "string",
|
|
20287
|
+
description: "Description of the music to generate.",
|
|
20288
|
+
required: true
|
|
20289
|
+
},
|
|
20290
|
+
{
|
|
20291
|
+
name: "duration",
|
|
20292
|
+
type: "string",
|
|
20293
|
+
description: "Whole number of seconds from 1 to 300.",
|
|
20294
|
+
defaultValue: "10"
|
|
20295
|
+
}
|
|
20296
|
+
],
|
|
20297
|
+
examples: [
|
|
20298
|
+
{
|
|
20299
|
+
label: "Generate music",
|
|
20300
|
+
inputs: {
|
|
20301
|
+
prompt: "A calm ambient piano cue with a gentle ending",
|
|
20302
|
+
duration: "10"
|
|
20303
|
+
}
|
|
20304
|
+
}
|
|
20305
|
+
]
|
|
20306
|
+
},
|
|
20307
|
+
{
|
|
20308
|
+
id: "elevenlabs-sound-effects",
|
|
20309
|
+
name: "Eleven Text To Sound V2",
|
|
20310
|
+
mediaType: "audio",
|
|
20311
|
+
description: "ElevenLabs sound-effect generation from an English prompt.",
|
|
20312
|
+
capabilities: ["text-to-audio"],
|
|
20313
|
+
providers: ["elevenlabs-audio"],
|
|
20314
|
+
variants: [
|
|
20315
|
+
{
|
|
20316
|
+
key: "default",
|
|
20317
|
+
display: "Per generation",
|
|
20318
|
+
conditions: {},
|
|
20319
|
+
billingUnit: "per_generation",
|
|
20320
|
+
creditsPerUnit: 12
|
|
20321
|
+
}
|
|
20322
|
+
],
|
|
20323
|
+
fields: [
|
|
20324
|
+
{
|
|
20325
|
+
name: "prompt",
|
|
20326
|
+
type: "string",
|
|
20327
|
+
description: "English description of the sound effect.",
|
|
20328
|
+
required: true
|
|
20329
|
+
},
|
|
20330
|
+
{
|
|
20331
|
+
name: "duration",
|
|
20332
|
+
type: "string",
|
|
20333
|
+
description: "Duration in seconds from 0.5 to 30.",
|
|
20334
|
+
defaultValue: "5"
|
|
20335
|
+
}
|
|
20336
|
+
],
|
|
20337
|
+
examples: [
|
|
20338
|
+
{
|
|
20339
|
+
label: "Generate a sound effect",
|
|
20340
|
+
inputs: {
|
|
20341
|
+
prompt: "A soft camera shutter in a quiet studio",
|
|
20342
|
+
duration: "2"
|
|
20343
|
+
}
|
|
20344
|
+
}
|
|
20345
|
+
]
|
|
20346
|
+
},
|
|
20347
|
+
{
|
|
20348
|
+
id: "elevenlabs-text-to-speech",
|
|
20349
|
+
name: "ElevenLabs Text to Speech",
|
|
20350
|
+
mediaType: "audio",
|
|
20351
|
+
description: "ElevenLabs text-to-speech using an existing voice ID.",
|
|
20352
|
+
capabilities: ["text-to-speech"],
|
|
20353
|
+
providers: ["elevenlabs-audio"],
|
|
20354
|
+
variants: [
|
|
20355
|
+
{
|
|
20356
|
+
key: "multilingual-v2-v3",
|
|
20357
|
+
display: "Per character",
|
|
20358
|
+
conditions: {},
|
|
20359
|
+
billingUnit: "per_character",
|
|
20360
|
+
creditsPerUnit: 0.01
|
|
20361
|
+
}
|
|
20362
|
+
],
|
|
20363
|
+
fields: [
|
|
20364
|
+
{
|
|
20365
|
+
name: "text",
|
|
20366
|
+
type: "string",
|
|
20367
|
+
description: "Text to speak.",
|
|
20368
|
+
required: true
|
|
20369
|
+
},
|
|
20370
|
+
{
|
|
20371
|
+
name: "voiceId",
|
|
20372
|
+
type: "string",
|
|
20373
|
+
description: "Existing ElevenLabs voice ID.",
|
|
20374
|
+
required: true
|
|
20375
|
+
},
|
|
20376
|
+
{
|
|
20377
|
+
name: "providerModelId",
|
|
20378
|
+
type: "string",
|
|
20379
|
+
description: "ElevenLabs speech model.",
|
|
20380
|
+
values: ["eleven_multilingual_v2", "eleven_v3"],
|
|
20381
|
+
defaultValue: "eleven_multilingual_v2"
|
|
20382
|
+
}
|
|
20383
|
+
],
|
|
20384
|
+
examples: [
|
|
20385
|
+
{
|
|
20386
|
+
label: "Generate speech",
|
|
20387
|
+
inputs: {
|
|
20388
|
+
text: "Welcome to Spira.",
|
|
20389
|
+
voiceId: "<voice-id>",
|
|
20390
|
+
providerModelId: "eleven_multilingual_v2"
|
|
20391
|
+
}
|
|
20392
|
+
}
|
|
20393
|
+
]
|
|
20394
|
+
},
|
|
20395
|
+
{
|
|
20396
|
+
id: "elevenlabs-voice-clone-tts",
|
|
20397
|
+
name: "ElevenLabs Voice TTS",
|
|
20398
|
+
mediaType: "audio",
|
|
20399
|
+
description: "Text-to-speech using either an existing voice ID or one reference audio URL.",
|
|
20400
|
+
capabilities: ["text-to-speech", "audio-to-speech"],
|
|
20401
|
+
providers: ["elevenlabs-audio"],
|
|
20402
|
+
variants: [
|
|
20403
|
+
{
|
|
20404
|
+
key: "voice-clone-tts",
|
|
20405
|
+
display: "Per character",
|
|
20406
|
+
conditions: {},
|
|
20407
|
+
billingUnit: "per_character",
|
|
20408
|
+
creditsPerUnit: 0.01
|
|
20409
|
+
}
|
|
20410
|
+
],
|
|
20411
|
+
fields: [
|
|
20412
|
+
{
|
|
20413
|
+
name: "text",
|
|
20414
|
+
type: "string",
|
|
20415
|
+
description: "Text to speak.",
|
|
20416
|
+
required: true
|
|
20417
|
+
},
|
|
20418
|
+
{
|
|
20419
|
+
name: "voiceId",
|
|
20420
|
+
type: "string",
|
|
20421
|
+
description: "Existing ElevenLabs voice ID. Do not combine with referenceAudio."
|
|
20422
|
+
},
|
|
20423
|
+
{
|
|
20424
|
+
name: "referenceAudio",
|
|
20425
|
+
type: "string[]",
|
|
20426
|
+
description: "Exactly one audio URL when cloning a voice. Do not combine with voiceId."
|
|
20427
|
+
},
|
|
20428
|
+
{
|
|
20429
|
+
name: "providerModelId",
|
|
20430
|
+
type: "string",
|
|
20431
|
+
description: "ElevenLabs speech model.",
|
|
20432
|
+
values: ["eleven_multilingual_v2", "eleven_v3"],
|
|
20433
|
+
defaultValue: "eleven_multilingual_v2"
|
|
20434
|
+
},
|
|
20435
|
+
{
|
|
20436
|
+
name: "qualityMode",
|
|
20437
|
+
type: "string",
|
|
20438
|
+
description: "Voice delivery mode.",
|
|
20439
|
+
values: ["stable", "expressive"],
|
|
20440
|
+
defaultValue: "stable"
|
|
20441
|
+
}
|
|
20442
|
+
],
|
|
20443
|
+
examples: [
|
|
20444
|
+
{
|
|
20445
|
+
label: "Generate speech from a reference voice",
|
|
20446
|
+
inputs: {
|
|
20447
|
+
text: "Welcome to Spira.",
|
|
20448
|
+
referenceAudio: ["https://example.com/reference.mp3"],
|
|
20449
|
+
providerModelId: "eleven_multilingual_v2",
|
|
20450
|
+
qualityMode: "stable"
|
|
20451
|
+
}
|
|
20452
|
+
}
|
|
20453
|
+
],
|
|
20454
|
+
notes: ["Provide either voiceId or exactly one referenceAudio URL, never both."]
|
|
20196
20455
|
}
|
|
20197
20456
|
];
|
|
20198
20457
|
function listCliAiModelDocs(mediaType) {
|
|
@@ -20202,6 +20461,115 @@ function getCliAiModelDoc(modelId) {
|
|
|
20202
20461
|
return cliAiModelDocs.find((model) => model.id === modelId);
|
|
20203
20462
|
}
|
|
20204
20463
|
|
|
20464
|
+
// src/ai/wait.ts
|
|
20465
|
+
function wait(durationMs) {
|
|
20466
|
+
return new Promise((resolve) => setTimeout(resolve, durationMs));
|
|
20467
|
+
}
|
|
20468
|
+
function jobStatus(result) {
|
|
20469
|
+
if (!result.data || typeof result.data !== "object") return void 0;
|
|
20470
|
+
const status = result.data.status;
|
|
20471
|
+
return typeof status === "string" ? status : void 0;
|
|
20472
|
+
}
|
|
20473
|
+
function providerError(result) {
|
|
20474
|
+
if (!result.data || typeof result.data !== "object") return void 0;
|
|
20475
|
+
const latestExecution = result.data.latestExecution;
|
|
20476
|
+
if (!latestExecution || typeof latestExecution !== "object") return void 0;
|
|
20477
|
+
const error51 = latestExecution.error;
|
|
20478
|
+
if (typeof error51 === "string") return error51;
|
|
20479
|
+
if (error51 && typeof error51 === "object" && typeof error51.message === "string") {
|
|
20480
|
+
return error51.message;
|
|
20481
|
+
}
|
|
20482
|
+
return void 0;
|
|
20483
|
+
}
|
|
20484
|
+
function completedResult(result) {
|
|
20485
|
+
return cliResultSchema.parse({
|
|
20486
|
+
...result,
|
|
20487
|
+
resultType: "aiJobStatus",
|
|
20488
|
+
command: "spira ai jobs create",
|
|
20489
|
+
nextActions: []
|
|
20490
|
+
});
|
|
20491
|
+
}
|
|
20492
|
+
function pollingError(result) {
|
|
20493
|
+
return cliResultSchema.parse({
|
|
20494
|
+
...result,
|
|
20495
|
+
command: "spira ai jobs create"
|
|
20496
|
+
});
|
|
20497
|
+
}
|
|
20498
|
+
function terminalError(result, jobId, status) {
|
|
20499
|
+
const message = providerError(result) ?? `AI job ${jobId} ended with status ${status}.`;
|
|
20500
|
+
return cliResultSchema.parse({
|
|
20501
|
+
...result,
|
|
20502
|
+
status: "error",
|
|
20503
|
+
resultType: "aiJobStatus",
|
|
20504
|
+
command: "spira ai jobs create",
|
|
20505
|
+
error: {
|
|
20506
|
+
code: status === "FAILED" ? "PROVIDER_FAILED" : "ASYNC_STATE",
|
|
20507
|
+
message,
|
|
20508
|
+
resource: { type: "aiJob", id: jobId },
|
|
20509
|
+
retryable: false,
|
|
20510
|
+
nextActions: [{ label: "Show job status", command: `spira ai jobs status ${jobId}` }]
|
|
20511
|
+
}
|
|
20512
|
+
});
|
|
20513
|
+
}
|
|
20514
|
+
function timeoutResult(jobId, timeoutMs, lastResult) {
|
|
20515
|
+
return cliResultSchema.parse({
|
|
20516
|
+
object: "cli.result",
|
|
20517
|
+
status: "error",
|
|
20518
|
+
resultType: "aiJobStatus",
|
|
20519
|
+
command: "spira ai jobs create",
|
|
20520
|
+
data: lastResult?.data,
|
|
20521
|
+
ids: { ...lastResult?.ids, jobId },
|
|
20522
|
+
error: {
|
|
20523
|
+
code: "TIMEOUT",
|
|
20524
|
+
message: `AI job ${jobId} did not finish within ${timeoutMs}ms.`,
|
|
20525
|
+
resource: { type: "aiJob", id: jobId },
|
|
20526
|
+
retryable: true,
|
|
20527
|
+
nextActions: [{ label: "Show job status", command: `spira ai jobs status ${jobId}` }]
|
|
20528
|
+
}
|
|
20529
|
+
});
|
|
20530
|
+
}
|
|
20531
|
+
function contractError(result, jobId) {
|
|
20532
|
+
return cliResultSchema.parse({
|
|
20533
|
+
...result,
|
|
20534
|
+
status: "error",
|
|
20535
|
+
resultType: "aiJobStatus",
|
|
20536
|
+
command: "spira ai jobs create",
|
|
20537
|
+
error: {
|
|
20538
|
+
code: "CONTRACT_MISMATCH",
|
|
20539
|
+
message: `AI job ${jobId} status response did not contain a recognized status.`,
|
|
20540
|
+
resource: { type: "aiJob", id: jobId },
|
|
20541
|
+
retryable: false,
|
|
20542
|
+
nextActions: [{ label: "Show job status", command: `spira ai jobs status ${jobId}` }]
|
|
20543
|
+
}
|
|
20544
|
+
});
|
|
20545
|
+
}
|
|
20546
|
+
async function waitForAiJob(input) {
|
|
20547
|
+
const now = input.now ?? Date.now;
|
|
20548
|
+
const sleep = input.sleep ?? wait;
|
|
20549
|
+
const deadline = now() + input.timeoutMs;
|
|
20550
|
+
let lastResult;
|
|
20551
|
+
let lastStatus;
|
|
20552
|
+
while (true) {
|
|
20553
|
+
const result = await input.poll();
|
|
20554
|
+
lastResult = result;
|
|
20555
|
+
if (result.status === "error") {
|
|
20556
|
+
if (!result.error?.retryable) return pollingError(result);
|
|
20557
|
+
} else {
|
|
20558
|
+
const status = jobStatus(result);
|
|
20559
|
+
if (status && status !== lastStatus) {
|
|
20560
|
+
input.onStatus?.(status);
|
|
20561
|
+
lastStatus = status;
|
|
20562
|
+
}
|
|
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);
|
|
20566
|
+
}
|
|
20567
|
+
const remainingMs = deadline - now();
|
|
20568
|
+
if (remainingMs <= 0) return timeoutResult(input.jobId, input.timeoutMs, lastResult);
|
|
20569
|
+
await sleep(Math.min(input.intervalMs, remainingMs));
|
|
20570
|
+
}
|
|
20571
|
+
}
|
|
20572
|
+
|
|
20205
20573
|
// src/commands/definitions.ts
|
|
20206
20574
|
var commonListOptions = [
|
|
20207
20575
|
{
|
|
@@ -20385,7 +20753,10 @@ var commandDefinitions = [
|
|
|
20385
20753
|
},
|
|
20386
20754
|
resultType: "socialSubmissionList",
|
|
20387
20755
|
examples: ["spira social submissions list --platform tiktok --status draft --limit 10"],
|
|
20388
|
-
relatedCommands: [
|
|
20756
|
+
relatedCommands: [
|
|
20757
|
+
"spira social submissions get <submissionId>",
|
|
20758
|
+
"spira social submissions publish <submissionId> --confirm"
|
|
20759
|
+
]
|
|
20389
20760
|
},
|
|
20390
20761
|
{
|
|
20391
20762
|
path: ["social", "submissions", "get"],
|
|
@@ -20431,7 +20802,12 @@ var commandDefinitions = [
|
|
|
20431
20802
|
defaultValue: "oauth_account"
|
|
20432
20803
|
},
|
|
20433
20804
|
{ name: "title", flag: "--title <title>", description: "Draft title.", type: "string" },
|
|
20434
|
-
{
|
|
20805
|
+
{
|
|
20806
|
+
name: "description",
|
|
20807
|
+
flag: "--description <description>",
|
|
20808
|
+
description: "Draft description/text.",
|
|
20809
|
+
type: "string"
|
|
20810
|
+
},
|
|
20435
20811
|
{ name: "mediaUrls", flag: "--media-url <url>", description: "Media URL. Repeatable.", type: "stringArray" },
|
|
20436
20812
|
{ name: "agentId", flag: "--agent-id <agentId>", description: "Associated agent ID.", type: "string" },
|
|
20437
20813
|
{
|
|
@@ -20458,9 +20834,7 @@ var commandDefinitions = [
|
|
|
20458
20834
|
]
|
|
20459
20835
|
},
|
|
20460
20836
|
resultType: "socialSubmissionAction",
|
|
20461
|
-
examples: [
|
|
20462
|
-
'spira social submissions create --platform twitter --content-kind text --description "Launch note"'
|
|
20463
|
-
],
|
|
20837
|
+
examples: ['spira social submissions create --platform twitter --content-kind text --description "Launch note"'],
|
|
20464
20838
|
relatedCommands: [
|
|
20465
20839
|
"spira social submissions get <submissionId>",
|
|
20466
20840
|
"spira social submissions publish <submissionId> --confirm"
|
|
@@ -20471,9 +20845,20 @@ var commandDefinitions = [
|
|
|
20471
20845
|
purpose: "Publish a social submission draft.",
|
|
20472
20846
|
arguments: [{ name: "submissionId", description: "Submission ID.", required: true }],
|
|
20473
20847
|
options: [
|
|
20474
|
-
{
|
|
20848
|
+
{
|
|
20849
|
+
name: "confirm",
|
|
20850
|
+
flag: "--confirm",
|
|
20851
|
+
description: "Required explicit publish confirmation.",
|
|
20852
|
+
type: "boolean",
|
|
20853
|
+
required: true
|
|
20854
|
+
},
|
|
20475
20855
|
{ name: "title", flag: "--title <title>", description: "Platform title override.", type: "string" },
|
|
20476
|
-
{
|
|
20856
|
+
{
|
|
20857
|
+
name: "description",
|
|
20858
|
+
flag: "--description <description>",
|
|
20859
|
+
description: "Platform description override.",
|
|
20860
|
+
type: "string"
|
|
20861
|
+
},
|
|
20477
20862
|
{ name: "text", flag: "--text <text>", description: "Text override for X or LinkedIn.", type: "string" },
|
|
20478
20863
|
{ name: "mediaUrls", flag: "--media-url <url>", description: "Media URL. Repeatable.", type: "stringArray" },
|
|
20479
20864
|
{ name: "privacyLevel", flag: "--privacy-level <value>", description: "TikTok privacy level.", type: "string" },
|
|
@@ -20514,18 +20899,18 @@ var commandDefinitions = [
|
|
|
20514
20899
|
},
|
|
20515
20900
|
{
|
|
20516
20901
|
path: ["ai", "models", "list"],
|
|
20517
|
-
purpose: "List AI image and
|
|
20902
|
+
purpose: "List AI image, video, and audio models available from the CLI.",
|
|
20518
20903
|
options: [
|
|
20519
20904
|
{
|
|
20520
20905
|
name: "mediaType",
|
|
20521
20906
|
flag: "--media-type <type>",
|
|
20522
20907
|
description: "Filter by model media type.",
|
|
20523
20908
|
type: "string",
|
|
20524
|
-
enumValues: ["image", "video"]
|
|
20909
|
+
enumValues: ["image", "video", "audio"]
|
|
20525
20910
|
}
|
|
20526
20911
|
],
|
|
20527
20912
|
resultType: "aiModelList",
|
|
20528
|
-
examples: ["spira ai models list", "spira ai models list --media-type
|
|
20913
|
+
examples: ["spira ai models list", "spira ai models list --media-type audio"],
|
|
20529
20914
|
relatedCommands: ["spira ai models get <modelId>", "spira ai jobs create"]
|
|
20530
20915
|
},
|
|
20531
20916
|
{
|
|
@@ -20538,11 +20923,45 @@ var commandDefinitions = [
|
|
|
20538
20923
|
},
|
|
20539
20924
|
{
|
|
20540
20925
|
path: ["ai", "jobs", "create"],
|
|
20541
|
-
purpose: "Create an AI image or
|
|
20926
|
+
purpose: "Create an AI image, video, or audio generation job.",
|
|
20542
20927
|
options: [
|
|
20543
20928
|
{ name: "modelId", flag: "--model-id <modelId>", description: "AI model ID.", type: "string", required: true },
|
|
20544
|
-
{
|
|
20545
|
-
|
|
20929
|
+
{
|
|
20930
|
+
name: "providerId",
|
|
20931
|
+
flag: "--provider-id <providerId>",
|
|
20932
|
+
description: "Advanced routing override.",
|
|
20933
|
+
type: "string",
|
|
20934
|
+
hidden: true
|
|
20935
|
+
},
|
|
20936
|
+
{
|
|
20937
|
+
name: "inputsJson",
|
|
20938
|
+
flag: "--inputs-json <json>",
|
|
20939
|
+
description: "Model inputs JSON object.",
|
|
20940
|
+
type: "json",
|
|
20941
|
+
required: true
|
|
20942
|
+
},
|
|
20943
|
+
{
|
|
20944
|
+
name: "wait",
|
|
20945
|
+
flag: "--wait",
|
|
20946
|
+
description: "Wait for the job to finish and print the final result.",
|
|
20947
|
+
type: "boolean"
|
|
20948
|
+
},
|
|
20949
|
+
{
|
|
20950
|
+
name: "waitTimeout",
|
|
20951
|
+
flag: "--wait-timeout <duration>",
|
|
20952
|
+
description: "Maximum wait duration.",
|
|
20953
|
+
type: "duration",
|
|
20954
|
+
defaultValue: 6e5,
|
|
20955
|
+
minimum: 1e3
|
|
20956
|
+
},
|
|
20957
|
+
{
|
|
20958
|
+
name: "waitInterval",
|
|
20959
|
+
flag: "--wait-interval <duration>",
|
|
20960
|
+
description: "Polling interval while waiting.",
|
|
20961
|
+
type: "duration",
|
|
20962
|
+
defaultValue: 3e3,
|
|
20963
|
+
minimum: 1e3
|
|
20964
|
+
}
|
|
20546
20965
|
],
|
|
20547
20966
|
http: {
|
|
20548
20967
|
method: "POST",
|
|
@@ -20555,7 +20974,10 @@ var commandDefinitions = [
|
|
|
20555
20974
|
]
|
|
20556
20975
|
},
|
|
20557
20976
|
resultType: "aiJobAction",
|
|
20558
|
-
examples: [
|
|
20977
|
+
examples: [
|
|
20978
|
+
`spira ai jobs create --model-id nano-banana-2 --inputs-json '{"prompt":"A product photo"}'`,
|
|
20979
|
+
`spira ai jobs create --model-id nano-banana-2 --inputs-json '{"prompt":"A product photo"}' --wait`
|
|
20980
|
+
],
|
|
20559
20981
|
relatedCommands: ["spira ai jobs status <jobId>", "spira ai models get <modelId>", "spira credits balance"]
|
|
20560
20982
|
},
|
|
20561
20983
|
{
|
|
@@ -20592,6 +21014,33 @@ var commandDefinitions = [
|
|
|
20592
21014
|
"spira workflows runs list <workflowId>"
|
|
20593
21015
|
]
|
|
20594
21016
|
},
|
|
21017
|
+
{
|
|
21018
|
+
path: ["workflows", "node-catalog"],
|
|
21019
|
+
purpose: "Show the current server workflow node catalog.",
|
|
21020
|
+
options: [
|
|
21021
|
+
{
|
|
21022
|
+
name: "surface",
|
|
21023
|
+
flag: "--surface <surface>",
|
|
21024
|
+
description: "Workflow access surface.",
|
|
21025
|
+
type: "string",
|
|
21026
|
+
enumValues: ["dashboard", "admin"],
|
|
21027
|
+
defaultValue: "dashboard"
|
|
21028
|
+
}
|
|
21029
|
+
],
|
|
21030
|
+
http: {
|
|
21031
|
+
method: "GET",
|
|
21032
|
+
path: "/api/cli/v1/workflows/node-catalog",
|
|
21033
|
+
authRequired: true,
|
|
21034
|
+
query: [{ name: "surface", source: "option", from: "surface" }]
|
|
21035
|
+
},
|
|
21036
|
+
resultType: "workflowNodeCatalog",
|
|
21037
|
+
examples: ["spira workflows node-catalog --json", "spira workflows node-catalog --surface admin --json"],
|
|
21038
|
+
relatedCommands: [
|
|
21039
|
+
"spira workflows create --name <name>",
|
|
21040
|
+
"spira workflows update <workflowId>",
|
|
21041
|
+
"spira workflows get <workflowId>"
|
|
21042
|
+
]
|
|
21043
|
+
},
|
|
20595
21044
|
{
|
|
20596
21045
|
path: ["workflows", "create"],
|
|
20597
21046
|
purpose: "Create a canvas workflow.",
|
|
@@ -20635,7 +21084,11 @@ var commandDefinitions = [
|
|
|
20635
21084
|
'spira workflows create --name "Product launch workflow"',
|
|
20636
21085
|
`spira workflows create --name "Product launch workflow" --workflow-definition-json '{"nodes":[],"edges":[]}'`
|
|
20637
21086
|
],
|
|
20638
|
-
relatedCommands: [
|
|
21087
|
+
relatedCommands: [
|
|
21088
|
+
"spira workflows get <workflowId>",
|
|
21089
|
+
"spira workflows update <workflowId>",
|
|
21090
|
+
"spira workflows runs start <workflowId>"
|
|
21091
|
+
]
|
|
20639
21092
|
},
|
|
20640
21093
|
{
|
|
20641
21094
|
path: ["workflows", "get"],
|
|
@@ -20773,7 +21226,12 @@ var commandDefinitions = [
|
|
|
20773
21226
|
purpose: "Start a workflow run and return its run ID.",
|
|
20774
21227
|
arguments: [{ name: "workflowId", description: "Workflow ID.", required: true }],
|
|
20775
21228
|
options: [
|
|
20776
|
-
{
|
|
21229
|
+
{
|
|
21230
|
+
name: "startOptionsJson",
|
|
21231
|
+
flag: "--start-options-json <json>",
|
|
21232
|
+
description: "Workflow start options JSON.",
|
|
21233
|
+
type: "json"
|
|
21234
|
+
},
|
|
20777
21235
|
{
|
|
20778
21236
|
name: "surface",
|
|
20779
21237
|
flag: "--surface <surface>",
|
|
@@ -20832,7 +21290,9 @@ var commandDefinitions = [
|
|
|
20832
21290
|
path: ["schedules", "executions"],
|
|
20833
21291
|
purpose: "List persisted schedule execution attempts.",
|
|
20834
21292
|
arguments: [{ name: "scheduleId", description: "Schedule ID.", required: true }],
|
|
20835
|
-
options: [
|
|
21293
|
+
options: [
|
|
21294
|
+
{ name: "limit", flag: "--limit <number>", description: "Maximum executions to return.", type: "number" }
|
|
21295
|
+
],
|
|
20836
21296
|
http: {
|
|
20837
21297
|
method: "GET",
|
|
20838
21298
|
path: "/api/schedules/:scheduleId/executions",
|
|
@@ -20848,11 +21308,28 @@ var commandDefinitions = [
|
|
|
20848
21308
|
path: ["schedules", "calendar"],
|
|
20849
21309
|
purpose: "List scheduled occurrences between two times.",
|
|
20850
21310
|
options: [
|
|
20851
|
-
{
|
|
20852
|
-
|
|
21311
|
+
{
|
|
21312
|
+
name: "from",
|
|
21313
|
+
flag: "--from <ms>",
|
|
21314
|
+
description: "Start timestamp in epoch milliseconds.",
|
|
21315
|
+
type: "number",
|
|
21316
|
+
required: true
|
|
21317
|
+
},
|
|
21318
|
+
{
|
|
21319
|
+
name: "to",
|
|
21320
|
+
flag: "--to <ms>",
|
|
21321
|
+
description: "End timestamp in epoch milliseconds.",
|
|
21322
|
+
type: "number",
|
|
21323
|
+
required: true
|
|
21324
|
+
},
|
|
20853
21325
|
{ name: "limit", flag: "--limit <number>", description: "Maximum occurrences to return.", type: "number" },
|
|
20854
21326
|
{ name: "offset", flag: "--offset <number>", description: "Zero-based occurrence offset.", type: "number" },
|
|
20855
|
-
{
|
|
21327
|
+
{
|
|
21328
|
+
name: "scheduleId",
|
|
21329
|
+
flag: "--schedule-id <scheduleId>",
|
|
21330
|
+
description: "Optional schedule id filter.",
|
|
21331
|
+
type: "string"
|
|
21332
|
+
}
|
|
20856
21333
|
],
|
|
20857
21334
|
http: {
|
|
20858
21335
|
method: "GET",
|
|
@@ -20869,6 +21346,125 @@ var commandDefinitions = [
|
|
|
20869
21346
|
resultType: "scheduleOccurrenceList",
|
|
20870
21347
|
examples: ["spira schedules calendar --from 1781884800000 --to 1782490000000"],
|
|
20871
21348
|
relatedCommands: ["spira schedules get <scheduleId>"]
|
|
21349
|
+
},
|
|
21350
|
+
{
|
|
21351
|
+
path: ["dataBase", "glob"],
|
|
21352
|
+
purpose: "List files and folders in your Brand database matching a glob pattern.",
|
|
21353
|
+
arguments: [{ name: "pattern", description: "Glob pattern (default: /**).", required: false }],
|
|
21354
|
+
http: {
|
|
21355
|
+
method: "GET",
|
|
21356
|
+
path: "/api/cli/v1/data-base/glob",
|
|
21357
|
+
authRequired: true,
|
|
21358
|
+
query: [{ name: "pattern", source: "argument", from: "pattern" }]
|
|
21359
|
+
},
|
|
21360
|
+
resultType: "dataBaseGlob",
|
|
21361
|
+
examples: ["spira dataBase glob", "spira dataBase glob '/docs/**'", "spira dataBase glob '/**/*.md'"],
|
|
21362
|
+
relatedCommands: ["spira dataBase read <path>", 'spira dataBase search "<query>"']
|
|
21363
|
+
},
|
|
21364
|
+
{
|
|
21365
|
+
path: ["dataBase", "read"],
|
|
21366
|
+
purpose: "Read file content at a Brand database path with optional line/page slicing.",
|
|
21367
|
+
arguments: [{ name: "path", description: "Brand database path (e.g. /docs/style-guide.md).", required: true }],
|
|
21368
|
+
options: [
|
|
21369
|
+
{ name: "line", flag: "--line <number>", description: "Start line (1-based).", type: "number" },
|
|
21370
|
+
{ name: "lineEnd", flag: "--line-end <number>", description: "End line (inclusive).", type: "number" },
|
|
21371
|
+
{ name: "page", flag: "--page <number>", description: "Start page (1-based, PDFs).", type: "number" },
|
|
21372
|
+
{ name: "pageEnd", flag: "--page-end <number>", description: "End page (inclusive).", type: "number" },
|
|
21373
|
+
{
|
|
21374
|
+
name: "expand",
|
|
21375
|
+
flag: "--expand <number>",
|
|
21376
|
+
description: "Extra lines of context around the range.",
|
|
21377
|
+
type: "number"
|
|
21378
|
+
}
|
|
21379
|
+
],
|
|
21380
|
+
http: {
|
|
21381
|
+
method: "GET",
|
|
21382
|
+
path: "/api/cli/v1/data-base/read",
|
|
21383
|
+
authRequired: true,
|
|
21384
|
+
query: [
|
|
21385
|
+
{ name: "path", source: "argument", from: "path" },
|
|
21386
|
+
{ name: "line", source: "option", from: "line" },
|
|
21387
|
+
{ name: "lineEnd", source: "option", from: "lineEnd" },
|
|
21388
|
+
{ name: "page", source: "option", from: "page" },
|
|
21389
|
+
{ name: "pageEnd", source: "option", from: "pageEnd" },
|
|
21390
|
+
{ name: "expand", source: "option", from: "expand" }
|
|
21391
|
+
]
|
|
21392
|
+
},
|
|
21393
|
+
resultType: "dataBaseRead",
|
|
21394
|
+
examples: [
|
|
21395
|
+
"spira dataBase read /README.md",
|
|
21396
|
+
"spira dataBase read /docs/style-guide.md --line 40 --line-end 120",
|
|
21397
|
+
"spira dataBase read /decks/pitch.pdf --page 2 --page-end 3"
|
|
21398
|
+
],
|
|
21399
|
+
relatedCommands: ["spira dataBase glob", 'spira dataBase search "<query>"']
|
|
21400
|
+
},
|
|
21401
|
+
{
|
|
21402
|
+
path: ["dataBase", "grep"],
|
|
21403
|
+
purpose: "Lexical (FTS + exact) search across your Brand database. Fast and deterministic.",
|
|
21404
|
+
arguments: [{ name: "query", description: "Search query.", required: true }],
|
|
21405
|
+
options: [
|
|
21406
|
+
{ name: "scope", flag: "--scope <path>", description: "Restrict to a folder path.", type: "string" },
|
|
21407
|
+
{ name: "limit", flag: "--limit <number>", description: "Max matches (default 20, max 100).", type: "number" },
|
|
21408
|
+
{
|
|
21409
|
+
name: "debug",
|
|
21410
|
+
flag: "--debug",
|
|
21411
|
+
description: "Include candidate scoring breakdown (JSON mode).",
|
|
21412
|
+
type: "boolean"
|
|
21413
|
+
}
|
|
21414
|
+
],
|
|
21415
|
+
http: {
|
|
21416
|
+
method: "POST",
|
|
21417
|
+
path: "/api/cli/v1/data-base/grep",
|
|
21418
|
+
authRequired: true,
|
|
21419
|
+
body: [
|
|
21420
|
+
{ name: "query", source: "argument", from: "query" },
|
|
21421
|
+
{ name: "scope", source: "option", from: "scope" },
|
|
21422
|
+
{ name: "limit", source: "option", from: "limit" },
|
|
21423
|
+
{ name: "debug", source: "option", from: "debug" }
|
|
21424
|
+
]
|
|
21425
|
+
},
|
|
21426
|
+
resultType: "dataBaseGrep",
|
|
21427
|
+
examples: ['spira dataBase grep "brand voice"', 'spira dataBase grep "tone" --scope /docs --limit 10'],
|
|
21428
|
+
relatedCommands: ["spira dataBase read <path>", 'spira dataBase search "<query>"']
|
|
21429
|
+
},
|
|
21430
|
+
{
|
|
21431
|
+
path: ["dataBase", "search"],
|
|
21432
|
+
purpose: "Semantic search (pgvector + RRF + MMR) across your Brand database.",
|
|
21433
|
+
arguments: [{ name: "query", description: "Natural-language query.", required: true }],
|
|
21434
|
+
options: [
|
|
21435
|
+
{ name: "scope", flag: "--scope <path>", description: "Restrict to a folder path.", type: "string" },
|
|
21436
|
+
{ name: "limit", flag: "--limit <number>", description: "Max matches (default 20, max 100).", type: "number" },
|
|
21437
|
+
{
|
|
21438
|
+
name: "rerank",
|
|
21439
|
+
flag: "--rerank",
|
|
21440
|
+
description: "Enable LLM reranking (slower, higher quality).",
|
|
21441
|
+
type: "boolean"
|
|
21442
|
+
},
|
|
21443
|
+
{
|
|
21444
|
+
name: "debug",
|
|
21445
|
+
flag: "--debug",
|
|
21446
|
+
description: "Include candidate scoring breakdown (JSON mode).",
|
|
21447
|
+
type: "boolean"
|
|
21448
|
+
}
|
|
21449
|
+
],
|
|
21450
|
+
http: {
|
|
21451
|
+
method: "POST",
|
|
21452
|
+
path: "/api/cli/v1/data-base/search",
|
|
21453
|
+
authRequired: true,
|
|
21454
|
+
body: [
|
|
21455
|
+
{ name: "query", source: "argument", from: "query" },
|
|
21456
|
+
{ name: "scope", source: "option", from: "scope" },
|
|
21457
|
+
{ name: "limit", source: "option", from: "limit" },
|
|
21458
|
+
{ name: "rerank", source: "option", from: "rerank" },
|
|
21459
|
+
{ name: "debug", source: "option", from: "debug" }
|
|
21460
|
+
]
|
|
21461
|
+
},
|
|
21462
|
+
resultType: "dataBaseSearch",
|
|
21463
|
+
examples: [
|
|
21464
|
+
'spira dataBase search "what is our stance on price anchoring"',
|
|
21465
|
+
'spira dataBase search "founder biography" --scope /about --rerank'
|
|
21466
|
+
],
|
|
21467
|
+
relatedCommands: ["spira dataBase read <path>", 'spira dataBase grep "<query>"']
|
|
20872
21468
|
}
|
|
20873
21469
|
];
|
|
20874
21470
|
function findCommandDefinition(path) {
|
|
@@ -20892,6 +21488,13 @@ function validateCommandInput(command, args, options) {
|
|
|
20892
21488
|
return validationError(command, option.name, `Expected one of: ${option.enumValues.join(", ")}.`);
|
|
20893
21489
|
}
|
|
20894
21490
|
}
|
|
21491
|
+
if (typeof value === "number" && option.minimum !== void 0 && value < option.minimum) {
|
|
21492
|
+
return validationError(
|
|
21493
|
+
command,
|
|
21494
|
+
option.name,
|
|
21495
|
+
`${getOptionDisplayName(option.flag)} must be at least ${option.minimum}.`
|
|
21496
|
+
);
|
|
21497
|
+
}
|
|
20895
21498
|
}
|
|
20896
21499
|
return null;
|
|
20897
21500
|
}
|
|
@@ -21445,6 +22048,43 @@ async function logout() {
|
|
|
21445
22048
|
});
|
|
21446
22049
|
}
|
|
21447
22050
|
|
|
22051
|
+
// src/render/ai-job-wait.ts
|
|
22052
|
+
function objectRecord(value) {
|
|
22053
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
22054
|
+
}
|
|
22055
|
+
function compactResult(result) {
|
|
22056
|
+
const data = objectRecord(result.data);
|
|
22057
|
+
const execution = objectRecord(data.latestExecution);
|
|
22058
|
+
const status = typeof data.status === "string" ? data.status.toLowerCase() : result.status;
|
|
22059
|
+
const resultUrls = Array.isArray(execution.resultUrls) ? execution.resultUrls.filter((url2) => typeof url2 === "string") : [];
|
|
22060
|
+
return {
|
|
22061
|
+
id: typeof data.id === "string" ? data.id : result.ids.jobId,
|
|
22062
|
+
modelId: typeof data.modelId === "string" ? data.modelId : void 0,
|
|
22063
|
+
status,
|
|
22064
|
+
resultType: typeof execution.resultType === "string" ? execution.resultType : void 0,
|
|
22065
|
+
resultUrls,
|
|
22066
|
+
...result.error ? {
|
|
22067
|
+
error: {
|
|
22068
|
+
code: result.error.code,
|
|
22069
|
+
message: result.error.message,
|
|
22070
|
+
retryable: result.error.retryable
|
|
22071
|
+
}
|
|
22072
|
+
} : {}
|
|
22073
|
+
};
|
|
22074
|
+
}
|
|
22075
|
+
function renderAiJobWait(result, options) {
|
|
22076
|
+
const compact = compactResult(result);
|
|
22077
|
+
if (options.json) return `${JSON.stringify(compact, null, 2)}
|
|
22078
|
+
`;
|
|
22079
|
+
const lines = [`id: ${compact.id ?? "-"}`, `status: ${compact.status}`];
|
|
22080
|
+
if (compact.modelId) lines.push(`modelId: ${compact.modelId}`);
|
|
22081
|
+
if (compact.resultType) lines.push(`resultType: ${compact.resultType}`);
|
|
22082
|
+
lines.push(...compact.resultUrls);
|
|
22083
|
+
if (compact.error) lines.push(`error: ${compact.error.message}`);
|
|
22084
|
+
return `${lines.join("\n")}
|
|
22085
|
+
`;
|
|
22086
|
+
}
|
|
22087
|
+
|
|
21448
22088
|
// src/render/human.ts
|
|
21449
22089
|
function valueToText(value) {
|
|
21450
22090
|
if (value === null || value === void 0 || value === "") return "-";
|
|
@@ -21495,11 +22135,11 @@ function renderKeyValues(data) {
|
|
|
21495
22135
|
`;
|
|
21496
22136
|
return Object.entries(data).filter(([, value]) => typeof value !== "object" || value === null).map(([key, value]) => `${key}: ${valueToText(value)}`).join("\n").concat("\n");
|
|
21497
22137
|
}
|
|
21498
|
-
function
|
|
22138
|
+
function objectRecord2(data) {
|
|
21499
22139
|
return data && typeof data === "object" ? data : {};
|
|
21500
22140
|
}
|
|
21501
22141
|
function formatWhen(value) {
|
|
21502
|
-
const record2 =
|
|
22142
|
+
const record2 = objectRecord2(value);
|
|
21503
22143
|
const entries = Object.entries(record2);
|
|
21504
22144
|
if (!entries.length) return "";
|
|
21505
22145
|
return entries.map(([key, item]) => `${key}=${valueToText(item)}`).join(", ");
|
|
@@ -21512,11 +22152,34 @@ function formatCreditOption(option) {
|
|
|
21512
22152
|
const when = formatWhen(option.when ?? option.conditions);
|
|
21513
22153
|
return ` ${key}: ${label} - ${credits} credits ${unit}${when ? ` (${when})` : ""}`;
|
|
21514
22154
|
}
|
|
22155
|
+
function portSummary(ports) {
|
|
22156
|
+
if (!Array.isArray(ports) || ports.length === 0) return "-";
|
|
22157
|
+
return ports.map((port) => {
|
|
22158
|
+
const record2 = objectRecord2(port);
|
|
22159
|
+
const suffix = record2.multiple ? "[]" : "";
|
|
22160
|
+
return `${valueToText(record2.id)}:${valueToText(record2.valueType)}${suffix}`;
|
|
22161
|
+
}).join(", ");
|
|
22162
|
+
}
|
|
22163
|
+
function renderWorkflowNodeCatalog(data) {
|
|
22164
|
+
const record2 = objectRecord2(data);
|
|
22165
|
+
const nodes = Array.isArray(record2.nodes) ? record2.nodes : [];
|
|
22166
|
+
const rows = nodes.map((node) => {
|
|
22167
|
+
const nodeRecord = objectRecord2(node);
|
|
22168
|
+
const ports = objectRecord2(nodeRecord.ports);
|
|
22169
|
+
return {
|
|
22170
|
+
type: nodeRecord.type,
|
|
22171
|
+
inputs: portSummary(ports.inputs),
|
|
22172
|
+
outputs: portSummary(ports.outputs),
|
|
22173
|
+
requiredPermission: nodeRecord.requiredPermission
|
|
22174
|
+
};
|
|
22175
|
+
});
|
|
22176
|
+
return renderRows(rows, ["type", "inputs", "outputs", "requiredPermission"]);
|
|
22177
|
+
}
|
|
21515
22178
|
function renderAccountList(data) {
|
|
21516
22179
|
const channels = data && typeof data === "object" ? data.publishChannels : null;
|
|
21517
22180
|
if (Array.isArray(channels)) {
|
|
21518
22181
|
const rows = channels.map((channel) => {
|
|
21519
|
-
const record2 =
|
|
22182
|
+
const record2 = objectRecord2(channel);
|
|
21520
22183
|
return {
|
|
21521
22184
|
platform: record2.platform,
|
|
21522
22185
|
connected: record2.oauthAccountsCount,
|
|
@@ -21529,12 +22192,12 @@ function renderAccountList(data) {
|
|
|
21529
22192
|
return renderKeyValues(data);
|
|
21530
22193
|
}
|
|
21531
22194
|
function renderAccountConnect(data) {
|
|
21532
|
-
const record2 =
|
|
22195
|
+
const record2 = objectRecord2(data);
|
|
21533
22196
|
return `platform: ${valueToText(record2.platform)}
|
|
21534
22197
|
`;
|
|
21535
22198
|
}
|
|
21536
22199
|
function renderCreditBalance(data) {
|
|
21537
|
-
const record2 =
|
|
22200
|
+
const record2 = objectRecord2(data);
|
|
21538
22201
|
const lines = [
|
|
21539
22202
|
`availableCredits: ${formatCredits(record2.availableCredits)}`,
|
|
21540
22203
|
`permanentCredits: ${formatCredits(record2.availablePermanentCredits)}`,
|
|
@@ -21561,7 +22224,7 @@ function renderUser(prefix, value) {
|
|
|
21561
22224
|
function renderAuthWhoami(data) {
|
|
21562
22225
|
if (!data || typeof data !== "object") return renderKeyValues(data);
|
|
21563
22226
|
const record2 = data;
|
|
21564
|
-
const effective =
|
|
22227
|
+
const effective = objectRecord2(record2.effectiveUser);
|
|
21565
22228
|
const name = valueToText(effective.name);
|
|
21566
22229
|
const email3 = valueToText(effective.email);
|
|
21567
22230
|
const lines = [`user: ${name}${email3 === "-" ? "" : ` <${email3}>`}`];
|
|
@@ -21576,8 +22239,8 @@ function renderAuthLogin(data) {
|
|
|
21576
22239
|
`;
|
|
21577
22240
|
}
|
|
21578
22241
|
function renderSocialSubmissionDetail(data) {
|
|
21579
|
-
const raw =
|
|
21580
|
-
const nested =
|
|
22242
|
+
const raw = objectRecord2(data);
|
|
22243
|
+
const nested = objectRecord2(raw.data);
|
|
21581
22244
|
const record2 = nested.submission_id || nested.postUrl || nested.tweetId ? {
|
|
21582
22245
|
id: nested.submission_id,
|
|
21583
22246
|
platform: raw.platform,
|
|
@@ -21611,8 +22274,8 @@ function renderSocialSubmissionDetail(data) {
|
|
|
21611
22274
|
`;
|
|
21612
22275
|
}
|
|
21613
22276
|
function renderAiJobStatus(data) {
|
|
21614
|
-
const record2 =
|
|
21615
|
-
const latestExecution =
|
|
22277
|
+
const record2 = objectRecord2(data);
|
|
22278
|
+
const latestExecution = objectRecord2(record2.latestExecution);
|
|
21616
22279
|
const resultUrls = Array.isArray(latestExecution.resultUrls) ? latestExecution.resultUrls : [];
|
|
21617
22280
|
const lines = [
|
|
21618
22281
|
`id: ${valueToText(record2.id)}`,
|
|
@@ -21631,10 +22294,10 @@ function renderAiModelList(data) {
|
|
|
21631
22294
|
return renderRows(dataArray(data), ["id", "mediaType", "creditOptions", "name", "capabilities"]);
|
|
21632
22295
|
}
|
|
21633
22296
|
function renderAiModelDetail(data) {
|
|
21634
|
-
const record2 =
|
|
21635
|
-
const fields = Array.isArray(record2.fields) ? record2.fields.map(
|
|
21636
|
-
const creditOptions = Array.isArray(record2.creditOptions) ? record2.creditOptions.map(
|
|
21637
|
-
const examples = Array.isArray(record2.examples) ? record2.examples.map(
|
|
22297
|
+
const record2 = objectRecord2(data);
|
|
22298
|
+
const fields = Array.isArray(record2.fields) ? record2.fields.map(objectRecord2) : [];
|
|
22299
|
+
const creditOptions = Array.isArray(record2.creditOptions) ? record2.creditOptions.map(objectRecord2) : Array.isArray(record2.variants) ? record2.variants.map(objectRecord2) : [];
|
|
22300
|
+
const examples = Array.isArray(record2.examples) ? record2.examples.map(objectRecord2) : [];
|
|
21638
22301
|
const notes = Array.isArray(record2.notes) ? record2.notes : [];
|
|
21639
22302
|
const lines = [
|
|
21640
22303
|
`id: ${valueToText(record2.id)}`,
|
|
@@ -21674,8 +22337,8 @@ function renderAiModelDetail(data) {
|
|
|
21674
22337
|
`;
|
|
21675
22338
|
}
|
|
21676
22339
|
function renderWorkflowDetail(data) {
|
|
21677
|
-
const record2 =
|
|
21678
|
-
const definition =
|
|
22340
|
+
const record2 = objectRecord2(data);
|
|
22341
|
+
const definition = objectRecord2(record2.workflowDefinition);
|
|
21679
22342
|
const nodes = Array.isArray(definition.nodes) ? definition.nodes : [];
|
|
21680
22343
|
const edges = Array.isArray(definition.edges) ? definition.edges : [];
|
|
21681
22344
|
return [
|
|
@@ -21688,9 +22351,9 @@ function renderWorkflowDetail(data) {
|
|
|
21688
22351
|
].join("\n").concat("\n");
|
|
21689
22352
|
}
|
|
21690
22353
|
function renderScheduleDetail(data) {
|
|
21691
|
-
const record2 =
|
|
21692
|
-
const executorConfig =
|
|
21693
|
-
const currentPattern =
|
|
22354
|
+
const record2 = objectRecord2(data);
|
|
22355
|
+
const executorConfig = objectRecord2(record2.executorConfig);
|
|
22356
|
+
const currentPattern = objectRecord2(record2.currentPattern);
|
|
21694
22357
|
return [
|
|
21695
22358
|
`id: ${valueToText(record2.id)}`,
|
|
21696
22359
|
`executorType: ${valueToText(record2.executorType)}`,
|
|
@@ -21703,6 +22366,112 @@ function renderScheduleDetail(data) {
|
|
|
21703
22366
|
`scheduledAt: ${valueToText(currentPattern.scheduledAt)}`
|
|
21704
22367
|
].join("\n").concat("\n");
|
|
21705
22368
|
}
|
|
22369
|
+
function formatByteSize(value) {
|
|
22370
|
+
const n = typeof value === "number" && Number.isFinite(value) ? value : NaN;
|
|
22371
|
+
if (!Number.isFinite(n) || n < 0) return "-";
|
|
22372
|
+
if (n < 1024) return `${n} B`;
|
|
22373
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(n < 10 * 1024 ? 1 : 0)} KB`;
|
|
22374
|
+
if (n < 1024 * 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
22375
|
+
return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
22376
|
+
}
|
|
22377
|
+
function formatAnchor(anchor) {
|
|
22378
|
+
const parts = [];
|
|
22379
|
+
const line = anchor.line;
|
|
22380
|
+
const lineEnd = anchor.lineEnd;
|
|
22381
|
+
const page = anchor.page;
|
|
22382
|
+
const pageEnd = anchor.pageEnd;
|
|
22383
|
+
const timeStart = anchor.timeStartMs;
|
|
22384
|
+
const timeEnd = anchor.timeEndMs;
|
|
22385
|
+
if (typeof line === "number") {
|
|
22386
|
+
parts.push(typeof lineEnd === "number" ? `L${line}-${lineEnd}` : `L${line}`);
|
|
22387
|
+
}
|
|
22388
|
+
if (typeof page === "number") {
|
|
22389
|
+
parts.push(typeof pageEnd === "number" ? `p${page}-${pageEnd}` : `p${page}`);
|
|
22390
|
+
}
|
|
22391
|
+
if (typeof timeStart === "number") {
|
|
22392
|
+
parts.push(typeof timeEnd === "number" ? `t${timeStart}-${timeEnd}ms` : `t${timeStart}ms`);
|
|
22393
|
+
}
|
|
22394
|
+
return parts.join(", ") || "-";
|
|
22395
|
+
}
|
|
22396
|
+
function renderDataBaseGlob(data) {
|
|
22397
|
+
const record2 = objectRecord2(data);
|
|
22398
|
+
const pattern = valueToText(record2.pattern);
|
|
22399
|
+
const entries = Array.isArray(record2.entries) ? record2.entries.map(objectRecord2) : [];
|
|
22400
|
+
if (entries.length === 0) return `pattern: ${pattern}
|
|
22401
|
+
No entries.
|
|
22402
|
+
`;
|
|
22403
|
+
const rows = entries.map((entry) => {
|
|
22404
|
+
const isFolder = entry.type === "folder";
|
|
22405
|
+
const isVirtual = entry.virtual === true;
|
|
22406
|
+
return {
|
|
22407
|
+
type: isFolder ? "dir" : isVirtual ? "file*" : "file",
|
|
22408
|
+
path: entry.path,
|
|
22409
|
+
fileType: isFolder ? "-" : valueToText(entry.fileType),
|
|
22410
|
+
size: isFolder ? `${valueToText(entry.fileCount)} files` : formatByteSize(entry.size),
|
|
22411
|
+
status: isFolder ? "-" : valueToText(entry.processingStatus),
|
|
22412
|
+
updatedAt: entry.updatedAt
|
|
22413
|
+
};
|
|
22414
|
+
});
|
|
22415
|
+
return `pattern: ${pattern} (${entries.length} entries)
|
|
22416
|
+
${renderRows(rows, ["type", "path", "fileType", "size", "status", "updatedAt"])}`;
|
|
22417
|
+
}
|
|
22418
|
+
function renderDataBaseRead(data) {
|
|
22419
|
+
const record2 = objectRecord2(data);
|
|
22420
|
+
const path = valueToText(record2.path);
|
|
22421
|
+
const fileType = valueToText(record2.fileType);
|
|
22422
|
+
const mimeType = valueToText(record2.mimeType);
|
|
22423
|
+
const rangeObj = objectRecord2(record2.range);
|
|
22424
|
+
let rangeStr = "-";
|
|
22425
|
+
if (Array.isArray(rangeObj.line) && rangeObj.line.length === 2) {
|
|
22426
|
+
rangeStr = `lines ${rangeObj.line[0]}-${rangeObj.line[1]}`;
|
|
22427
|
+
} else if (Array.isArray(rangeObj.page) && rangeObj.page.length === 2) {
|
|
22428
|
+
rangeStr = `pages ${rangeObj.page[0]}-${rangeObj.page[1]}`;
|
|
22429
|
+
}
|
|
22430
|
+
const totalLines = record2.totalLines !== void 0 ? valueToText(record2.totalLines) : "-";
|
|
22431
|
+
const truncated = valueToText(record2.truncated);
|
|
22432
|
+
const lines = [
|
|
22433
|
+
`path: ${path}`,
|
|
22434
|
+
`fileType: ${fileType}`,
|
|
22435
|
+
`mimeType: ${mimeType}`,
|
|
22436
|
+
`range: ${rangeStr}`,
|
|
22437
|
+
`totalLines: ${totalLines}`,
|
|
22438
|
+
`truncated: ${truncated}`
|
|
22439
|
+
];
|
|
22440
|
+
if (record2.virtual === true) lines.push("virtual: true");
|
|
22441
|
+
if (record2.processingStatus) lines.push(`processingStatus: ${valueToText(record2.processingStatus)}`);
|
|
22442
|
+
if (record2.storageUrl) lines.push(`storageUrl: ${valueToText(record2.storageUrl)}`);
|
|
22443
|
+
const content = typeof record2.content === "string" ? record2.content : "";
|
|
22444
|
+
const header = `${lines.join("\n")}
|
|
22445
|
+
`;
|
|
22446
|
+
const nextLine = typeof record2.nextLine === "number" ? record2.nextLine : void 0;
|
|
22447
|
+
const footer = record2.truncated === true && nextLine ? `
|
|
22448
|
+
--
|
|
22449
|
+
(truncated; continue with: spira dataBase read ${path} --line ${nextLine})
|
|
22450
|
+
` : "";
|
|
22451
|
+
return `${header}
|
|
22452
|
+
${content}${footer}`;
|
|
22453
|
+
}
|
|
22454
|
+
function renderDataBaseMatches(data) {
|
|
22455
|
+
const record2 = objectRecord2(data);
|
|
22456
|
+
const query = valueToText(record2.query);
|
|
22457
|
+
const matches = Array.isArray(record2.matches) ? record2.matches.map(objectRecord2) : [];
|
|
22458
|
+
if (matches.length === 0) return `query: ${query}
|
|
22459
|
+
No matches.
|
|
22460
|
+
`;
|
|
22461
|
+
const blocks = matches.map((match) => {
|
|
22462
|
+
const anchor = formatAnchor(objectRecord2(match.anchor));
|
|
22463
|
+
const score = typeof match.score === "number" ? match.score.toFixed(3) : valueToText(match.score);
|
|
22464
|
+
const fields = Array.isArray(match.matchedFields) ? match.matchedFields.join(",") : "";
|
|
22465
|
+
const header = `${valueToText(match.path)}(${anchor}) [score=${score}${fields ? ` fields=${fields}` : ""}]`;
|
|
22466
|
+
const snippet = typeof match.snippet === "string" ? match.snippet : valueToText(match.snippet);
|
|
22467
|
+
const indented = snippet.split("\n").map((line) => ` ${line}`).join("\n");
|
|
22468
|
+
return `${header}
|
|
22469
|
+
${indented}`;
|
|
22470
|
+
});
|
|
22471
|
+
return `query: ${query} (${matches.length} matches)
|
|
22472
|
+
${blocks.join("\n\n")}
|
|
22473
|
+
`;
|
|
22474
|
+
}
|
|
21706
22475
|
function renderSuccess(result) {
|
|
21707
22476
|
switch (result.resultType) {
|
|
21708
22477
|
case "authLogin":
|
|
@@ -21720,7 +22489,7 @@ function renderSuccess(result) {
|
|
|
21720
22489
|
case "socialSubmissionList":
|
|
21721
22490
|
return renderRows(
|
|
21722
22491
|
dataArray(result.data).map((item) => {
|
|
21723
|
-
const record2 =
|
|
22492
|
+
const record2 = objectRecord2(item);
|
|
21724
22493
|
return {
|
|
21725
22494
|
id: record2.id,
|
|
21726
22495
|
platform: record2.platform,
|
|
@@ -21745,6 +22514,8 @@ function renderSuccess(result) {
|
|
|
21745
22514
|
return renderAiModelDetail(result.data);
|
|
21746
22515
|
case "workflowList":
|
|
21747
22516
|
return renderRows(dataArray(result.data), ["id", "name", "creatorName", "nodeTypes", "updatedAt"]);
|
|
22517
|
+
case "workflowNodeCatalog":
|
|
22518
|
+
return renderWorkflowNodeCatalog(result.data);
|
|
21748
22519
|
case "workflowDetail":
|
|
21749
22520
|
return renderWorkflowDetail(result.data);
|
|
21750
22521
|
case "workflowRunList":
|
|
@@ -21757,6 +22528,13 @@ function renderSuccess(result) {
|
|
|
21757
22528
|
return renderRows(dataArray(result.data), ["id", "scheduleId", "scheduledAt", "status", "executorType"]);
|
|
21758
22529
|
case "scheduleExecutionList":
|
|
21759
22530
|
return renderRows(dataArray(result.data), ["id", "scheduleId", "status", "originalScheduledAt", "startedAt", "finishedAt"]);
|
|
22531
|
+
case "dataBaseGlob":
|
|
22532
|
+
return renderDataBaseGlob(result.data);
|
|
22533
|
+
case "dataBaseRead":
|
|
22534
|
+
return renderDataBaseRead(result.data);
|
|
22535
|
+
case "dataBaseGrep":
|
|
22536
|
+
case "dataBaseSearch":
|
|
22537
|
+
return renderDataBaseMatches(result.data);
|
|
21760
22538
|
default:
|
|
21761
22539
|
return renderKeyValues(result.data);
|
|
21762
22540
|
}
|
|
@@ -21808,6 +22586,7 @@ function renderJson(result) {
|
|
|
21808
22586
|
|
|
21809
22587
|
// src/render/index.ts
|
|
21810
22588
|
function renderResult(result, options) {
|
|
22589
|
+
if (options.compactAiJobWait) return renderAiJobWait(result, options);
|
|
21811
22590
|
return options.json ? renderJson(result) : renderHuman(result);
|
|
21812
22591
|
}
|
|
21813
22592
|
|
|
@@ -21960,6 +22739,24 @@ function parseNumber(value) {
|
|
|
21960
22739
|
if (!Number.isFinite(parsed)) throw new InvalidArgumentError("Expected a number.");
|
|
21961
22740
|
return parsed;
|
|
21962
22741
|
}
|
|
22742
|
+
function parseDuration(value) {
|
|
22743
|
+
const match = /^(\d+(?:\.\d+)?)(ms|s|m|h)$/.exec(value.trim());
|
|
22744
|
+
if (!match) throw new InvalidArgumentError("Expected a duration such as 500ms, 3s, 10m, or 1h.");
|
|
22745
|
+
const amount = Number(match[1]);
|
|
22746
|
+
const unit = match[2];
|
|
22747
|
+
const multiplier = unit === "ms" ? 1 : unit === "s" ? 1e3 : unit === "m" ? 6e4 : 36e5;
|
|
22748
|
+
const durationMs = amount * multiplier;
|
|
22749
|
+
if (!Number.isSafeInteger(durationMs) || durationMs <= 0) {
|
|
22750
|
+
throw new InvalidArgumentError("Expected a positive duration.");
|
|
22751
|
+
}
|
|
22752
|
+
return durationMs;
|
|
22753
|
+
}
|
|
22754
|
+
function formatDuration(durationMs) {
|
|
22755
|
+
if (durationMs % 36e5 === 0) return `${durationMs / 36e5}h`;
|
|
22756
|
+
if (durationMs % 6e4 === 0) return `${durationMs / 6e4}m`;
|
|
22757
|
+
if (durationMs % 1e3 === 0) return `${durationMs / 1e3}s`;
|
|
22758
|
+
return `${durationMs}ms`;
|
|
22759
|
+
}
|
|
21963
22760
|
function parseJson(value) {
|
|
21964
22761
|
try {
|
|
21965
22762
|
return JSON.parse(value);
|
|
@@ -21978,6 +22775,11 @@ function addOption(command, definition) {
|
|
|
21978
22775
|
command.addOption(option.argParser(parseNumber).default(definition.defaultValue));
|
|
21979
22776
|
return;
|
|
21980
22777
|
}
|
|
22778
|
+
if (definition.type === "duration") {
|
|
22779
|
+
const defaultDescription = typeof definition.defaultValue === "number" ? formatDuration(definition.defaultValue) : void 0;
|
|
22780
|
+
command.addOption(option.argParser(parseDuration).default(definition.defaultValue, defaultDescription));
|
|
22781
|
+
return;
|
|
22782
|
+
}
|
|
21981
22783
|
if (definition.type === "json") {
|
|
21982
22784
|
command.addOption(option.argParser(parseJson).default(definition.defaultValue));
|
|
21983
22785
|
return;
|
|
@@ -22034,10 +22836,7 @@ function getOrCreateSubcommand(parent, name) {
|
|
|
22034
22836
|
return next;
|
|
22035
22837
|
}
|
|
22036
22838
|
function renderCommandReference(definition) {
|
|
22037
|
-
const lines = [
|
|
22038
|
-
`Command: spira ${definition.path.join(" ")}`,
|
|
22039
|
-
`Description: ${definition.purpose}`
|
|
22040
|
-
];
|
|
22839
|
+
const lines = [`Command: spira ${definition.path.join(" ")}`, `Description: ${definition.purpose}`];
|
|
22041
22840
|
if (definition.arguments?.length) {
|
|
22042
22841
|
lines.push("Arguments:");
|
|
22043
22842
|
for (const argument of definition.arguments) {
|
|
@@ -22066,8 +22865,8 @@ function renderCommandIndex() {
|
|
|
22066
22865
|
`;
|
|
22067
22866
|
}
|
|
22068
22867
|
async function packageVersion() {
|
|
22069
|
-
if ("0.0.
|
|
22070
|
-
return "0.0.
|
|
22868
|
+
if ("0.0.6".length > 0) {
|
|
22869
|
+
return "0.0.6";
|
|
22071
22870
|
}
|
|
22072
22871
|
try {
|
|
22073
22872
|
const packageUrl = new URL("../package.json", import_meta.url);
|
|
@@ -22240,7 +23039,38 @@ ${url2}
|
|
|
22240
23039
|
resultType: "aiModelDetail",
|
|
22241
23040
|
command: "spira ai models get",
|
|
22242
23041
|
data: publicAiModelDoc(model),
|
|
22243
|
-
nextActions: [
|
|
23042
|
+
nextActions: [
|
|
23043
|
+
{ label: "Create job", command: `spira ai jobs create --model-id ${model.id} --inputs-json '<json>'` }
|
|
23044
|
+
]
|
|
23045
|
+
});
|
|
23046
|
+
}
|
|
23047
|
+
if (commandKey(definition.path) === "ai jobs create") {
|
|
23048
|
+
const created = await executeHttpCommand({ command: definition, args, options });
|
|
23049
|
+
if (options.wait !== true || created.status === "error") return created;
|
|
23050
|
+
const jobId = created.ids.jobId;
|
|
23051
|
+
if (!jobId) {
|
|
23052
|
+
return cliResultSchema.parse({
|
|
23053
|
+
...created,
|
|
23054
|
+
status: "error",
|
|
23055
|
+
error: {
|
|
23056
|
+
code: "CONTRACT_MISMATCH",
|
|
23057
|
+
message: "AI job creation response did not contain a jobId.",
|
|
23058
|
+
retryable: false,
|
|
23059
|
+
nextActions: []
|
|
23060
|
+
}
|
|
23061
|
+
});
|
|
23062
|
+
}
|
|
23063
|
+
const statusCommand = findCommandDefinition(["ai", "jobs", "status"]);
|
|
23064
|
+
if (!statusCommand) throw new Error("AI job status command is not registered.");
|
|
23065
|
+
output.error(`AI job ${jobId}: SUBMITTED
|
|
23066
|
+
`);
|
|
23067
|
+
return waitForAiJob({
|
|
23068
|
+
jobId,
|
|
23069
|
+
timeoutMs: options.waitTimeout,
|
|
23070
|
+
intervalMs: options.waitInterval,
|
|
23071
|
+
onStatus: (status) => output.error(`AI job ${jobId}: ${status}
|
|
23072
|
+
`),
|
|
23073
|
+
poll: () => executeHttpCommand({ command: statusCommand, args: { jobId }, options: {} })
|
|
22244
23074
|
});
|
|
22245
23075
|
}
|
|
22246
23076
|
return executeHttpCommand({ command: definition, args, options });
|
|
@@ -22258,12 +23088,19 @@ function addLeafCommand(root, definition, output) {
|
|
|
22258
23088
|
for (const option of definition.options ?? []) addOption(leaf, option);
|
|
22259
23089
|
leaf.action(async (...values) => {
|
|
22260
23090
|
const command = values[values.length - 1];
|
|
22261
|
-
const positional = values.slice(0, -1).map((value) => String(value));
|
|
22262
|
-
const args = Object.fromEntries(
|
|
23091
|
+
const positional = values.slice(0, -1).map((value) => value === void 0 ? "" : String(value));
|
|
23092
|
+
const args = Object.fromEntries(
|
|
23093
|
+
(definition.arguments ?? []).map((argument, index) => [argument.name, positional[index] ?? ""])
|
|
23094
|
+
);
|
|
22263
23095
|
const options = command.optsWithGlobals();
|
|
22264
23096
|
const validationResult2 = validateCommandInput(definition, args, options);
|
|
22265
23097
|
const result = validationResult2 ?? await executeDefinition(definition, args, options, output);
|
|
22266
|
-
output.write(
|
|
23098
|
+
output.write(
|
|
23099
|
+
renderResult(result, {
|
|
23100
|
+
json: Boolean(options.json),
|
|
23101
|
+
compactAiJobWait: commandKey(definition.path) === "ai jobs create" && options.wait === true
|
|
23102
|
+
})
|
|
23103
|
+
);
|
|
22267
23104
|
if (result.status === "error") process.exitCode = 1;
|
|
22268
23105
|
});
|
|
22269
23106
|
parent.addCommand(leaf);
|