@spira-lab/cli 0.0.5 → 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 +27 -22
- package/dist/index.cjs +633 -68
- package/package.json +2 -25
package/README.md
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
# Spira CLI
|
|
2
|
-
|
|
3
|
-
Command line client for Spira.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install -g @spira-lab/cli
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
spira --help
|
|
15
|
-
spira auth login
|
|
16
|
-
spira ai models list
|
|
17
|
-
spira
|
|
18
|
-
spira
|
|
19
|
-
spira
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
# Spira CLI
|
|
2
|
+
|
|
3
|
+
Command line client for Spira.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @spira-lab/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
spira --help
|
|
15
|
+
spira auth login
|
|
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
|
|
21
|
+
spira workflows create --name "Product launch workflow"
|
|
22
|
+
spira workflows update <workflowId> --workflow-definition-json '{"nodes":[],"edges":[]}'
|
|
23
|
+
```
|
|
24
|
+
|
|
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
|
@@ -20198,6 +20198,260 @@ var cliAiModelDocs = [
|
|
|
20198
20198
|
"first_last_frames_to_video requires exactly 2 referenceImage URLs.",
|
|
20199
20199
|
"extendFromTaskId requires extendProviderId=socheap-media and resolution=720p."
|
|
20200
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."]
|
|
20201
20455
|
}
|
|
20202
20456
|
];
|
|
20203
20457
|
function listCliAiModelDocs(mediaType) {
|
|
@@ -20207,6 +20461,115 @@ function getCliAiModelDoc(modelId) {
|
|
|
20207
20461
|
return cliAiModelDocs.find((model) => model.id === modelId);
|
|
20208
20462
|
}
|
|
20209
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
|
+
|
|
20210
20573
|
// src/commands/definitions.ts
|
|
20211
20574
|
var commonListOptions = [
|
|
20212
20575
|
{
|
|
@@ -20390,7 +20753,10 @@ var commandDefinitions = [
|
|
|
20390
20753
|
},
|
|
20391
20754
|
resultType: "socialSubmissionList",
|
|
20392
20755
|
examples: ["spira social submissions list --platform tiktok --status draft --limit 10"],
|
|
20393
|
-
relatedCommands: [
|
|
20756
|
+
relatedCommands: [
|
|
20757
|
+
"spira social submissions get <submissionId>",
|
|
20758
|
+
"spira social submissions publish <submissionId> --confirm"
|
|
20759
|
+
]
|
|
20394
20760
|
},
|
|
20395
20761
|
{
|
|
20396
20762
|
path: ["social", "submissions", "get"],
|
|
@@ -20436,7 +20802,12 @@ var commandDefinitions = [
|
|
|
20436
20802
|
defaultValue: "oauth_account"
|
|
20437
20803
|
},
|
|
20438
20804
|
{ name: "title", flag: "--title <title>", description: "Draft title.", type: "string" },
|
|
20439
|
-
{
|
|
20805
|
+
{
|
|
20806
|
+
name: "description",
|
|
20807
|
+
flag: "--description <description>",
|
|
20808
|
+
description: "Draft description/text.",
|
|
20809
|
+
type: "string"
|
|
20810
|
+
},
|
|
20440
20811
|
{ name: "mediaUrls", flag: "--media-url <url>", description: "Media URL. Repeatable.", type: "stringArray" },
|
|
20441
20812
|
{ name: "agentId", flag: "--agent-id <agentId>", description: "Associated agent ID.", type: "string" },
|
|
20442
20813
|
{
|
|
@@ -20463,9 +20834,7 @@ var commandDefinitions = [
|
|
|
20463
20834
|
]
|
|
20464
20835
|
},
|
|
20465
20836
|
resultType: "socialSubmissionAction",
|
|
20466
|
-
examples: [
|
|
20467
|
-
'spira social submissions create --platform twitter --content-kind text --description "Launch note"'
|
|
20468
|
-
],
|
|
20837
|
+
examples: ['spira social submissions create --platform twitter --content-kind text --description "Launch note"'],
|
|
20469
20838
|
relatedCommands: [
|
|
20470
20839
|
"spira social submissions get <submissionId>",
|
|
20471
20840
|
"spira social submissions publish <submissionId> --confirm"
|
|
@@ -20476,9 +20845,20 @@ var commandDefinitions = [
|
|
|
20476
20845
|
purpose: "Publish a social submission draft.",
|
|
20477
20846
|
arguments: [{ name: "submissionId", description: "Submission ID.", required: true }],
|
|
20478
20847
|
options: [
|
|
20479
|
-
{
|
|
20848
|
+
{
|
|
20849
|
+
name: "confirm",
|
|
20850
|
+
flag: "--confirm",
|
|
20851
|
+
description: "Required explicit publish confirmation.",
|
|
20852
|
+
type: "boolean",
|
|
20853
|
+
required: true
|
|
20854
|
+
},
|
|
20480
20855
|
{ name: "title", flag: "--title <title>", description: "Platform title override.", type: "string" },
|
|
20481
|
-
{
|
|
20856
|
+
{
|
|
20857
|
+
name: "description",
|
|
20858
|
+
flag: "--description <description>",
|
|
20859
|
+
description: "Platform description override.",
|
|
20860
|
+
type: "string"
|
|
20861
|
+
},
|
|
20482
20862
|
{ name: "text", flag: "--text <text>", description: "Text override for X or LinkedIn.", type: "string" },
|
|
20483
20863
|
{ name: "mediaUrls", flag: "--media-url <url>", description: "Media URL. Repeatable.", type: "stringArray" },
|
|
20484
20864
|
{ name: "privacyLevel", flag: "--privacy-level <value>", description: "TikTok privacy level.", type: "string" },
|
|
@@ -20519,18 +20899,18 @@ var commandDefinitions = [
|
|
|
20519
20899
|
},
|
|
20520
20900
|
{
|
|
20521
20901
|
path: ["ai", "models", "list"],
|
|
20522
|
-
purpose: "List AI image and
|
|
20902
|
+
purpose: "List AI image, video, and audio models available from the CLI.",
|
|
20523
20903
|
options: [
|
|
20524
20904
|
{
|
|
20525
20905
|
name: "mediaType",
|
|
20526
20906
|
flag: "--media-type <type>",
|
|
20527
20907
|
description: "Filter by model media type.",
|
|
20528
20908
|
type: "string",
|
|
20529
|
-
enumValues: ["image", "video"]
|
|
20909
|
+
enumValues: ["image", "video", "audio"]
|
|
20530
20910
|
}
|
|
20531
20911
|
],
|
|
20532
20912
|
resultType: "aiModelList",
|
|
20533
|
-
examples: ["spira ai models list", "spira ai models list --media-type
|
|
20913
|
+
examples: ["spira ai models list", "spira ai models list --media-type audio"],
|
|
20534
20914
|
relatedCommands: ["spira ai models get <modelId>", "spira ai jobs create"]
|
|
20535
20915
|
},
|
|
20536
20916
|
{
|
|
@@ -20543,11 +20923,45 @@ var commandDefinitions = [
|
|
|
20543
20923
|
},
|
|
20544
20924
|
{
|
|
20545
20925
|
path: ["ai", "jobs", "create"],
|
|
20546
|
-
purpose: "Create an AI image or
|
|
20926
|
+
purpose: "Create an AI image, video, or audio generation job.",
|
|
20547
20927
|
options: [
|
|
20548
20928
|
{ name: "modelId", flag: "--model-id <modelId>", description: "AI model ID.", type: "string", required: true },
|
|
20549
|
-
{
|
|
20550
|
-
|
|
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
|
+
}
|
|
20551
20965
|
],
|
|
20552
20966
|
http: {
|
|
20553
20967
|
method: "POST",
|
|
@@ -20560,7 +20974,10 @@ var commandDefinitions = [
|
|
|
20560
20974
|
]
|
|
20561
20975
|
},
|
|
20562
20976
|
resultType: "aiJobAction",
|
|
20563
|
-
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
|
+
],
|
|
20564
20981
|
relatedCommands: ["spira ai jobs status <jobId>", "spira ai models get <modelId>", "spira credits balance"]
|
|
20565
20982
|
},
|
|
20566
20983
|
{
|
|
@@ -20617,10 +21034,7 @@ var commandDefinitions = [
|
|
|
20617
21034
|
query: [{ name: "surface", source: "option", from: "surface" }]
|
|
20618
21035
|
},
|
|
20619
21036
|
resultType: "workflowNodeCatalog",
|
|
20620
|
-
examples: [
|
|
20621
|
-
"spira workflows node-catalog --json",
|
|
20622
|
-
"spira workflows node-catalog --surface admin --json"
|
|
20623
|
-
],
|
|
21037
|
+
examples: ["spira workflows node-catalog --json", "spira workflows node-catalog --surface admin --json"],
|
|
20624
21038
|
relatedCommands: [
|
|
20625
21039
|
"spira workflows create --name <name>",
|
|
20626
21040
|
"spira workflows update <workflowId>",
|
|
@@ -20670,7 +21084,11 @@ var commandDefinitions = [
|
|
|
20670
21084
|
'spira workflows create --name "Product launch workflow"',
|
|
20671
21085
|
`spira workflows create --name "Product launch workflow" --workflow-definition-json '{"nodes":[],"edges":[]}'`
|
|
20672
21086
|
],
|
|
20673
|
-
relatedCommands: [
|
|
21087
|
+
relatedCommands: [
|
|
21088
|
+
"spira workflows get <workflowId>",
|
|
21089
|
+
"spira workflows update <workflowId>",
|
|
21090
|
+
"spira workflows runs start <workflowId>"
|
|
21091
|
+
]
|
|
20674
21092
|
},
|
|
20675
21093
|
{
|
|
20676
21094
|
path: ["workflows", "get"],
|
|
@@ -20808,7 +21226,12 @@ var commandDefinitions = [
|
|
|
20808
21226
|
purpose: "Start a workflow run and return its run ID.",
|
|
20809
21227
|
arguments: [{ name: "workflowId", description: "Workflow ID.", required: true }],
|
|
20810
21228
|
options: [
|
|
20811
|
-
{
|
|
21229
|
+
{
|
|
21230
|
+
name: "startOptionsJson",
|
|
21231
|
+
flag: "--start-options-json <json>",
|
|
21232
|
+
description: "Workflow start options JSON.",
|
|
21233
|
+
type: "json"
|
|
21234
|
+
},
|
|
20812
21235
|
{
|
|
20813
21236
|
name: "surface",
|
|
20814
21237
|
flag: "--surface <surface>",
|
|
@@ -20867,7 +21290,9 @@ var commandDefinitions = [
|
|
|
20867
21290
|
path: ["schedules", "executions"],
|
|
20868
21291
|
purpose: "List persisted schedule execution attempts.",
|
|
20869
21292
|
arguments: [{ name: "scheduleId", description: "Schedule ID.", required: true }],
|
|
20870
|
-
options: [
|
|
21293
|
+
options: [
|
|
21294
|
+
{ name: "limit", flag: "--limit <number>", description: "Maximum executions to return.", type: "number" }
|
|
21295
|
+
],
|
|
20871
21296
|
http: {
|
|
20872
21297
|
method: "GET",
|
|
20873
21298
|
path: "/api/schedules/:scheduleId/executions",
|
|
@@ -20883,11 +21308,28 @@ var commandDefinitions = [
|
|
|
20883
21308
|
path: ["schedules", "calendar"],
|
|
20884
21309
|
purpose: "List scheduled occurrences between two times.",
|
|
20885
21310
|
options: [
|
|
20886
|
-
{
|
|
20887
|
-
|
|
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
|
+
},
|
|
20888
21325
|
{ name: "limit", flag: "--limit <number>", description: "Maximum occurrences to return.", type: "number" },
|
|
20889
21326
|
{ name: "offset", flag: "--offset <number>", description: "Zero-based occurrence offset.", type: "number" },
|
|
20890
|
-
{
|
|
21327
|
+
{
|
|
21328
|
+
name: "scheduleId",
|
|
21329
|
+
flag: "--schedule-id <scheduleId>",
|
|
21330
|
+
description: "Optional schedule id filter.",
|
|
21331
|
+
type: "string"
|
|
21332
|
+
}
|
|
20891
21333
|
],
|
|
20892
21334
|
http: {
|
|
20893
21335
|
method: "GET",
|
|
@@ -20928,7 +21370,12 @@ var commandDefinitions = [
|
|
|
20928
21370
|
{ name: "lineEnd", flag: "--line-end <number>", description: "End line (inclusive).", type: "number" },
|
|
20929
21371
|
{ name: "page", flag: "--page <number>", description: "Start page (1-based, PDFs).", type: "number" },
|
|
20930
21372
|
{ name: "pageEnd", flag: "--page-end <number>", description: "End page (inclusive).", type: "number" },
|
|
20931
|
-
{
|
|
21373
|
+
{
|
|
21374
|
+
name: "expand",
|
|
21375
|
+
flag: "--expand <number>",
|
|
21376
|
+
description: "Extra lines of context around the range.",
|
|
21377
|
+
type: "number"
|
|
21378
|
+
}
|
|
20932
21379
|
],
|
|
20933
21380
|
http: {
|
|
20934
21381
|
method: "GET",
|
|
@@ -20958,7 +21405,12 @@ var commandDefinitions = [
|
|
|
20958
21405
|
options: [
|
|
20959
21406
|
{ name: "scope", flag: "--scope <path>", description: "Restrict to a folder path.", type: "string" },
|
|
20960
21407
|
{ name: "limit", flag: "--limit <number>", description: "Max matches (default 20, max 100).", type: "number" },
|
|
20961
|
-
{
|
|
21408
|
+
{
|
|
21409
|
+
name: "debug",
|
|
21410
|
+
flag: "--debug",
|
|
21411
|
+
description: "Include candidate scoring breakdown (JSON mode).",
|
|
21412
|
+
type: "boolean"
|
|
21413
|
+
}
|
|
20962
21414
|
],
|
|
20963
21415
|
http: {
|
|
20964
21416
|
method: "POST",
|
|
@@ -20982,8 +21434,18 @@ var commandDefinitions = [
|
|
|
20982
21434
|
options: [
|
|
20983
21435
|
{ name: "scope", flag: "--scope <path>", description: "Restrict to a folder path.", type: "string" },
|
|
20984
21436
|
{ name: "limit", flag: "--limit <number>", description: "Max matches (default 20, max 100).", type: "number" },
|
|
20985
|
-
{
|
|
20986
|
-
|
|
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
|
+
}
|
|
20987
21449
|
],
|
|
20988
21450
|
http: {
|
|
20989
21451
|
method: "POST",
|
|
@@ -21026,6 +21488,13 @@ function validateCommandInput(command, args, options) {
|
|
|
21026
21488
|
return validationError(command, option.name, `Expected one of: ${option.enumValues.join(", ")}.`);
|
|
21027
21489
|
}
|
|
21028
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
|
+
}
|
|
21029
21498
|
}
|
|
21030
21499
|
return null;
|
|
21031
21500
|
}
|
|
@@ -21579,6 +22048,43 @@ async function logout() {
|
|
|
21579
22048
|
});
|
|
21580
22049
|
}
|
|
21581
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
|
+
|
|
21582
22088
|
// src/render/human.ts
|
|
21583
22089
|
function valueToText(value) {
|
|
21584
22090
|
if (value === null || value === void 0 || value === "") return "-";
|
|
@@ -21629,11 +22135,11 @@ function renderKeyValues(data) {
|
|
|
21629
22135
|
`;
|
|
21630
22136
|
return Object.entries(data).filter(([, value]) => typeof value !== "object" || value === null).map(([key, value]) => `${key}: ${valueToText(value)}`).join("\n").concat("\n");
|
|
21631
22137
|
}
|
|
21632
|
-
function
|
|
22138
|
+
function objectRecord2(data) {
|
|
21633
22139
|
return data && typeof data === "object" ? data : {};
|
|
21634
22140
|
}
|
|
21635
22141
|
function formatWhen(value) {
|
|
21636
|
-
const record2 =
|
|
22142
|
+
const record2 = objectRecord2(value);
|
|
21637
22143
|
const entries = Object.entries(record2);
|
|
21638
22144
|
if (!entries.length) return "";
|
|
21639
22145
|
return entries.map(([key, item]) => `${key}=${valueToText(item)}`).join(", ");
|
|
@@ -21649,17 +22155,17 @@ function formatCreditOption(option) {
|
|
|
21649
22155
|
function portSummary(ports) {
|
|
21650
22156
|
if (!Array.isArray(ports) || ports.length === 0) return "-";
|
|
21651
22157
|
return ports.map((port) => {
|
|
21652
|
-
const record2 =
|
|
22158
|
+
const record2 = objectRecord2(port);
|
|
21653
22159
|
const suffix = record2.multiple ? "[]" : "";
|
|
21654
22160
|
return `${valueToText(record2.id)}:${valueToText(record2.valueType)}${suffix}`;
|
|
21655
22161
|
}).join(", ");
|
|
21656
22162
|
}
|
|
21657
22163
|
function renderWorkflowNodeCatalog(data) {
|
|
21658
|
-
const record2 =
|
|
22164
|
+
const record2 = objectRecord2(data);
|
|
21659
22165
|
const nodes = Array.isArray(record2.nodes) ? record2.nodes : [];
|
|
21660
22166
|
const rows = nodes.map((node) => {
|
|
21661
|
-
const nodeRecord =
|
|
21662
|
-
const ports =
|
|
22167
|
+
const nodeRecord = objectRecord2(node);
|
|
22168
|
+
const ports = objectRecord2(nodeRecord.ports);
|
|
21663
22169
|
return {
|
|
21664
22170
|
type: nodeRecord.type,
|
|
21665
22171
|
inputs: portSummary(ports.inputs),
|
|
@@ -21673,7 +22179,7 @@ function renderAccountList(data) {
|
|
|
21673
22179
|
const channels = data && typeof data === "object" ? data.publishChannels : null;
|
|
21674
22180
|
if (Array.isArray(channels)) {
|
|
21675
22181
|
const rows = channels.map((channel) => {
|
|
21676
|
-
const record2 =
|
|
22182
|
+
const record2 = objectRecord2(channel);
|
|
21677
22183
|
return {
|
|
21678
22184
|
platform: record2.platform,
|
|
21679
22185
|
connected: record2.oauthAccountsCount,
|
|
@@ -21686,12 +22192,12 @@ function renderAccountList(data) {
|
|
|
21686
22192
|
return renderKeyValues(data);
|
|
21687
22193
|
}
|
|
21688
22194
|
function renderAccountConnect(data) {
|
|
21689
|
-
const record2 =
|
|
22195
|
+
const record2 = objectRecord2(data);
|
|
21690
22196
|
return `platform: ${valueToText(record2.platform)}
|
|
21691
22197
|
`;
|
|
21692
22198
|
}
|
|
21693
22199
|
function renderCreditBalance(data) {
|
|
21694
|
-
const record2 =
|
|
22200
|
+
const record2 = objectRecord2(data);
|
|
21695
22201
|
const lines = [
|
|
21696
22202
|
`availableCredits: ${formatCredits(record2.availableCredits)}`,
|
|
21697
22203
|
`permanentCredits: ${formatCredits(record2.availablePermanentCredits)}`,
|
|
@@ -21718,7 +22224,7 @@ function renderUser(prefix, value) {
|
|
|
21718
22224
|
function renderAuthWhoami(data) {
|
|
21719
22225
|
if (!data || typeof data !== "object") return renderKeyValues(data);
|
|
21720
22226
|
const record2 = data;
|
|
21721
|
-
const effective =
|
|
22227
|
+
const effective = objectRecord2(record2.effectiveUser);
|
|
21722
22228
|
const name = valueToText(effective.name);
|
|
21723
22229
|
const email3 = valueToText(effective.email);
|
|
21724
22230
|
const lines = [`user: ${name}${email3 === "-" ? "" : ` <${email3}>`}`];
|
|
@@ -21733,8 +22239,8 @@ function renderAuthLogin(data) {
|
|
|
21733
22239
|
`;
|
|
21734
22240
|
}
|
|
21735
22241
|
function renderSocialSubmissionDetail(data) {
|
|
21736
|
-
const raw =
|
|
21737
|
-
const nested =
|
|
22242
|
+
const raw = objectRecord2(data);
|
|
22243
|
+
const nested = objectRecord2(raw.data);
|
|
21738
22244
|
const record2 = nested.submission_id || nested.postUrl || nested.tweetId ? {
|
|
21739
22245
|
id: nested.submission_id,
|
|
21740
22246
|
platform: raw.platform,
|
|
@@ -21768,8 +22274,8 @@ function renderSocialSubmissionDetail(data) {
|
|
|
21768
22274
|
`;
|
|
21769
22275
|
}
|
|
21770
22276
|
function renderAiJobStatus(data) {
|
|
21771
|
-
const record2 =
|
|
21772
|
-
const latestExecution =
|
|
22277
|
+
const record2 = objectRecord2(data);
|
|
22278
|
+
const latestExecution = objectRecord2(record2.latestExecution);
|
|
21773
22279
|
const resultUrls = Array.isArray(latestExecution.resultUrls) ? latestExecution.resultUrls : [];
|
|
21774
22280
|
const lines = [
|
|
21775
22281
|
`id: ${valueToText(record2.id)}`,
|
|
@@ -21788,10 +22294,10 @@ function renderAiModelList(data) {
|
|
|
21788
22294
|
return renderRows(dataArray(data), ["id", "mediaType", "creditOptions", "name", "capabilities"]);
|
|
21789
22295
|
}
|
|
21790
22296
|
function renderAiModelDetail(data) {
|
|
21791
|
-
const record2 =
|
|
21792
|
-
const fields = Array.isArray(record2.fields) ? record2.fields.map(
|
|
21793
|
-
const creditOptions = Array.isArray(record2.creditOptions) ? record2.creditOptions.map(
|
|
21794
|
-
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) : [];
|
|
21795
22301
|
const notes = Array.isArray(record2.notes) ? record2.notes : [];
|
|
21796
22302
|
const lines = [
|
|
21797
22303
|
`id: ${valueToText(record2.id)}`,
|
|
@@ -21831,8 +22337,8 @@ function renderAiModelDetail(data) {
|
|
|
21831
22337
|
`;
|
|
21832
22338
|
}
|
|
21833
22339
|
function renderWorkflowDetail(data) {
|
|
21834
|
-
const record2 =
|
|
21835
|
-
const definition =
|
|
22340
|
+
const record2 = objectRecord2(data);
|
|
22341
|
+
const definition = objectRecord2(record2.workflowDefinition);
|
|
21836
22342
|
const nodes = Array.isArray(definition.nodes) ? definition.nodes : [];
|
|
21837
22343
|
const edges = Array.isArray(definition.edges) ? definition.edges : [];
|
|
21838
22344
|
return [
|
|
@@ -21845,9 +22351,9 @@ function renderWorkflowDetail(data) {
|
|
|
21845
22351
|
].join("\n").concat("\n");
|
|
21846
22352
|
}
|
|
21847
22353
|
function renderScheduleDetail(data) {
|
|
21848
|
-
const record2 =
|
|
21849
|
-
const executorConfig =
|
|
21850
|
-
const currentPattern =
|
|
22354
|
+
const record2 = objectRecord2(data);
|
|
22355
|
+
const executorConfig = objectRecord2(record2.executorConfig);
|
|
22356
|
+
const currentPattern = objectRecord2(record2.currentPattern);
|
|
21851
22357
|
return [
|
|
21852
22358
|
`id: ${valueToText(record2.id)}`,
|
|
21853
22359
|
`executorType: ${valueToText(record2.executorType)}`,
|
|
@@ -21888,9 +22394,9 @@ function formatAnchor(anchor) {
|
|
|
21888
22394
|
return parts.join(", ") || "-";
|
|
21889
22395
|
}
|
|
21890
22396
|
function renderDataBaseGlob(data) {
|
|
21891
|
-
const record2 =
|
|
22397
|
+
const record2 = objectRecord2(data);
|
|
21892
22398
|
const pattern = valueToText(record2.pattern);
|
|
21893
|
-
const entries = Array.isArray(record2.entries) ? record2.entries.map(
|
|
22399
|
+
const entries = Array.isArray(record2.entries) ? record2.entries.map(objectRecord2) : [];
|
|
21894
22400
|
if (entries.length === 0) return `pattern: ${pattern}
|
|
21895
22401
|
No entries.
|
|
21896
22402
|
`;
|
|
@@ -21910,11 +22416,11 @@ No entries.
|
|
|
21910
22416
|
${renderRows(rows, ["type", "path", "fileType", "size", "status", "updatedAt"])}`;
|
|
21911
22417
|
}
|
|
21912
22418
|
function renderDataBaseRead(data) {
|
|
21913
|
-
const record2 =
|
|
22419
|
+
const record2 = objectRecord2(data);
|
|
21914
22420
|
const path = valueToText(record2.path);
|
|
21915
22421
|
const fileType = valueToText(record2.fileType);
|
|
21916
22422
|
const mimeType = valueToText(record2.mimeType);
|
|
21917
|
-
const rangeObj =
|
|
22423
|
+
const rangeObj = objectRecord2(record2.range);
|
|
21918
22424
|
let rangeStr = "-";
|
|
21919
22425
|
if (Array.isArray(rangeObj.line) && rangeObj.line.length === 2) {
|
|
21920
22426
|
rangeStr = `lines ${rangeObj.line[0]}-${rangeObj.line[1]}`;
|
|
@@ -21946,14 +22452,14 @@ function renderDataBaseRead(data) {
|
|
|
21946
22452
|
${content}${footer}`;
|
|
21947
22453
|
}
|
|
21948
22454
|
function renderDataBaseMatches(data) {
|
|
21949
|
-
const record2 =
|
|
22455
|
+
const record2 = objectRecord2(data);
|
|
21950
22456
|
const query = valueToText(record2.query);
|
|
21951
|
-
const matches = Array.isArray(record2.matches) ? record2.matches.map(
|
|
22457
|
+
const matches = Array.isArray(record2.matches) ? record2.matches.map(objectRecord2) : [];
|
|
21952
22458
|
if (matches.length === 0) return `query: ${query}
|
|
21953
22459
|
No matches.
|
|
21954
22460
|
`;
|
|
21955
22461
|
const blocks = matches.map((match) => {
|
|
21956
|
-
const anchor = formatAnchor(
|
|
22462
|
+
const anchor = formatAnchor(objectRecord2(match.anchor));
|
|
21957
22463
|
const score = typeof match.score === "number" ? match.score.toFixed(3) : valueToText(match.score);
|
|
21958
22464
|
const fields = Array.isArray(match.matchedFields) ? match.matchedFields.join(",") : "";
|
|
21959
22465
|
const header = `${valueToText(match.path)}(${anchor}) [score=${score}${fields ? ` fields=${fields}` : ""}]`;
|
|
@@ -21983,7 +22489,7 @@ function renderSuccess(result) {
|
|
|
21983
22489
|
case "socialSubmissionList":
|
|
21984
22490
|
return renderRows(
|
|
21985
22491
|
dataArray(result.data).map((item) => {
|
|
21986
|
-
const record2 =
|
|
22492
|
+
const record2 = objectRecord2(item);
|
|
21987
22493
|
return {
|
|
21988
22494
|
id: record2.id,
|
|
21989
22495
|
platform: record2.platform,
|
|
@@ -22080,6 +22586,7 @@ function renderJson(result) {
|
|
|
22080
22586
|
|
|
22081
22587
|
// src/render/index.ts
|
|
22082
22588
|
function renderResult(result, options) {
|
|
22589
|
+
if (options.compactAiJobWait) return renderAiJobWait(result, options);
|
|
22083
22590
|
return options.json ? renderJson(result) : renderHuman(result);
|
|
22084
22591
|
}
|
|
22085
22592
|
|
|
@@ -22232,6 +22739,24 @@ function parseNumber(value) {
|
|
|
22232
22739
|
if (!Number.isFinite(parsed)) throw new InvalidArgumentError("Expected a number.");
|
|
22233
22740
|
return parsed;
|
|
22234
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
|
+
}
|
|
22235
22760
|
function parseJson(value) {
|
|
22236
22761
|
try {
|
|
22237
22762
|
return JSON.parse(value);
|
|
@@ -22250,6 +22775,11 @@ function addOption(command, definition) {
|
|
|
22250
22775
|
command.addOption(option.argParser(parseNumber).default(definition.defaultValue));
|
|
22251
22776
|
return;
|
|
22252
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
|
+
}
|
|
22253
22783
|
if (definition.type === "json") {
|
|
22254
22784
|
command.addOption(option.argParser(parseJson).default(definition.defaultValue));
|
|
22255
22785
|
return;
|
|
@@ -22306,10 +22836,7 @@ function getOrCreateSubcommand(parent, name) {
|
|
|
22306
22836
|
return next;
|
|
22307
22837
|
}
|
|
22308
22838
|
function renderCommandReference(definition) {
|
|
22309
|
-
const lines = [
|
|
22310
|
-
`Command: spira ${definition.path.join(" ")}`,
|
|
22311
|
-
`Description: ${definition.purpose}`
|
|
22312
|
-
];
|
|
22839
|
+
const lines = [`Command: spira ${definition.path.join(" ")}`, `Description: ${definition.purpose}`];
|
|
22313
22840
|
if (definition.arguments?.length) {
|
|
22314
22841
|
lines.push("Arguments:");
|
|
22315
22842
|
for (const argument of definition.arguments) {
|
|
@@ -22338,8 +22865,8 @@ function renderCommandIndex() {
|
|
|
22338
22865
|
`;
|
|
22339
22866
|
}
|
|
22340
22867
|
async function packageVersion() {
|
|
22341
|
-
if ("0.0.
|
|
22342
|
-
return "0.0.
|
|
22868
|
+
if ("0.0.6".length > 0) {
|
|
22869
|
+
return "0.0.6";
|
|
22343
22870
|
}
|
|
22344
22871
|
try {
|
|
22345
22872
|
const packageUrl = new URL("../package.json", import_meta.url);
|
|
@@ -22512,7 +23039,38 @@ ${url2}
|
|
|
22512
23039
|
resultType: "aiModelDetail",
|
|
22513
23040
|
command: "spira ai models get",
|
|
22514
23041
|
data: publicAiModelDoc(model),
|
|
22515
|
-
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: {} })
|
|
22516
23074
|
});
|
|
22517
23075
|
}
|
|
22518
23076
|
return executeHttpCommand({ command: definition, args, options });
|
|
@@ -22531,11 +23089,18 @@ function addLeafCommand(root, definition, output) {
|
|
|
22531
23089
|
leaf.action(async (...values) => {
|
|
22532
23090
|
const command = values[values.length - 1];
|
|
22533
23091
|
const positional = values.slice(0, -1).map((value) => value === void 0 ? "" : String(value));
|
|
22534
|
-
const args = Object.fromEntries(
|
|
23092
|
+
const args = Object.fromEntries(
|
|
23093
|
+
(definition.arguments ?? []).map((argument, index) => [argument.name, positional[index] ?? ""])
|
|
23094
|
+
);
|
|
22535
23095
|
const options = command.optsWithGlobals();
|
|
22536
23096
|
const validationResult2 = validateCommandInput(definition, args, options);
|
|
22537
23097
|
const result = validationResult2 ?? await executeDefinition(definition, args, options, output);
|
|
22538
|
-
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
|
+
);
|
|
22539
23104
|
if (result.status === "error") process.exitCode = 1;
|
|
22540
23105
|
});
|
|
22541
23106
|
parent.addCommand(leaf);
|
package/package.json
CHANGED
|
@@ -1,40 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spira-lab/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Command line client for Spira.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.cjs",
|
|
7
6
|
"bin": {
|
|
8
7
|
"spira": "dist/index.cjs"
|
|
9
8
|
},
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
10
|
"engines": {
|
|
11
11
|
"node": ">=20.0.0"
|
|
12
12
|
},
|
|
13
13
|
"publishConfig": {
|
|
14
14
|
"access": "public",
|
|
15
15
|
"registry": "https://registry.npmjs.org/"
|
|
16
|
-
},
|
|
17
|
-
"files": [
|
|
18
|
-
"dist",
|
|
19
|
-
"package.json",
|
|
20
|
-
"README.md"
|
|
21
|
-
],
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "pnpm --filter @spira-ai/contracts build && node scripts/build.mjs && node scripts/fix-bin-mode.mjs",
|
|
24
|
-
"dev": "tsx src/index.ts",
|
|
25
|
-
"publish:public": "node scripts/publish.mjs",
|
|
26
|
-
"typecheck": "tsgo --noEmit -p tsconfig.json",
|
|
27
|
-
"test": "vitest run"
|
|
28
|
-
},
|
|
29
|
-
"devDependencies": {
|
|
30
|
-
"@typescript/native-preview": "catalog:",
|
|
31
|
-
"@spira-ai/contracts": "workspace:*",
|
|
32
|
-
"@types/node": "catalog:",
|
|
33
|
-
"commander": "^14.0.2",
|
|
34
|
-
"esbuild": "^0.27.1",
|
|
35
|
-
"tsx": "^4.22.4",
|
|
36
|
-
"typescript": "catalog:",
|
|
37
|
-
"vitest": "catalog:",
|
|
38
|
-
"zod": "catalog:"
|
|
39
16
|
}
|
|
40
17
|
}
|