@helix3/helix-cli 0.1.13-helix3.74 → 0.1.13-helix3.75
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/dist/audioCommand.d.ts +50 -0
- package/dist/audioCommand.js +132 -61
- package/dist/audioCommand.js.map +1 -1
- package/dist/index.js +128 -12
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +10 -1
- package/dist/lib.js +14 -2
- package/dist/lib.js.map +1 -1
- package/dist/money.d.ts +31 -0
- package/dist/money.js +50 -2
- package/dist/money.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/world/analyze.d.ts +6 -1
- package/dist/world/analyze.js +94 -0
- package/dist/world/analyze.js.map +1 -1
- package/package.json +1 -1
package/dist/audioCommand.d.ts
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import { type GenerateAudioInput, type StandaloneGeneratedAsset } from './assets';
|
|
3
3
|
import type { CliCredentials } from './config';
|
|
4
|
+
type GenerateAudioOptions = {
|
|
5
|
+
mode: string;
|
|
6
|
+
additionalPrompt?: string;
|
|
7
|
+
model?: string;
|
|
8
|
+
outputFormat?: string;
|
|
9
|
+
projectId?: string;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
durationSeconds?: string;
|
|
13
|
+
loop?: boolean;
|
|
14
|
+
promptInfluence?: string;
|
|
15
|
+
forceInstrumental?: boolean;
|
|
16
|
+
text?: string;
|
|
17
|
+
voiceId?: string;
|
|
18
|
+
languageCode?: string;
|
|
19
|
+
seed?: string;
|
|
20
|
+
stability?: string;
|
|
21
|
+
similarityBoost?: string;
|
|
22
|
+
style?: string;
|
|
23
|
+
speed?: string;
|
|
24
|
+
speakerBoost?: boolean;
|
|
25
|
+
pronunciationDictionaryIds?: string;
|
|
26
|
+
output?: string;
|
|
27
|
+
};
|
|
4
28
|
export type GenerateAudioCommandDependencies = {
|
|
5
29
|
requireAuth: () => CliCredentials;
|
|
6
30
|
generateAudio: (creds: CliCredentials, input: GenerateAudioInput, options?: {
|
|
@@ -12,4 +36,30 @@ export type GenerateAudioCommandDependencies = {
|
|
|
12
36
|
printJson: (value: unknown) => void;
|
|
13
37
|
fail: (message: string) => never;
|
|
14
38
|
};
|
|
39
|
+
export declare const AUDIO_MODES: readonly ["sound_effect", "music", "text_to_speech"];
|
|
40
|
+
export declare const AUDIO_OPTIONS: {
|
|
41
|
+
flags: string;
|
|
42
|
+
description: string;
|
|
43
|
+
default?: string;
|
|
44
|
+
shared?: boolean;
|
|
45
|
+
}[];
|
|
46
|
+
/** Attach the audio flags. `skipShared` omits the ones the target command already declares. */
|
|
47
|
+
export declare function addAudioOptions(command: Command, opts?: {
|
|
48
|
+
skipShared?: boolean;
|
|
49
|
+
}): Command;
|
|
50
|
+
export type AudioFlagValues = Omit<GenerateAudioOptions, 'output'>;
|
|
51
|
+
/**
|
|
52
|
+
* Parse the audio flags into the library's input shape. Shared by both routes so a flag can never
|
|
53
|
+
* mean one thing synchronously and another durably.
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildAudioInput(prompt: string | undefined, opts: AudioFlagValues, fail: (message: string) => never): GenerateAudioInput;
|
|
56
|
+
/**
|
|
57
|
+
* Everything that can be known to be wrong BEFORE any LIX moves. Returns the problems rather than
|
|
58
|
+
* throwing, so callers can render them all at once with the fix named.
|
|
59
|
+
*
|
|
60
|
+
* The voice check is deliberately NOT done here — it needs the catalog, and a network lookup that
|
|
61
|
+
* fails must not become a new way to block a valid job.
|
|
62
|
+
*/
|
|
63
|
+
export declare function validateAudioRequest(prompt: string | undefined, opts: AudioFlagValues): string[];
|
|
15
64
|
export declare function registerGenerateAudioCommand(assets: Command, dependencies: GenerateAudioCommandDependencies): void;
|
|
65
|
+
export {};
|
package/dist/audioCommand.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AUDIO_OPTIONS = exports.AUDIO_MODES = void 0;
|
|
4
|
+
exports.addAudioOptions = addAudioOptions;
|
|
5
|
+
exports.buildAudioInput = buildAudioInput;
|
|
6
|
+
exports.validateAudioRequest = validateAudioRequest;
|
|
3
7
|
exports.registerGenerateAudioCommand = registerGenerateAudioCommand;
|
|
4
8
|
const api_1 = require("./api");
|
|
5
9
|
const assets_1 = require("./assets");
|
|
6
|
-
|
|
10
|
+
exports.AUDIO_MODES = [
|
|
7
11
|
'sound_effect',
|
|
8
12
|
'music',
|
|
9
13
|
'text_to_speech',
|
|
@@ -24,73 +28,140 @@ function commaList(value) {
|
|
|
24
28
|
.filter(Boolean);
|
|
25
29
|
return values?.length ? values : undefined;
|
|
26
30
|
}
|
|
31
|
+
// The audio flags, as DATA, so the synchronous route (`generate-audio`) and the durable route
|
|
32
|
+
// (`assets start audio`) attach the SAME names from one definition. They drifted once already:
|
|
33
|
+
// `start` accepted no mode/text/voice flags at all, so the async path agents are told to prefer
|
|
34
|
+
// could only ever produce a sound effect, and music and speech were reachable only by blocking the
|
|
35
|
+
// call with no job id to poll.
|
|
36
|
+
//
|
|
37
|
+
// `start` already declares --title/--additional-prompt/--project-id/--session-id/--duration-seconds
|
|
38
|
+
// /--loop for every kind, so those are marked shared and skipped when attaching there.
|
|
39
|
+
exports.AUDIO_OPTIONS = [
|
|
40
|
+
{ flags: '--mode <mode>', description: 'sound_effect, music, or text_to_speech', default: 'sound_effect' },
|
|
41
|
+
{ flags: '--additional-prompt <text>', description: 'additional generation guidance', shared: true },
|
|
42
|
+
{ flags: '--model <model>', description: 'an allowlisted ElevenLabs model id for the selected --mode' },
|
|
43
|
+
{ flags: '--output-format <format>', description: 'portable MP3 output format, e.g. mp3_44100_128' },
|
|
44
|
+
{ flags: '--project-id <id>', description: 'Dreamer project id', shared: true },
|
|
45
|
+
{ flags: '--session-id <id>', description: 'Dreamer session id', shared: true },
|
|
46
|
+
{ flags: '--title <title>', description: 'asset title', shared: true },
|
|
47
|
+
{ flags: '--duration-seconds <n>', description: 'requested duration — sound_effect: 0.5-30s; music: 3-600s', shared: true },
|
|
48
|
+
{ flags: '--loop', description: 'sound_effect only: request a seamless loop', shared: true },
|
|
49
|
+
{ flags: '--prompt-influence <n>', description: 'sound_effect only: how closely to follow the prompt (0-1)' },
|
|
50
|
+
{ flags: '--force-instrumental', description: 'music only: request an instrumental-only composition' },
|
|
51
|
+
{ flags: '--text <text>', description: 'text_to_speech only: the text to convert to speech' },
|
|
52
|
+
{ flags: '--voice-id <id>', description: 'text_to_speech only: the voice id to speak with (see `helix assets voices`)' },
|
|
53
|
+
{ flags: '--language-code <code>', description: 'text_to_speech only: ISO 639-1 language code' },
|
|
54
|
+
{ flags: '--seed <n>', description: 'text_to_speech only: a seed for deterministic output' },
|
|
55
|
+
{ flags: '--stability <n>', description: 'text_to_speech only: voice stability (0-1)' },
|
|
56
|
+
{ flags: '--similarity-boost <n>', description: 'text_to_speech only: voice similarity boost (0-1)' },
|
|
57
|
+
{ flags: '--style <n>', description: 'text_to_speech only: voice style (0-1)' },
|
|
58
|
+
{ flags: '--speed <n>', description: 'text_to_speech only: voice speed (0.7-1.2)' },
|
|
59
|
+
{ flags: '--speaker-boost', description: 'text_to_speech only: enable speaker boost' },
|
|
60
|
+
{
|
|
61
|
+
flags: '--pronunciation-dictionary-ids <a,b,c>',
|
|
62
|
+
description: 'text_to_speech only: up to 3 comma-separated pronunciation dictionary ids',
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
/** Attach the audio flags. `skipShared` omits the ones the target command already declares. */
|
|
66
|
+
function addAudioOptions(command, opts = {}) {
|
|
67
|
+
for (const option of exports.AUDIO_OPTIONS) {
|
|
68
|
+
if (opts.skipShared && option.shared)
|
|
69
|
+
continue;
|
|
70
|
+
if (option.default === undefined)
|
|
71
|
+
command.option(option.flags, option.description);
|
|
72
|
+
else
|
|
73
|
+
command.option(option.flags, option.description, option.default);
|
|
74
|
+
}
|
|
75
|
+
return command;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Parse the audio flags into the library's input shape. Shared by both routes so a flag can never
|
|
79
|
+
* mean one thing synchronously and another durably.
|
|
80
|
+
*/
|
|
81
|
+
function buildAudioInput(prompt, opts, fail) {
|
|
82
|
+
const stability = optionalNumber(opts.stability, '--stability', fail);
|
|
83
|
+
const similarityBoost = optionalNumber(opts.similarityBoost, '--similarity-boost', fail);
|
|
84
|
+
const style = optionalNumber(opts.style, '--style', fail);
|
|
85
|
+
const speed = optionalNumber(opts.speed, '--speed', fail);
|
|
86
|
+
const voiceSettings = stability === undefined &&
|
|
87
|
+
similarityBoost === undefined &&
|
|
88
|
+
style === undefined &&
|
|
89
|
+
speed === undefined &&
|
|
90
|
+
opts.speakerBoost === undefined
|
|
91
|
+
? undefined
|
|
92
|
+
: { stability, similarityBoost, style, speed, speakerBoost: opts.speakerBoost };
|
|
93
|
+
return {
|
|
94
|
+
prompt,
|
|
95
|
+
audioMode: opts.mode,
|
|
96
|
+
additionalPrompt: opts.additionalPrompt,
|
|
97
|
+
model: opts.model,
|
|
98
|
+
outputFormat: opts.outputFormat,
|
|
99
|
+
projectId: opts.projectId,
|
|
100
|
+
sessionId: opts.sessionId,
|
|
101
|
+
title: opts.title,
|
|
102
|
+
durationSeconds: optionalNumber(opts.durationSeconds, '--duration-seconds', fail),
|
|
103
|
+
loop: opts.loop,
|
|
104
|
+
promptInfluence: optionalNumber(opts.promptInfluence, '--prompt-influence', fail),
|
|
105
|
+
forceInstrumental: opts.forceInstrumental,
|
|
106
|
+
text: opts.text,
|
|
107
|
+
voiceId: opts.voiceId,
|
|
108
|
+
languageCode: opts.languageCode,
|
|
109
|
+
seed: optionalNumber(opts.seed, '--seed', fail),
|
|
110
|
+
voiceSettings,
|
|
111
|
+
pronunciationDictionaryIds: commaList(opts.pronunciationDictionaryIds),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Everything that can be known to be wrong BEFORE any LIX moves. Returns the problems rather than
|
|
116
|
+
* throwing, so callers can render them all at once with the fix named.
|
|
117
|
+
*
|
|
118
|
+
* The voice check is deliberately NOT done here — it needs the catalog, and a network lookup that
|
|
119
|
+
* fails must not become a new way to block a valid job.
|
|
120
|
+
*/
|
|
121
|
+
function validateAudioRequest(prompt, opts) {
|
|
122
|
+
const problems = [];
|
|
123
|
+
const mode = opts.mode;
|
|
124
|
+
if (!exports.AUDIO_MODES.includes(mode)) {
|
|
125
|
+
problems.push(`--mode must be ${exports.AUDIO_MODES.join(', ')}; got '${opts.mode}'`);
|
|
126
|
+
return problems; // every other rule is mode-specific
|
|
127
|
+
}
|
|
128
|
+
if (mode === 'text_to_speech') {
|
|
129
|
+
if (!opts.text?.trim()) {
|
|
130
|
+
problems.push('text_to_speech needs --text "<what to say>" (the prompt argument is not used for speech)');
|
|
131
|
+
}
|
|
132
|
+
if (!opts.voiceId?.trim()) {
|
|
133
|
+
problems.push('text_to_speech needs --voice-id <id> — list them with `helix assets voices`');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (!prompt?.trim()) {
|
|
137
|
+
problems.push(`${mode} needs a prompt argument describing the audio to generate`);
|
|
138
|
+
}
|
|
139
|
+
if (mode !== 'music' && opts.forceInstrumental) {
|
|
140
|
+
problems.push('--force-instrumental applies to music only');
|
|
141
|
+
}
|
|
142
|
+
if (mode !== 'sound_effect' && opts.loop) {
|
|
143
|
+
problems.push('--loop applies to sound_effect only');
|
|
144
|
+
}
|
|
145
|
+
if (mode !== 'sound_effect' && opts.promptInfluence !== undefined) {
|
|
146
|
+
problems.push('--prompt-influence applies to sound_effect only');
|
|
147
|
+
}
|
|
148
|
+
return problems;
|
|
149
|
+
}
|
|
27
150
|
function registerGenerateAudioCommand(assets, dependencies) {
|
|
28
|
-
assets
|
|
151
|
+
const command = assets
|
|
29
152
|
.command('generate-audio')
|
|
30
|
-
.description('Generate a sound effect, music track, or spoken line via ElevenLabs (through Dreamer), auto-publish it to Vault, and optionally save it locally')
|
|
31
|
-
.argument('[prompt]', 'generation prompt — required for sound_effect/music; text_to_speech uses --text instead')
|
|
32
|
-
|
|
33
|
-
.option('--additional-prompt <text>', 'additional generation guidance')
|
|
34
|
-
.option('--model <model>', 'an allowlisted ElevenLabs model id for the selected --mode')
|
|
35
|
-
.option('--output-format <format>', 'portable MP3 output format, e.g. mp3_44100_128')
|
|
36
|
-
.option('--project-id <id>', 'Dreamer project id')
|
|
37
|
-
.option('--session-id <id>', 'Dreamer session id')
|
|
38
|
-
.option('--title <title>', 'asset title')
|
|
39
|
-
.option('--duration-seconds <n>', 'requested duration — sound_effect: 0.5-30s; music: 3-600s')
|
|
40
|
-
.option('--loop', 'sound_effect only: request a seamless loop')
|
|
41
|
-
.option('--prompt-influence <n>', 'sound_effect only: how closely to follow the prompt (0-1)')
|
|
42
|
-
.option('--force-instrumental', 'music only: request an instrumental-only composition')
|
|
43
|
-
.option('--text <text>', 'text_to_speech only: the text to convert to speech')
|
|
44
|
-
.option('--voice-id <id>', 'text_to_speech only: the ElevenLabs voice id to speak with')
|
|
45
|
-
.option('--language-code <code>', 'text_to_speech only: ISO 639-1 language code')
|
|
46
|
-
.option('--seed <n>', 'text_to_speech only: a seed for deterministic output')
|
|
47
|
-
.option('--stability <n>', 'text_to_speech only: voice stability (0-1)')
|
|
48
|
-
.option('--similarity-boost <n>', 'text_to_speech only: voice similarity boost (0-1)')
|
|
49
|
-
.option('--style <n>', 'text_to_speech only: voice style (0-1)')
|
|
50
|
-
.option('--speed <n>', 'text_to_speech only: voice speed (0.7-1.2)')
|
|
51
|
-
.option('--speaker-boost', 'text_to_speech only: enable speaker boost')
|
|
52
|
-
.option('--pronunciation-dictionary-ids <a,b,c>', 'text_to_speech only: up to 3 comma-separated pronunciation dictionary ids')
|
|
153
|
+
.description('Generate a sound effect, music track, or spoken line via ElevenLabs (through Dreamer), auto-publish it to Vault, and optionally save it locally. Blocks until done — use `helix assets start audio` for a pollable job.')
|
|
154
|
+
.argument('[prompt]', 'generation prompt — required for sound_effect/music; text_to_speech uses --text instead');
|
|
155
|
+
addAudioOptions(command)
|
|
53
156
|
.option('--output <path>', 'save the generated audio file to this path')
|
|
54
157
|
.action(async (prompt, opts) => {
|
|
55
|
-
|
|
56
|
-
|
|
158
|
+
const problems = validateAudioRequest(prompt, opts);
|
|
159
|
+
if (problems.length) {
|
|
160
|
+
dependencies.fail(`✖ ${problems.join('\n ✖ ')}`);
|
|
57
161
|
}
|
|
58
|
-
const durationSeconds = optionalNumber(opts.durationSeconds, '--duration-seconds', dependencies.fail);
|
|
59
|
-
const promptInfluence = optionalNumber(opts.promptInfluence, '--prompt-influence', dependencies.fail);
|
|
60
|
-
const seed = optionalNumber(opts.seed, '--seed', dependencies.fail);
|
|
61
|
-
const stability = optionalNumber(opts.stability, '--stability', dependencies.fail);
|
|
62
|
-
const similarityBoost = optionalNumber(opts.similarityBoost, '--similarity-boost', dependencies.fail);
|
|
63
|
-
const style = optionalNumber(opts.style, '--style', dependencies.fail);
|
|
64
|
-
const speed = optionalNumber(opts.speed, '--speed', dependencies.fail);
|
|
65
|
-
const voiceSettings = stability === undefined &&
|
|
66
|
-
similarityBoost === undefined &&
|
|
67
|
-
style === undefined &&
|
|
68
|
-
speed === undefined &&
|
|
69
|
-
opts.speakerBoost === undefined
|
|
70
|
-
? undefined
|
|
71
|
-
: { stability, similarityBoost, style, speed, speakerBoost: opts.speakerBoost };
|
|
72
162
|
const creds = dependencies.requireAuth();
|
|
73
163
|
try {
|
|
74
|
-
const job = await dependencies.generateAudio(creds, {
|
|
75
|
-
prompt,
|
|
76
|
-
audioMode: opts.mode,
|
|
77
|
-
additionalPrompt: opts.additionalPrompt,
|
|
78
|
-
model: opts.model,
|
|
79
|
-
outputFormat: opts.outputFormat,
|
|
80
|
-
projectId: opts.projectId,
|
|
81
|
-
sessionId: opts.sessionId,
|
|
82
|
-
title: opts.title,
|
|
83
|
-
durationSeconds,
|
|
84
|
-
loop: opts.loop,
|
|
85
|
-
promptInfluence,
|
|
86
|
-
forceInstrumental: opts.forceInstrumental,
|
|
87
|
-
text: opts.text,
|
|
88
|
-
voiceId: opts.voiceId,
|
|
89
|
-
languageCode: opts.languageCode,
|
|
90
|
-
seed,
|
|
91
|
-
voiceSettings,
|
|
92
|
-
pronunciationDictionaryIds: commaList(opts.pronunciationDictionaryIds),
|
|
93
|
-
}, { timeoutMs: assets_1.AUDIO_GENERATION_TIMEOUT_MS });
|
|
164
|
+
const job = await dependencies.generateAudio(creds, buildAudioInput(prompt, opts, dependencies.fail), { timeoutMs: assets_1.AUDIO_GENERATION_TIMEOUT_MS });
|
|
94
165
|
if (opts.output && job.status === 'succeeded' && job.asset) {
|
|
95
166
|
await dependencies.saveGeneratedAsset(job.asset, opts.output, {
|
|
96
167
|
apiUrl: creds.apiUrl,
|
package/dist/audioCommand.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audioCommand.js","sourceRoot":"","sources":["../src/audioCommand.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"audioCommand.js","sourceRoot":"","sources":["../src/audioCommand.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -299,7 +299,25 @@ program
|
|
|
299
299
|
.argument('[directory]', 'bundle directory', '.')
|
|
300
300
|
.option('--thumbnail <file>', 'cover image (png, jpg/jpeg, webp, avif, or gif); otherwise uses thumbnail.<ext> at the bundle root')
|
|
301
301
|
.option('--skip-subpath-check', 'skip the pre-publish sub-path load check (not recommended: it is the only gate that serves the bundle the way production does)')
|
|
302
|
+
.option('--description <text>', `catalog description, max ${lib_1.WORLD_DESCRIPTION_MAX} chars (pass "" to clear). helix.json cannot carry this — the manifest schema forbids unknown fields`)
|
|
303
|
+
.option('--description-file <path>', 'read the catalog description from a file')
|
|
302
304
|
.action(async (directory, opts) => {
|
|
305
|
+
// Pure input checks first — bad flags should fail before auth, the network, or any upload.
|
|
306
|
+
if (opts.description !== undefined && opts.descriptionFile) {
|
|
307
|
+
fail('✖ Pass --description or --description-file, not both');
|
|
308
|
+
}
|
|
309
|
+
let description = opts.description;
|
|
310
|
+
if (opts.descriptionFile) {
|
|
311
|
+
try {
|
|
312
|
+
description = (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(opts.descriptionFile), 'utf8').trim();
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
fail(`✖ could not read --description-file: ${err instanceof Error ? err.message : String(err)}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (description !== undefined && description.length > lib_1.WORLD_DESCRIPTION_MAX) {
|
|
319
|
+
fail(`✖ description is ${description.length} chars; the limit is ${lib_1.WORLD_DESCRIPTION_MAX}. Nothing was published.`);
|
|
320
|
+
}
|
|
303
321
|
const creds = requireAuth();
|
|
304
322
|
const mismatch = (0, config_1.credsEnvMismatchWarning)(creds.apiUrl);
|
|
305
323
|
if (mismatch)
|
|
@@ -325,7 +343,10 @@ program
|
|
|
325
343
|
}
|
|
326
344
|
}
|
|
327
345
|
try {
|
|
328
|
-
const result = await (0, lib_1.publishWorld)((0, node_path_1.resolve)(directory), creds, (msg) => console.log(msg), {
|
|
346
|
+
const result = await (0, lib_1.publishWorld)((0, node_path_1.resolve)(directory), creds, (msg) => console.log(msg), {
|
|
347
|
+
thumbnail: opts.thumbnail ? (0, node_path_1.resolve)(opts.thumbnail) : undefined,
|
|
348
|
+
description,
|
|
349
|
+
});
|
|
329
350
|
console.log(`✔ Published "${result.title}" build ${result.buildNumber}`);
|
|
330
351
|
if (result.playUrl)
|
|
331
352
|
console.log(` play: ${result.playUrl}`);
|
|
@@ -816,11 +837,14 @@ function buildQuoteInput(kind, prompt, opts) {
|
|
|
816
837
|
* A quote failure never blocks the generation (the server prices it anyway); it warns, because a
|
|
817
838
|
* hard dependency on an advisory call would make the estimate a new way to fail.
|
|
818
839
|
*/
|
|
819
|
-
async function showEstimateBeforeSpending(creds,
|
|
840
|
+
async function showEstimateBeforeSpending(creds,
|
|
841
|
+
// The EXACT body the generation call will send. Passing the real request (rather than rebuilding
|
|
842
|
+
// an approximation of it) is what stops the quoted price and the charged price drifting apart.
|
|
843
|
+
input, opts) {
|
|
820
844
|
if (opts.estimate === false)
|
|
821
845
|
return;
|
|
822
846
|
try {
|
|
823
|
-
const quote = await (0, lib_1.quoteGeneration)(creds,
|
|
847
|
+
const quote = await (0, lib_1.quoteGeneration)(creds, input);
|
|
824
848
|
const balance = await (0, lib_1.getLixBalance)(creds).catch(() => null);
|
|
825
849
|
console.log((0, lib_1.formatQuote)(quote, balance));
|
|
826
850
|
const maxLix = optionalNumber(typeof opts.maxLix === 'string' ? opts.maxLix : undefined, '--max-lix');
|
|
@@ -1140,11 +1164,14 @@ assets
|
|
|
1140
1164
|
fail(`✖ ${err instanceof Error ? err.message : String(err)}`);
|
|
1141
1165
|
}
|
|
1142
1166
|
});
|
|
1143
|
-
assets
|
|
1167
|
+
const assetsStart = assets
|
|
1144
1168
|
.command('start')
|
|
1145
|
-
.description('Start one generation job without waiting; prop/character use Dreamer, other supported kinds use the shared asset pipeline')
|
|
1169
|
+
.description('Start one generation job without waiting; prop/character use Dreamer, other supported kinds use the shared asset pipeline. For audio, --mode selects sound_effect (default), music, or text_to_speech.')
|
|
1146
1170
|
.argument('<kind>', 'prop, character, image, material, audio, gaussian_splat, or animation (explicit unavailable stub)')
|
|
1147
|
-
|
|
1171
|
+
// Optional at the parser level ON PURPOSE: text_to_speech takes its content from --text, and a
|
|
1172
|
+
// commander-level "missing required argument 'prompt'" would be actively misleading there.
|
|
1173
|
+
// Required-ness is enforced below, per kind, with a message that names the right flag.
|
|
1174
|
+
.argument('[prompt]', 'asset prompt (sound_effect/music/other kinds; speech uses --text)')
|
|
1148
1175
|
.option('--title <title>', 'asset title')
|
|
1149
1176
|
.option('--additional-prompt <text>', 'additional generation guidance')
|
|
1150
1177
|
.option('--target-polycount <n>', 'prop/character triangle target')
|
|
@@ -1156,7 +1183,11 @@ assets
|
|
|
1156
1183
|
.option('--repeat-per-meter <n>', 'material physical scale: tile repeats per metre')
|
|
1157
1184
|
.option('--splat-scope <scope>', 'Gaussian-splat scope: object or environment', 'environment')
|
|
1158
1185
|
.option('--max-lix <n>', 'refuse to start if the estimate exceeds this many LIX')
|
|
1159
|
-
.option('--no-estimate', 'skip the up-front cost estimate')
|
|
1186
|
+
.option('--no-estimate', 'skip the up-front cost estimate');
|
|
1187
|
+
// The audio surface, attached from the SAME definition `generate-audio` uses (minus the flags
|
|
1188
|
+
// `start` already declares for every kind), so the durable route reaches music and speech with
|
|
1189
|
+
// identical flag names instead of only ever producing a sound effect.
|
|
1190
|
+
(0, audioCommand_1.addAudioOptions)(assetsStart, { skipShared: true })
|
|
1160
1191
|
.action(async (kind, prompt, opts) => {
|
|
1161
1192
|
const supported = [
|
|
1162
1193
|
'prop',
|
|
@@ -1170,6 +1201,13 @@ assets
|
|
|
1170
1201
|
if (!supported.includes(kind)) {
|
|
1171
1202
|
fail(`✖ kind must be ${supported.join(', ')}; got '${kind}'`);
|
|
1172
1203
|
}
|
|
1204
|
+
// Every kind except audio takes its subject from the prompt argument.
|
|
1205
|
+
if (kind !== 'audio' && !prompt?.trim()) {
|
|
1206
|
+
fail(`✖ ${kind} needs a prompt argument describing what to generate`);
|
|
1207
|
+
}
|
|
1208
|
+
// Past the guard above, only the audio path can still have an absent prompt, and that path
|
|
1209
|
+
// never reads this binding (speech uses --text; sound_effect/music are checked separately).
|
|
1210
|
+
const promptText = prompt ?? '';
|
|
1173
1211
|
const resolution = optionalNumber(opts.resolution, '--resolution');
|
|
1174
1212
|
if (resolution !== 1024 && resolution !== 2048) {
|
|
1175
1213
|
fail('✖ --resolution must be 1024 or 2048');
|
|
@@ -1178,22 +1216,70 @@ assets
|
|
|
1178
1216
|
!['object', 'environment'].includes(opts.splatScope)) {
|
|
1179
1217
|
fail('✖ --splat-scope must be object or environment');
|
|
1180
1218
|
}
|
|
1219
|
+
// Audio is the one kind whose request shape is mode-dependent, so build and check it before
|
|
1220
|
+
// anything else. Until now `start` accepted no audio flags at all, which meant the durable
|
|
1221
|
+
// route agents are told to prefer could only ever emit a sound effect: music and speech were
|
|
1222
|
+
// reachable only by blocking the call, with no job id to poll.
|
|
1223
|
+
let audioInput = null;
|
|
1224
|
+
if (kind === 'audio') {
|
|
1225
|
+
const problems = (0, audioCommand_1.validateAudioRequest)(prompt, opts);
|
|
1226
|
+
if (problems.length) {
|
|
1227
|
+
fail(`✖ ${problems.join('\n ✖ ')}`);
|
|
1228
|
+
}
|
|
1229
|
+
audioInput = {
|
|
1230
|
+
kind,
|
|
1231
|
+
...(0, audioCommand_1.buildAudioInput)(prompt, opts, fail),
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1181
1234
|
const creds = requireAuth();
|
|
1182
|
-
|
|
1235
|
+
// A voice id that is not in the catalog fails HERE, before the quote and before any LIX
|
|
1236
|
+
// moves. A lookup that cannot run only warns — an advisory check must not become a new way
|
|
1237
|
+
// to block a valid job.
|
|
1238
|
+
if (audioInput && typeof audioInput.voiceId === 'string') {
|
|
1239
|
+
const voiceId = audioInput.voiceId;
|
|
1240
|
+
try {
|
|
1241
|
+
const known = await (0, lib_1.listDreamerVoices)(creds, { search: voiceId, pageSize: 100 });
|
|
1242
|
+
const match = known.voices.find((v) => v.id === voiceId);
|
|
1243
|
+
if (!match) {
|
|
1244
|
+
const nearby = known.voices.slice(0, 5).map((v) => `${v.id} (${v.name})`);
|
|
1245
|
+
fail(`✖ No voice "${voiceId}" in the catalog. Nothing was charged.\n` +
|
|
1246
|
+
` List them with: helix assets voices\n` +
|
|
1247
|
+
(nearby.length ? ` Close matches: ${nearby.join(', ')}` : ''));
|
|
1248
|
+
}
|
|
1249
|
+
const languageCode = audioInput.languageCode;
|
|
1250
|
+
if (typeof languageCode === 'string' &&
|
|
1251
|
+
match.languages.length &&
|
|
1252
|
+
!match.languages.some((l) => l.code === languageCode)) {
|
|
1253
|
+
fail(`✖ Voice "${match.name}" does not support language "${languageCode}". Nothing was charged.\n` +
|
|
1254
|
+
` It supports: ${match.languages.map((l) => l.code).join(', ')}`);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
catch (err) {
|
|
1258
|
+
if (err instanceof lib_1.ApiError) {
|
|
1259
|
+
console.warn(` • could not verify the voice id against the catalog: ${err.message}`);
|
|
1260
|
+
}
|
|
1261
|
+
else {
|
|
1262
|
+
throw err;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
const generationInput = audioInput ?? buildQuoteInput(kind, promptText, opts);
|
|
1267
|
+
await showEstimateBeforeSpending(creds, generationInput, opts);
|
|
1183
1268
|
try {
|
|
1184
1269
|
if (kind === 'prop' || kind === 'character') {
|
|
1185
1270
|
printJson(await (0, lib_1.startDreamerAssetGeneration)(creds, {
|
|
1186
1271
|
kind,
|
|
1187
|
-
prompt,
|
|
1272
|
+
prompt: promptText,
|
|
1188
1273
|
title: opts.title,
|
|
1189
1274
|
additionalPrompt: opts.additionalPrompt,
|
|
1190
1275
|
targetPolycount: optionalNumber(opts.targetPolycount, '--target-polycount'),
|
|
1191
1276
|
}));
|
|
1192
1277
|
return;
|
|
1193
1278
|
}
|
|
1194
|
-
|
|
1279
|
+
// For audio, send the SAME object that was just quoted — not a re-derived one.
|
|
1280
|
+
printJson(await (0, lib_1.startStandaloneAssetGeneration)(creds, audioInput ?? {
|
|
1195
1281
|
kind: kind,
|
|
1196
|
-
prompt,
|
|
1282
|
+
prompt: promptText,
|
|
1197
1283
|
title: opts.title,
|
|
1198
1284
|
additionalPrompt: opts.additionalPrompt,
|
|
1199
1285
|
projectId: opts.projectId,
|
|
@@ -1210,6 +1296,36 @@ assets
|
|
|
1210
1296
|
}
|
|
1211
1297
|
});
|
|
1212
1298
|
// ---- Money: what you have, and what a job will cost BEFORE it runs ---------
|
|
1299
|
+
program
|
|
1300
|
+
.command('jobs')
|
|
1301
|
+
.description('List your generation jobs with what each one cost — the answer to "what was I charged for?" after a failure')
|
|
1302
|
+
.option('--state <state>', 'all, unfinished, failed, published, or awaiting_approval', 'all')
|
|
1303
|
+
.option('--limit <n>', 'how many to show', '20')
|
|
1304
|
+
.option('--offset <n>', 'skip this many')
|
|
1305
|
+
.option('--json', 'emit the raw rows')
|
|
1306
|
+
.action(async (opts) => {
|
|
1307
|
+
const states = ['all', 'unfinished', 'failed', 'published', 'awaiting_approval'];
|
|
1308
|
+
if (opts.state && !states.includes(opts.state)) {
|
|
1309
|
+
fail(`✖ --state must be ${states.join(', ')}; got '${opts.state}'`);
|
|
1310
|
+
}
|
|
1311
|
+
const creds = requireAuth();
|
|
1312
|
+
try {
|
|
1313
|
+
const result = await (0, lib_1.listJobs)(creds, {
|
|
1314
|
+
state: opts.state,
|
|
1315
|
+
limit: optionalNumber(opts.limit, '--limit'),
|
|
1316
|
+
offset: optionalNumber(opts.offset, '--offset'),
|
|
1317
|
+
});
|
|
1318
|
+
if (opts.json)
|
|
1319
|
+
printJson(result);
|
|
1320
|
+
else
|
|
1321
|
+
console.log((0, lib_1.formatJobs)(result));
|
|
1322
|
+
}
|
|
1323
|
+
catch (err) {
|
|
1324
|
+
fail(err instanceof lib_1.ApiError
|
|
1325
|
+
? err.render()
|
|
1326
|
+
: `✖ ${err instanceof Error ? err.message : String(err)}`);
|
|
1327
|
+
}
|
|
1328
|
+
});
|
|
1213
1329
|
program
|
|
1214
1330
|
.command('balance')
|
|
1215
1331
|
.description('Show your LIX balance: available, held by open jobs, and total')
|
|
@@ -1412,7 +1528,7 @@ assets
|
|
|
1412
1528
|
fail(`✖ --visibility must be public, unlisted, or private`);
|
|
1413
1529
|
}
|
|
1414
1530
|
const creds = requireAuth();
|
|
1415
|
-
await showEstimateBeforeSpending(creds, kind, prompt, opts);
|
|
1531
|
+
await showEstimateBeforeSpending(creds, buildQuoteInput(kind, prompt, opts), opts);
|
|
1416
1532
|
try {
|
|
1417
1533
|
printJson(await (0, lib_1.generateAndPublishAsset)(creds, {
|
|
1418
1534
|
kind,
|