@hypersoniclabs/helix-cli 0.1.25-helix3.462 → 0.1.25-helix3.463
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 +3 -0
- package/dist/assetGenerationDriver.d.ts +109 -0
- package/dist/assetGenerationDriver.js +216 -0
- package/dist/assetGenerationDriver.js.map +1 -0
- package/dist/index.js +109 -15
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_audio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_engine.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_expression.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_jbeam.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_materials.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_stock_events.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/fmod_studio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/fsb5.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_ac.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_beamng.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_fixtures.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/test_beamng_engine.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/test_sim_audio.cpython-312.pyc +0 -0
package/README.md
CHANGED
|
@@ -86,6 +86,9 @@ The token is stored in `~/.helix/credentials.json` (mode `600`). It's sent as
|
|
|
86
86
|
| `helix assets start\|status\|generate\|resume` | Start or poll generation without duplicates. Props/characters use the Asset Generation job lane; standalone assets use the shared broker and report the auto-published `vaultAssetId`. `generate` and `resume` block waiting for the job (see `--timeout`/`--poll-interval` below). |
|
|
87
87
|
| `helix assets voices [--search <text>] [--category premade\|professional] [--page-size <n>] [--page-token <token>] [--json]` | Discover safe text-to-speech voices through the authenticated HELIX catalog, including language, labels, preview, default/recommended selection, and pagination. |
|
|
88
88
|
| `helix assets generate-image\|generate-material\|generate-audio\|generate-environment` | Run the supported standalone generation adapters. Material is a validated PBR bundle (1K default, explicit 2K); splats declare object/environment scope. Animation remains an explicit unavailable boundary. |
|
|
89
|
+
| `helix assets start character "<prompt>" [--title t] [--visibility v] [--max-lix n]` | Starts a character on the server-side Asset Generation job driver (`POST /api/v1/asset-generation/jobs`, `mode: deterministic`) and returns at once; stdout is the driver job JSON (`id`). `--visibility` is sent only when you pass it; the server owns the default and every other policy (reference review, redraws, retries, visual QA wait, WARN/BLOCK, refunds). |
|
|
90
|
+
| `helix assets resume <id> [--timeout <ms>] [--poll-interval <ms>]` (driver job) | Waits for `succeeded`, `held`, `failed` or `handoff` (default **80 min**, poll **3 s**), one progress line per change on stderr (stage, reference gate, scheduled retry, policy events, cost). Final stdout is the driver job JSON plus top-level `publishedItemId` (= `result.itemId`). Exit 0 for succeeded, held (the item exists but is not released; `result.qa` says why; `helix assets refund <id>` is offered) and handoff; exit 1 for `failed` (JSON on stdout, message with `escalate`/`retriable` on stderr) and for a client-side timeout (nothing on stdout). `--visibility` must match the job's (it is fixed at start) or the command exits 1. An id the driver does not know (props, older characters) resumes through the previous loop below. `helix assets generate character` is `start` + `resume`. |
|
|
91
|
+
| `helix assets act <id> approve\|revise ["guidance"]`, `helix assets refund <id>`, `helix assets status <id> --pipeline driver` | Decide at the open reference gate, get the LIX back for a held avatar, or read the raw driver job. |
|
|
89
92
|
| `helix assets start-reference\|generate-reference` | Start or fully drive a character job from a local four-view PNG/JPEG/WebP. The backend validates and reuses the exact sheet; it skips image generation while preserving mesh, texture, rig, LOD, thumbnail, billing, and Vault publication. `generate-reference` blocks waiting for the job (see `--timeout`/`--poll-interval` below). |
|
|
90
93
|
| `helix assets generate\|generate-reference\|resume [--timeout <ms>] [--poll-interval <ms>]` | These three drive an Asset Generation prop/character job to completion (concept → mesh → texture → rig → publish) and block until it finishes. `--timeout` bounds how long the CLI process waits before giving up (default **1200000ms / 20min**, unchanged if omitted); `--poll-interval` sets how often it checks job status while waiting (default **2000ms**). A timeout is a CLIENT-SIDE giveup, not a job failure — the job keeps running server-side. The error prints the exact `helix assets resume <job-id>` command to check on or continue it; on a genuinely slow stage (e.g. mesh generation), rerun with a larger `--timeout`, or just poll separately with `helix assets status <job-id> --pipeline dreamer`. |
|
|
91
94
|
| `helix assets materials [--resolution <res>] [--no-resolve] [--human]` | Discover the small built-in material palette. `--resolution` keeps only materials carrying that texture resolution. Each entry also carries the Scene v2 `vault` pin and the `continuum` package ref; `--no-resolve` skips those lookups. |
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export type DriverVisibility = 'public' | 'unlisted' | 'private';
|
|
2
|
+
export type DriverStatus = 'queued' | 'running' | 'succeeded' | 'held' | 'failed' | 'handoff';
|
|
3
|
+
export declare const DRIVER_TERMINAL: readonly DriverStatus[];
|
|
4
|
+
export interface DriverFailure {
|
|
5
|
+
code: string;
|
|
6
|
+
message: string;
|
|
7
|
+
retriable: boolean;
|
|
8
|
+
escalate: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface DriverJobView {
|
|
11
|
+
id: string;
|
|
12
|
+
kind: string;
|
|
13
|
+
status: DriverStatus;
|
|
14
|
+
phase?: string;
|
|
15
|
+
visibility?: DriverVisibility;
|
|
16
|
+
stages?: Record<string, {
|
|
17
|
+
state: string;
|
|
18
|
+
note?: string;
|
|
19
|
+
}>;
|
|
20
|
+
gate?: {
|
|
21
|
+
state: string;
|
|
22
|
+
autoApproveAt?: string | null;
|
|
23
|
+
options?: string[];
|
|
24
|
+
} | null;
|
|
25
|
+
lix?: {
|
|
26
|
+
spentTotal?: number;
|
|
27
|
+
byStage?: Record<string, number>;
|
|
28
|
+
refunded?: number;
|
|
29
|
+
};
|
|
30
|
+
retry?: {
|
|
31
|
+
at: string;
|
|
32
|
+
message: string;
|
|
33
|
+
} | null;
|
|
34
|
+
result?: {
|
|
35
|
+
itemId?: string | null;
|
|
36
|
+
released?: string;
|
|
37
|
+
qa?: {
|
|
38
|
+
verdict?: string;
|
|
39
|
+
warnings?: unknown[];
|
|
40
|
+
blockers?: unknown[];
|
|
41
|
+
note?: string;
|
|
42
|
+
};
|
|
43
|
+
} | null;
|
|
44
|
+
failure?: DriverFailure | null;
|
|
45
|
+
actions?: string[];
|
|
46
|
+
lastEventSeq?: number;
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
export interface DriverEvent {
|
|
50
|
+
seq: number;
|
|
51
|
+
type: string;
|
|
52
|
+
[key: string]: unknown;
|
|
53
|
+
}
|
|
54
|
+
/** The slice of HelixApi this client needs, so tests can pass a fake. */
|
|
55
|
+
export interface DriverApi {
|
|
56
|
+
request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
57
|
+
}
|
|
58
|
+
export declare const DRIVER_BASE = "/api/v1/asset-generation";
|
|
59
|
+
/** The driver itself waits up to 75 minutes for visual QA; the client waits a little longer. */
|
|
60
|
+
export declare const DRIVER_DEFAULT_TIMEOUT_MS: number;
|
|
61
|
+
export declare const DRIVER_DEFAULT_POLL_MS = 3000;
|
|
62
|
+
export interface StartDriverAvatarInput {
|
|
63
|
+
prompt: string;
|
|
64
|
+
title?: string;
|
|
65
|
+
/** Sent ONLY when the user chose one; the server owns the default. */
|
|
66
|
+
visibility?: DriverVisibility;
|
|
67
|
+
}
|
|
68
|
+
export declare function startDriverAvatarJob(api: DriverApi, input: StartDriverAvatarInput, newKey?: () => string): Promise<DriverJobView>;
|
|
69
|
+
/** The driver job, or null when the id is not a driver job (a legacy generation job id, a prop). */
|
|
70
|
+
export declare function getDriverJob(api: DriverApi, id: string): Promise<DriverJobView | null>;
|
|
71
|
+
export declare class DriverVisibilityError extends Error {
|
|
72
|
+
constructor(requested: string, actual: string);
|
|
73
|
+
}
|
|
74
|
+
export declare class DriverTimeoutError extends Error {
|
|
75
|
+
readonly job: DriverJobView;
|
|
76
|
+
constructor(job: DriverJobView, timeoutMs: number);
|
|
77
|
+
}
|
|
78
|
+
/** One human line for an event, or null when it carries nothing a person needs. */
|
|
79
|
+
export declare function describeDriverEvent(e: DriverEvent): string | null;
|
|
80
|
+
export interface ResumeDriverOptions {
|
|
81
|
+
/** The user's --visibility, if any. Compared with the job's; never applied. */
|
|
82
|
+
visibility?: DriverVisibility;
|
|
83
|
+
timeoutMs?: number;
|
|
84
|
+
pollIntervalMs?: number;
|
|
85
|
+
/** One human progress line (the caller sends it to stderr). */
|
|
86
|
+
progress?: (line: string) => void;
|
|
87
|
+
sleep?: (ms: number) => Promise<void>;
|
|
88
|
+
now?: () => number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Wait for a driver job to reach a terminal status. Returns null when `id` is not a driver job (the caller falls
|
|
92
|
+
* back to the legacy loop). Throws DriverVisibilityError / DriverTimeoutError / the transport error; a `failed`
|
|
93
|
+
* job is RETURNED (the caller prints it and exits 1), not thrown.
|
|
94
|
+
*/
|
|
95
|
+
export declare function waitForDriverJob(api: DriverApi, id: string, options?: ResumeDriverOptions): Promise<DriverJobView | null>;
|
|
96
|
+
export interface DriverIo {
|
|
97
|
+
stdout: (text: string) => void;
|
|
98
|
+
stderr: (text: string) => void;
|
|
99
|
+
}
|
|
100
|
+
/** The JSON a consumer reads: the backend's view plus `publishedItemId` once the item exists. */
|
|
101
|
+
export declare function driverOutput(job: DriverJobView): DriverJobView & {
|
|
102
|
+
publishedItemId?: string;
|
|
103
|
+
};
|
|
104
|
+
export declare function renderDriverFailure(job: DriverJobView): string;
|
|
105
|
+
/**
|
|
106
|
+
* Wait, print, and return the process exit code. Returns null when `id` is not a driver job.
|
|
107
|
+
* Never calls process.exit, so piped output is not cut.
|
|
108
|
+
*/
|
|
109
|
+
export declare function runDriverResume(api: DriverApi, id: string, options: ResumeDriverOptions, io: DriverIo): Promise<number | null>;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DriverTimeoutError = exports.DriverVisibilityError = exports.DRIVER_DEFAULT_POLL_MS = exports.DRIVER_DEFAULT_TIMEOUT_MS = exports.DRIVER_BASE = exports.DRIVER_TERMINAL = void 0;
|
|
4
|
+
exports.startDriverAvatarJob = startDriverAvatarJob;
|
|
5
|
+
exports.getDriverJob = getDriverJob;
|
|
6
|
+
exports.describeDriverEvent = describeDriverEvent;
|
|
7
|
+
exports.waitForDriverJob = waitForDriverJob;
|
|
8
|
+
exports.driverOutput = driverOutput;
|
|
9
|
+
exports.renderDriverFailure = renderDriverFailure;
|
|
10
|
+
exports.runDriverResume = runDriverResume;
|
|
11
|
+
// Thin client of the Asset Generation job driver (helix-backend-api
|
|
12
|
+
// docs/architecture/asset-generation-driver.md). The server runs an avatar from prompt to released item under
|
|
13
|
+
// ONE policy (reference review, redraws, retries, visual QA wait, WARN/BLOCK handling, refunds). This file only
|
|
14
|
+
// starts a job, reads its status and events, and reports. It carries no policy: no approve call, no publish call,
|
|
15
|
+
// no retry of a stage, no escalation decision.
|
|
16
|
+
//
|
|
17
|
+
// Output contract (also parsed by the HELIX MCP):
|
|
18
|
+
// stdout the driver job view JSON as returned by the backend, plus a top-level `publishedItemId` (= result.itemId)
|
|
19
|
+
// once the item exists.
|
|
20
|
+
// stderr one human progress line per change while waiting; the failure text on a failed job.
|
|
21
|
+
// exit succeeded 0, held 0, handoff 0, failed 1, client timeout 1.
|
|
22
|
+
const node_crypto_1 = require("node:crypto");
|
|
23
|
+
const api_1 = require("./api");
|
|
24
|
+
exports.DRIVER_TERMINAL = ['succeeded', 'held', 'failed', 'handoff'];
|
|
25
|
+
exports.DRIVER_BASE = '/api/v1/asset-generation';
|
|
26
|
+
/** The driver itself waits up to 75 minutes for visual QA; the client waits a little longer. */
|
|
27
|
+
exports.DRIVER_DEFAULT_TIMEOUT_MS = 80 * 60_000;
|
|
28
|
+
exports.DRIVER_DEFAULT_POLL_MS = 3_000;
|
|
29
|
+
const MAX_CONSECUTIVE_POLL_ERRORS = 5;
|
|
30
|
+
async function startDriverAvatarJob(api, input, newKey = node_crypto_1.randomUUID) {
|
|
31
|
+
return api.request('POST', `${exports.DRIVER_BASE}/jobs`, {
|
|
32
|
+
kind: 'avatar',
|
|
33
|
+
prompt: input.prompt,
|
|
34
|
+
...(input.title ? { title: input.title } : {}),
|
|
35
|
+
mode: 'deterministic',
|
|
36
|
+
...(input.visibility ? { visibility: input.visibility } : {}),
|
|
37
|
+
idempotencyKey: newKey(),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** The driver job, or null when the id is not a driver job (a legacy generation job id, a prop). */
|
|
41
|
+
async function getDriverJob(api, id) {
|
|
42
|
+
try {
|
|
43
|
+
return await api.request('GET', `${exports.DRIVER_BASE}/jobs/${encodeURIComponent(id)}`);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (err instanceof api_1.ApiError && err.status === 404)
|
|
47
|
+
return null;
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
class DriverVisibilityError extends Error {
|
|
52
|
+
constructor(requested, actual) {
|
|
53
|
+
super(`Visibility is fixed when the job starts: this job was started as ${actual}, not ${requested}. ` +
|
|
54
|
+
`Re-run without --visibility to wait for it as it is.`);
|
|
55
|
+
this.name = 'DriverVisibilityError';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.DriverVisibilityError = DriverVisibilityError;
|
|
59
|
+
class DriverTimeoutError extends Error {
|
|
60
|
+
job;
|
|
61
|
+
constructor(job, timeoutMs) {
|
|
62
|
+
super(`Gave up waiting for Asset Generation job ${job.id} after ${timeoutMs}ms ` +
|
|
63
|
+
`(still ${job.status}${job.phase ? `:${job.phase}` : ''}). ` +
|
|
64
|
+
`This is a CLIENT-SIDE wait giving up. The job itself keeps running on the server and needs nothing from you. ` +
|
|
65
|
+
`Check or continue waiting with:\n` +
|
|
66
|
+
` helix assets resume ${job.id}\n` +
|
|
67
|
+
`To wait longer next time, pass --timeout <ms> (and optionally --poll-interval <ms>).`);
|
|
68
|
+
this.job = job;
|
|
69
|
+
this.name = 'DriverTimeoutError';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.DriverTimeoutError = DriverTimeoutError;
|
|
73
|
+
/** One human line for an event, or null when it carries nothing a person needs. */
|
|
74
|
+
function describeDriverEvent(e) {
|
|
75
|
+
switch (e.type) {
|
|
76
|
+
case 'stage': {
|
|
77
|
+
const note = typeof e.note === 'string' && e.note ? ` (${e.note})` : '';
|
|
78
|
+
return `stage ${String(e.stage)}: ${String(e.state)}${note}`;
|
|
79
|
+
}
|
|
80
|
+
case 'gate': {
|
|
81
|
+
if (e.state === 'open') {
|
|
82
|
+
const at = typeof e.autoApproveAt === 'number' ? new Date(e.autoApproveAt).toISOString() : null;
|
|
83
|
+
return `reference gate open${at ? `, auto-approves at ${at}` : ''}`;
|
|
84
|
+
}
|
|
85
|
+
return `reference gate closed${e.decision ? ` (${String(e.decision)})` : ''}`;
|
|
86
|
+
}
|
|
87
|
+
case 'retry_scheduled':
|
|
88
|
+
return `retry scheduled in ${String(e.inSeconds)}s (attempt ${String(e.attempt)} of ${String(e.of)}): ${String(e.message ?? '')}`.trim();
|
|
89
|
+
case 'policy':
|
|
90
|
+
return `policy ${String(e.rule)}: ${String(e.text ?? '')}`.trim();
|
|
91
|
+
case 'cost':
|
|
92
|
+
return `cost: ${String(e.spentTotal)} LIX spent`;
|
|
93
|
+
case 'step':
|
|
94
|
+
return typeof e.text === 'string' ? e.text : null;
|
|
95
|
+
case 'status':
|
|
96
|
+
return `status: ${String(e.status)}`;
|
|
97
|
+
case 'restart':
|
|
98
|
+
return `restart: ${String(e.note ?? '')}`.trim();
|
|
99
|
+
case 'failed':
|
|
100
|
+
return `failed: ${String(e.message ?? e.code)}`;
|
|
101
|
+
default:
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Wait for a driver job to reach a terminal status. Returns null when `id` is not a driver job (the caller falls
|
|
107
|
+
* back to the legacy loop). Throws DriverVisibilityError / DriverTimeoutError / the transport error; a `failed`
|
|
108
|
+
* job is RETURNED (the caller prints it and exits 1), not thrown.
|
|
109
|
+
*/
|
|
110
|
+
async function waitForDriverJob(api, id, options = {}) {
|
|
111
|
+
const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
112
|
+
const now = options.now ?? Date.now;
|
|
113
|
+
const timeoutMs = options.timeoutMs ?? exports.DRIVER_DEFAULT_TIMEOUT_MS;
|
|
114
|
+
const pollMs = options.pollIntervalMs ?? exports.DRIVER_DEFAULT_POLL_MS;
|
|
115
|
+
const say = options.progress ?? (() => { });
|
|
116
|
+
let job = await getDriverJob(api, id);
|
|
117
|
+
if (!job)
|
|
118
|
+
return null;
|
|
119
|
+
if (options.visibility && job.visibility && options.visibility !== job.visibility) {
|
|
120
|
+
throw new DriverVisibilityError(options.visibility, job.visibility);
|
|
121
|
+
}
|
|
122
|
+
const deadline = now() + timeoutMs;
|
|
123
|
+
let seq = 0;
|
|
124
|
+
let lastLine = '';
|
|
125
|
+
let errors = 0;
|
|
126
|
+
const line = (text) => {
|
|
127
|
+
if (!text || text === lastLine)
|
|
128
|
+
return;
|
|
129
|
+
lastLine = text;
|
|
130
|
+
say(text);
|
|
131
|
+
};
|
|
132
|
+
line(`job ${job.id}: ${job.status}${job.phase ? ` (${job.phase})` : ''}`);
|
|
133
|
+
for (;;) {
|
|
134
|
+
// Events are progress only, never a decision input; a failed read costs a line, not the wait.
|
|
135
|
+
if ((job.lastEventSeq ?? 0) > seq) {
|
|
136
|
+
try {
|
|
137
|
+
const page = await api.request('GET', `${exports.DRIVER_BASE}/jobs/${encodeURIComponent(id)}/events?after=${seq}`);
|
|
138
|
+
for (const e of page.events) {
|
|
139
|
+
line(describeDriverEvent(e));
|
|
140
|
+
seq = Math.max(seq, e.seq);
|
|
141
|
+
}
|
|
142
|
+
seq = Math.max(seq, page.lastSeq ?? seq);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// best effort
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (exports.DRIVER_TERMINAL.includes(job.status))
|
|
149
|
+
return job;
|
|
150
|
+
if (now() >= deadline)
|
|
151
|
+
throw new DriverTimeoutError(job, timeoutMs);
|
|
152
|
+
await sleep(pollMs);
|
|
153
|
+
try {
|
|
154
|
+
const next = await getDriverJob(api, id);
|
|
155
|
+
if (next)
|
|
156
|
+
job = next;
|
|
157
|
+
errors = 0;
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
// A transport blip on a poll is not a verdict about the job; a persistent one is real.
|
|
161
|
+
if (++errors >= MAX_CONSECUTIVE_POLL_ERRORS)
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/** The JSON a consumer reads: the backend's view plus `publishedItemId` once the item exists. */
|
|
167
|
+
function driverOutput(job) {
|
|
168
|
+
const itemId = job.result?.itemId;
|
|
169
|
+
return itemId ? { ...job, publishedItemId: itemId } : { ...job };
|
|
170
|
+
}
|
|
171
|
+
function renderDriverFailure(job) {
|
|
172
|
+
const f = job.failure;
|
|
173
|
+
if (!f)
|
|
174
|
+
return `✖ Asset Generation job ${job.id} failed (no details).`;
|
|
175
|
+
return (`✖ Asset Generation job ${job.id} failed: ${f.message} ` +
|
|
176
|
+
`(code: ${f.code}, escalate: ${f.escalate}, retriable: ${f.retriable})`);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Wait, print, and return the process exit code. Returns null when `id` is not a driver job.
|
|
180
|
+
* Never calls process.exit, so piped output is not cut.
|
|
181
|
+
*/
|
|
182
|
+
async function runDriverResume(api, id, options, io) {
|
|
183
|
+
let job;
|
|
184
|
+
try {
|
|
185
|
+
job = await waitForDriverJob(api, id, { ...options, progress: options.progress ?? ((l) => io.stderr(` ${l}`)) });
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
if (err instanceof DriverTimeoutError) {
|
|
189
|
+
io.stderr(`✖ ${err.message}`);
|
|
190
|
+
return 1;
|
|
191
|
+
}
|
|
192
|
+
if (err instanceof DriverVisibilityError) {
|
|
193
|
+
io.stderr(`✖ ${err.message}`);
|
|
194
|
+
return 1;
|
|
195
|
+
}
|
|
196
|
+
throw err;
|
|
197
|
+
}
|
|
198
|
+
if (!job)
|
|
199
|
+
return null;
|
|
200
|
+
io.stdout(JSON.stringify(driverOutput(job), null, 2));
|
|
201
|
+
if (job.status === 'failed') {
|
|
202
|
+
io.stderr(renderDriverFailure(job));
|
|
203
|
+
return 1;
|
|
204
|
+
}
|
|
205
|
+
if (job.status === 'held') {
|
|
206
|
+
const qa = job.result?.qa;
|
|
207
|
+
io.stderr(`Held: the avatar exists${job.result?.itemId ? ` (item ${job.result.itemId})` : ''} but is not released` +
|
|
208
|
+
`${qa?.verdict ? ` (platform check: ${qa.verdict})` : ''}${qa?.note ? `. ${qa.note}` : ''}.` +
|
|
209
|
+
`${job.actions?.includes('refund') ? ` Get the LIX back with: helix assets refund ${job.id}` : ''}`);
|
|
210
|
+
}
|
|
211
|
+
else if (job.status === 'handoff') {
|
|
212
|
+
io.stderr('Handed off: this request is for an agent; the driver ran nothing and nothing was charged.');
|
|
213
|
+
}
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=assetGenerationDriver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assetGenerationDriver.js","sourceRoot":"","sources":["../src/assetGenerationDriver.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,7 @@ const node_path_1 = require("node:path");
|
|
|
50
50
|
const node_os_1 = require("node:os");
|
|
51
51
|
const scenePerformance_1 = require("./scenePerformance");
|
|
52
52
|
const lib_1 = require("./lib");
|
|
53
|
+
const assetGenerationDriver_1 = require("./assetGenerationDriver");
|
|
53
54
|
const itemSearch_1 = require("./itemSearch");
|
|
54
55
|
const vault_1 = require("./vault");
|
|
55
56
|
const looks_1 = require("./looks");
|
|
@@ -3276,6 +3277,30 @@ function printJson(value) {
|
|
|
3276
3277
|
function note(line) {
|
|
3277
3278
|
process.stderr.write(`${line}\n`);
|
|
3278
3279
|
}
|
|
3280
|
+
/** Validate `--visibility`; undefined stays undefined (the driver owns the default). */
|
|
3281
|
+
function parseDriverVisibility(value) {
|
|
3282
|
+
if (value === undefined)
|
|
3283
|
+
return undefined;
|
|
3284
|
+
if (!['public', 'unlisted', 'private'].includes(value)) {
|
|
3285
|
+
fail('✖ --visibility must be public, unlisted, or private');
|
|
3286
|
+
}
|
|
3287
|
+
return value;
|
|
3288
|
+
}
|
|
3289
|
+
/** The driver takes a prompt, a title and a visibility. Guidance folds into the prompt; a polycount is not its input. */
|
|
3290
|
+
function driverStartInput(prompt, opts) {
|
|
3291
|
+
if (opts.targetPolycount !== undefined) {
|
|
3292
|
+
note('• --target-polycount is ignored for characters: the Asset Generation job driver sets it.');
|
|
3293
|
+
}
|
|
3294
|
+
return {
|
|
3295
|
+
prompt: opts.additionalPrompt ? `${prompt}\n${opts.additionalPrompt}` : prompt,
|
|
3296
|
+
title: opts.title,
|
|
3297
|
+
visibility: parseDriverVisibility(opts.visibility),
|
|
3298
|
+
};
|
|
3299
|
+
}
|
|
3300
|
+
const driverIo = {
|
|
3301
|
+
stdout: (text) => console.log(text),
|
|
3302
|
+
stderr: (text) => note(text),
|
|
3303
|
+
};
|
|
3279
3304
|
// Which flags the server accepts for which kind. The quote route REFUSES a field that does not
|
|
3280
3305
|
// apply to the kind — `resolution`/`repeatPerMeter` are material-only, `splatScope` is
|
|
3281
3306
|
// Gaussian-splat-only, `targetPolycount` is mesh-only. Commander gives `--resolution` and
|
|
@@ -3761,6 +3786,7 @@ const assetsStart = assets
|
|
|
3761
3786
|
.option('--resolution <n>', 'material resolution: 1024 or 2048', '1024')
|
|
3762
3787
|
.option('--repeat-per-meter <n>', 'material physical scale: tile repeats per metre')
|
|
3763
3788
|
.option('--splat-scope <scope>', 'Gaussian-splat scope: object or environment', 'environment')
|
|
3789
|
+
.option('--visibility <visibility>', 'character: public, unlisted, or private. Sent only when you pass it; otherwise the Asset Generation job driver applies its own default')
|
|
3764
3790
|
.option('--max-lix <n>', 'refuse to start if the estimate exceeds this many LIX')
|
|
3765
3791
|
.option('--no-estimate', 'skip the up-front cost estimate');
|
|
3766
3792
|
// The audio surface, attached from the SAME definition `generate-audio` uses (minus the flags
|
|
@@ -3845,7 +3871,13 @@ const assetsStart = assets
|
|
|
3845
3871
|
const generationInput = audioInput ?? buildQuoteInput(kind, promptText, opts);
|
|
3846
3872
|
await showEstimateBeforeSpending(creds, generationInput, opts);
|
|
3847
3873
|
try {
|
|
3848
|
-
if (kind === '
|
|
3874
|
+
if (kind === 'character') {
|
|
3875
|
+
// A character from a prompt is a thin client of the Asset Generation job driver: it owns every policy.
|
|
3876
|
+
const input = driverStartInput(promptText, opts);
|
|
3877
|
+
printJson(await (0, assetGenerationDriver_1.startDriverAvatarJob)(lib_1.HelixApi.forCredentials(creds), input));
|
|
3878
|
+
return;
|
|
3879
|
+
}
|
|
3880
|
+
if (kind === 'prop') {
|
|
3849
3881
|
printJson(await (0, lib_1.startDreamerAssetGeneration)(creds, {
|
|
3850
3882
|
kind,
|
|
3851
3883
|
prompt: promptText,
|
|
@@ -4051,13 +4083,20 @@ assets
|
|
|
4051
4083
|
.command('status')
|
|
4052
4084
|
.description('Poll one generation job without starting or advancing a duplicate')
|
|
4053
4085
|
.argument('<job-id>', 'Asset Generation job id')
|
|
4054
|
-
.requiredOption('--pipeline <pipeline>', 'dreamer = prop/character job lane (legacy name), asset = standalone lane')
|
|
4086
|
+
.requiredOption('--pipeline <pipeline>', 'driver = a character driven by the Asset Generation job driver (`start character`), dreamer = prop/legacy character job lane (legacy name), asset = standalone lane')
|
|
4055
4087
|
.action(async (jobId, opts) => {
|
|
4056
|
-
if (!['dreamer', 'asset'].includes(opts.pipeline)) {
|
|
4057
|
-
fail('✖ --pipeline must be dreamer or asset');
|
|
4088
|
+
if (!['driver', 'dreamer', 'asset'].includes(opts.pipeline)) {
|
|
4089
|
+
fail('✖ --pipeline must be driver, dreamer or asset');
|
|
4058
4090
|
}
|
|
4059
4091
|
const creds = requireAuth();
|
|
4060
4092
|
try {
|
|
4093
|
+
if (opts.pipeline === 'driver') {
|
|
4094
|
+
const job = await (0, assetGenerationDriver_1.getDriverJob)(lib_1.HelixApi.forCredentials(creds), jobId);
|
|
4095
|
+
if (!job)
|
|
4096
|
+
fail(`✖ ${jobId} is not an Asset Generation driver job (try --pipeline dreamer)`);
|
|
4097
|
+
printJson(job.result?.itemId ? { ...job, publishedItemId: job.result.itemId } : job);
|
|
4098
|
+
return;
|
|
4099
|
+
}
|
|
4061
4100
|
const job = opts.pipeline === 'dreamer'
|
|
4062
4101
|
? await (0, lib_1.getDreamerAssetGenerationJob)(creds, jobId)
|
|
4063
4102
|
: await (0, lib_1.getStandaloneAssetGenerationJob)(creds, jobId);
|
|
@@ -4116,22 +4155,33 @@ assets
|
|
|
4116
4155
|
.option('--title <title>', 'asset title')
|
|
4117
4156
|
.option('--additional-prompt <text>', 'additional generation guidance')
|
|
4118
4157
|
.option('--target-polycount <n>', 'target triangle count')
|
|
4119
|
-
.option('--visibility <visibility>', 'public, unlisted, or private
|
|
4120
|
-
.option('--timeout <ms>', 'how long to wait for the job to finish before giving up (client-side only — the job keeps running; default
|
|
4121
|
-
.option('--poll-interval <ms>', 'how often to poll job status while waiting (default 2000ms)')
|
|
4158
|
+
.option('--visibility <visibility>', 'public, unlisted, or private (default public for a prop; a character uses the Asset Generation job driver default unless you pass it)')
|
|
4159
|
+
.option('--timeout <ms>', 'how long to wait for the job to finish before giving up (client-side only — the job keeps running; default 20min for a prop, 80min for a character)')
|
|
4160
|
+
.option('--poll-interval <ms>', 'how often to poll job status while waiting (default 2000ms prop, 3000ms character)')
|
|
4122
4161
|
.option('--max-lix <n>', 'refuse to start if the estimate exceeds this many LIX')
|
|
4123
4162
|
.option('--no-estimate', 'skip the up-front cost estimate')
|
|
4124
4163
|
.action(async (kind, prompt, opts) => {
|
|
4125
4164
|
if (kind !== 'prop' && kind !== 'character') {
|
|
4126
4165
|
fail(`✖ kind must be prop or character, got '${kind}'`);
|
|
4127
4166
|
}
|
|
4128
|
-
|
|
4129
|
-
fail(`✖ --visibility must be public, unlisted, or private`);
|
|
4130
|
-
}
|
|
4167
|
+
const requestedVisibility = parseDriverVisibility(opts.visibility);
|
|
4131
4168
|
const timeoutMs = optionalPositiveMs(opts.timeout, '--timeout');
|
|
4132
4169
|
const pollIntervalMs = optionalPositiveMs(opts.pollInterval, '--poll-interval');
|
|
4133
4170
|
const creds = requireAuth();
|
|
4134
4171
|
await showEstimateBeforeSpending(creds, buildQuoteInput(kind, prompt, opts), opts);
|
|
4172
|
+
if (kind === 'character') {
|
|
4173
|
+
const api = lib_1.HelixApi.forCredentials(creds);
|
|
4174
|
+
try {
|
|
4175
|
+
const started = await (0, assetGenerationDriver_1.startDriverAvatarJob)(api, driverStartInput(prompt, opts));
|
|
4176
|
+
note(`Started Asset Generation job ${started.id}. Waiting (resume later with: helix assets resume ${started.id})`);
|
|
4177
|
+
const code = await (0, assetGenerationDriver_1.runDriverResume)(api, started.id, { timeoutMs, pollIntervalMs }, driverIo);
|
|
4178
|
+
process.exitCode = code ?? 1;
|
|
4179
|
+
}
|
|
4180
|
+
catch (err) {
|
|
4181
|
+
fail(`✖ ${err instanceof Error ? err.message : String(err)}`);
|
|
4182
|
+
}
|
|
4183
|
+
return;
|
|
4184
|
+
}
|
|
4135
4185
|
try {
|
|
4136
4186
|
printJson(await (0, lib_1.generateAndPublishAsset)(creds, {
|
|
4137
4187
|
kind,
|
|
@@ -4140,7 +4190,7 @@ assets
|
|
|
4140
4190
|
additionalPrompt: opts.additionalPrompt,
|
|
4141
4191
|
targetPolycount: optionalNumber(opts.targetPolycount, '--target-polycount'),
|
|
4142
4192
|
}, {
|
|
4143
|
-
visibility:
|
|
4193
|
+
visibility: requestedVisibility ?? 'public',
|
|
4144
4194
|
timeoutMs,
|
|
4145
4195
|
pollIntervalMs,
|
|
4146
4196
|
}));
|
|
@@ -4186,11 +4236,11 @@ assets
|
|
|
4186
4236
|
});
|
|
4187
4237
|
assets
|
|
4188
4238
|
.command('resume')
|
|
4189
|
-
.description('
|
|
4239
|
+
.description('Wait for an Asset Generation job and print its result. A character started with `helix assets start character` is driven by the server: this only reads it (progress lines on stderr, the job JSON on stdout; exit 0 for succeeded/held/handoff, 1 for failed or a client timeout). Older job ids (props, legacy characters) resume and publish through the previous loop')
|
|
4190
4240
|
.argument('<job-id>', 'Asset Generation job id')
|
|
4191
|
-
.option('--visibility <visibility>', 'public, unlisted, or private (
|
|
4192
|
-
.option('--timeout <ms>', 'how long to wait for the job to finish before giving up (client-side only — the job keeps running; default
|
|
4193
|
-
.option('--poll-interval <ms>', 'how often to poll job status while waiting (default 2000ms)')
|
|
4241
|
+
.option('--visibility <visibility>', 'public, unlisted, or private (legacy jobs: default is the visibility the job was generated with, else public). A driver job fixes its visibility when it starts: a different value here is an error')
|
|
4242
|
+
.option('--timeout <ms>', 'how long to wait for the job to finish before giving up (client-side only — the job keeps running; default 80min for a driver job, 20min for a legacy job)')
|
|
4243
|
+
.option('--poll-interval <ms>', 'how often to poll job status while waiting (default 3000ms driver job, 2000ms legacy job)')
|
|
4194
4244
|
.action(async (jobId, opts) => {
|
|
4195
4245
|
if (opts.visibility !== undefined &&
|
|
4196
4246
|
!['public', 'unlisted', 'private'].includes(opts.visibility)) {
|
|
@@ -4200,6 +4250,16 @@ assets
|
|
|
4200
4250
|
const pollIntervalMs = optionalPositiveMs(opts.pollInterval, '--poll-interval');
|
|
4201
4251
|
const creds = requireAuth();
|
|
4202
4252
|
try {
|
|
4253
|
+
const code = await (0, assetGenerationDriver_1.runDriverResume)(lib_1.HelixApi.forCredentials(creds), jobId, {
|
|
4254
|
+
visibility: opts.visibility,
|
|
4255
|
+
timeoutMs,
|
|
4256
|
+
pollIntervalMs,
|
|
4257
|
+
}, driverIo);
|
|
4258
|
+
if (code !== null) {
|
|
4259
|
+
process.exitCode = code;
|
|
4260
|
+
return;
|
|
4261
|
+
}
|
|
4262
|
+
// Not a driver job: the legacy loop, unchanged.
|
|
4203
4263
|
const job = await (0, lib_1.resumeAndPublishAsset)(creds, jobId, {
|
|
4204
4264
|
visibility: opts.visibility,
|
|
4205
4265
|
timeoutMs,
|
|
@@ -4217,6 +4277,40 @@ assets
|
|
|
4217
4277
|
fail(`✖ ${err instanceof Error ? err.message : String(err)}`);
|
|
4218
4278
|
}
|
|
4219
4279
|
});
|
|
4280
|
+
assets
|
|
4281
|
+
.command('act')
|
|
4282
|
+
.description('Decide at the open reference gate of an Asset Generation job driven by the server: approve it now, or revise it with guidance (a revise charges a regeneration)')
|
|
4283
|
+
.argument('<job-id>', 'Asset Generation job id')
|
|
4284
|
+
.argument('<action>', 'approve or revise')
|
|
4285
|
+
.argument('[guidance]', 'revise: what to change')
|
|
4286
|
+
.option('--prompt <text>', 'revise: what to change (same as the guidance argument)')
|
|
4287
|
+
.action(async (jobId, action, guidance, opts) => {
|
|
4288
|
+
if (action !== 'approve' && action !== 'revise')
|
|
4289
|
+
fail('✖ action must be approve or revise');
|
|
4290
|
+
const prompt = guidance ?? opts.prompt;
|
|
4291
|
+
if (action === 'approve' && prompt)
|
|
4292
|
+
fail('✖ approve takes no guidance; use revise');
|
|
4293
|
+
const creds = requireAuth();
|
|
4294
|
+
try {
|
|
4295
|
+
printJson(await lib_1.HelixApi.forCredentials(creds).request('POST', `${assetGenerationDriver_1.DRIVER_BASE}/jobs/${encodeURIComponent(jobId)}/actions`, { action, ...(prompt ? { prompt } : {}) }));
|
|
4296
|
+
}
|
|
4297
|
+
catch (err) {
|
|
4298
|
+
fail(err instanceof lib_1.ApiError ? err.render() : `✖ ${err instanceof Error ? err.message : String(err)}`);
|
|
4299
|
+
}
|
|
4300
|
+
});
|
|
4301
|
+
assets
|
|
4302
|
+
.command('refund')
|
|
4303
|
+
.description('Get the LIX back for an avatar the platform check held (Asset Generation job driver; server-verified, idempotent)')
|
|
4304
|
+
.argument('<job-id>', 'Asset Generation job id')
|
|
4305
|
+
.action(async (jobId) => {
|
|
4306
|
+
const creds = requireAuth();
|
|
4307
|
+
try {
|
|
4308
|
+
printJson(await lib_1.HelixApi.forCredentials(creds).request('POST', `${assetGenerationDriver_1.DRIVER_BASE}/jobs/${encodeURIComponent(jobId)}/refund`));
|
|
4309
|
+
}
|
|
4310
|
+
catch (err) {
|
|
4311
|
+
fail(err instanceof lib_1.ApiError ? err.render() : `✖ ${err instanceof Error ? err.message : String(err)}`);
|
|
4312
|
+
}
|
|
4313
|
+
});
|
|
4220
4314
|
assets
|
|
4221
4315
|
.command('generate-image')
|
|
4222
4316
|
.description('Generate a standalone image with Asset Generation')
|