bazaar.it 0.1.0 → 0.2.1
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 +485 -3
- package/bin/baz.js +6 -1
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +109 -0
- package/dist/commands/capabilities.d.ts +2 -0
- package/dist/commands/capabilities.js +44 -0
- package/dist/commands/context.d.ts +13 -0
- package/dist/commands/context.js +498 -0
- package/dist/commands/export.d.ts +2 -0
- package/dist/commands/export.js +360 -0
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.js +180 -0
- package/dist/commands/loop.d.ts +2 -0
- package/dist/commands/loop.js +538 -0
- package/dist/commands/mcp.d.ts +2 -0
- package/dist/commands/mcp.js +143 -0
- package/dist/commands/media.d.ts +2 -0
- package/dist/commands/media.js +362 -0
- package/dist/commands/project.d.ts +2 -0
- package/dist/commands/project.js +786 -0
- package/dist/commands/prompt.d.ts +2 -0
- package/dist/commands/prompt.js +540 -0
- package/dist/commands/recipe.d.ts +15 -0
- package/dist/commands/recipe.js +607 -0
- package/dist/commands/review.d.ts +17 -0
- package/dist/commands/review.js +345 -0
- package/dist/commands/scenes.d.ts +2 -0
- package/dist/commands/scenes.js +481 -0
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +226 -0
- package/dist/commands/state.d.ts +2 -0
- package/dist/commands/state.js +171 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +219 -0
- package/dist/commands/template.d.ts +2 -0
- package/dist/commands/template.js +123 -0
- package/dist/commands/verify.d.ts +2 -0
- package/dist/commands/verify.js +150 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +124 -0
- package/dist/lib/api.d.ts +188 -0
- package/dist/lib/api.js +719 -0
- package/dist/lib/banner.d.ts +12 -0
- package/dist/lib/banner.js +69 -0
- package/dist/lib/config.d.ts +33 -0
- package/dist/lib/config.js +99 -0
- package/dist/lib/output.d.ts +52 -0
- package/dist/lib/output.js +162 -0
- package/dist/lib/project-state.d.ts +52 -0
- package/dist/lib/project-state.js +178 -0
- package/dist/lib/sse.d.ts +168 -0
- package/dist/lib/sse.js +227 -0
- package/dist/lib/version.d.ts +1 -0
- package/dist/lib/version.js +3 -0
- package/dist/repl.d.ts +4 -0
- package/dist/repl.js +764 -0
- package/package.json +32 -5
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { loadConfig, hasAuth, getProjectId } from '../lib/config.js';
|
|
5
|
+
import { apiRequest, ApiError } from '../lib/api.js';
|
|
6
|
+
import { error, output, formatDuration } from '../lib/output.js';
|
|
7
|
+
import { normalizeScenes } from '../lib/project-state.js';
|
|
8
|
+
function handleStateError(err, globalOpts) {
|
|
9
|
+
if (err instanceof ApiError) {
|
|
10
|
+
if (globalOpts.json) {
|
|
11
|
+
output(err.toJSON(), { json: true, compact: globalOpts.compact });
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
error(err.message, err.suggestion);
|
|
15
|
+
}
|
|
16
|
+
process.exit(err.exitCode);
|
|
17
|
+
}
|
|
18
|
+
if (globalOpts.json) {
|
|
19
|
+
output({
|
|
20
|
+
type: 'error',
|
|
21
|
+
code: 'UNKNOWN',
|
|
22
|
+
message: err.message || 'Unknown error',
|
|
23
|
+
category: 'fatal',
|
|
24
|
+
retryable: false,
|
|
25
|
+
transient: false,
|
|
26
|
+
exitCode: 1,
|
|
27
|
+
}, { json: true, compact: globalOpts.compact });
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
error(err.message || 'Unknown error');
|
|
31
|
+
}
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
export const stateCommand = new Command('state')
|
|
35
|
+
.description('Get a machine-friendly snapshot of current project state')
|
|
36
|
+
.option('--include-code', 'Include scene TSX code in scene payload')
|
|
37
|
+
.option('--include-messages', 'Include recent project messages')
|
|
38
|
+
.action(async (options, cmd) => {
|
|
39
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
40
|
+
const config = loadConfig({
|
|
41
|
+
configPath: globalOpts.config,
|
|
42
|
+
apiUrl: globalOpts.apiUrl,
|
|
43
|
+
projectId: globalOpts.projectId,
|
|
44
|
+
});
|
|
45
|
+
if (!hasAuth(config)) {
|
|
46
|
+
if (globalOpts.json) {
|
|
47
|
+
output({
|
|
48
|
+
type: 'error',
|
|
49
|
+
code: 'AUTH_MISSING',
|
|
50
|
+
message: 'Not authenticated',
|
|
51
|
+
category: 'auth',
|
|
52
|
+
retryable: false,
|
|
53
|
+
transient: false,
|
|
54
|
+
exitCode: 13,
|
|
55
|
+
suggestion: 'Run: baz auth login <api-key>',
|
|
56
|
+
}, { json: true, compact: globalOpts.compact });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
error('Not authenticated', 'Run: baz auth login <api-key>');
|
|
60
|
+
}
|
|
61
|
+
process.exit(13);
|
|
62
|
+
}
|
|
63
|
+
let projectId;
|
|
64
|
+
try {
|
|
65
|
+
projectId = getProjectId(config, globalOpts.projectId);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (globalOpts.json) {
|
|
69
|
+
output({
|
|
70
|
+
type: 'error',
|
|
71
|
+
code: 'VALIDATION',
|
|
72
|
+
message: err.message,
|
|
73
|
+
category: 'validation',
|
|
74
|
+
retryable: false,
|
|
75
|
+
transient: false,
|
|
76
|
+
exitCode: 64,
|
|
77
|
+
}, { json: true, compact: globalOpts.compact });
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
error(err.message);
|
|
81
|
+
}
|
|
82
|
+
process.exit(64);
|
|
83
|
+
}
|
|
84
|
+
const spinner = globalOpts.json ? null : ora('Collecting state snapshot...').start();
|
|
85
|
+
try {
|
|
86
|
+
const [projectResult, validationResult, renderResult, subscriptionResult] = await Promise.all([
|
|
87
|
+
apiRequest(config, 'project.getFullProject', {
|
|
88
|
+
id: projectId,
|
|
89
|
+
include: options.includeMessages ? ['scenes', 'messages'] : ['scenes'],
|
|
90
|
+
includeSceneCode: options.includeCode === true,
|
|
91
|
+
}),
|
|
92
|
+
apiRequest(config, 'project.validate', { projectId }),
|
|
93
|
+
apiRequest(config, 'render.listRenders').catch(() => []),
|
|
94
|
+
apiRequest(config, 'payment.getSubscriptionSummary').catch(() => null),
|
|
95
|
+
]);
|
|
96
|
+
spinner?.stop();
|
|
97
|
+
const project = projectResult.project ?? projectResult;
|
|
98
|
+
const codeRequested = options.includeCode === true;
|
|
99
|
+
const scenes = normalizeScenes((projectResult.scenes ?? []), { codeOmitted: !codeRequested });
|
|
100
|
+
const activeTasks = (Array.isArray(renderResult) ? renderResult : [])
|
|
101
|
+
.filter((job) => job?.status === 'pending' || job?.status === 'rendering');
|
|
102
|
+
const scenePayload = scenes.map((scene) => {
|
|
103
|
+
const raw = (projectResult.scenes ?? []).find((item) => item.id === scene.id);
|
|
104
|
+
return {
|
|
105
|
+
id: scene.id,
|
|
106
|
+
name: scene.name,
|
|
107
|
+
track: scene.track,
|
|
108
|
+
order: scene.order,
|
|
109
|
+
startFrame: scene.startFrame,
|
|
110
|
+
durationFrames: scene.durationFrames,
|
|
111
|
+
endFrame: scene.endFrame,
|
|
112
|
+
hasCode: scene.hasCode,
|
|
113
|
+
hasCompilationError: scene.hasCompilationError,
|
|
114
|
+
...(options.includeCode === true && raw?.tsxCode ? { tsxCode: raw.tsxCode } : {}),
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
const snapshot = {
|
|
118
|
+
type: 'state_snapshot',
|
|
119
|
+
timestamp: new Date().toISOString(),
|
|
120
|
+
project: {
|
|
121
|
+
id: projectId,
|
|
122
|
+
title: project?.title || project?.name || 'Untitled',
|
|
123
|
+
format: project?.props?.meta?.format || 'landscape',
|
|
124
|
+
width: project?.props?.meta?.width,
|
|
125
|
+
height: project?.props?.meta?.height,
|
|
126
|
+
updatedAt: project?.updatedAt,
|
|
127
|
+
},
|
|
128
|
+
timeline: validationResult.summary,
|
|
129
|
+
validation: {
|
|
130
|
+
valid: validationResult.valid,
|
|
131
|
+
errorCount: validationResult.errors.length,
|
|
132
|
+
warningCount: validationResult.warnings.length,
|
|
133
|
+
},
|
|
134
|
+
scenes: scenePayload,
|
|
135
|
+
activeTasks: activeTasks.map((task) => ({
|
|
136
|
+
id: task?.id,
|
|
137
|
+
status: task?.status,
|
|
138
|
+
progress: task?.progress,
|
|
139
|
+
createdAt: task?.createdAt,
|
|
140
|
+
})),
|
|
141
|
+
resources: subscriptionResult
|
|
142
|
+
? {
|
|
143
|
+
balance: subscriptionResult.balance,
|
|
144
|
+
tierDisplayName: subscriptionResult.tierDisplayName,
|
|
145
|
+
canExportWatermarkFree: subscriptionResult.canExportWatermarkFree,
|
|
146
|
+
nextResetAt: subscriptionResult.nextResetAt,
|
|
147
|
+
}
|
|
148
|
+
: null,
|
|
149
|
+
...(options.includeMessages ? { messages: projectResult.messages ?? [] } : {}),
|
|
150
|
+
};
|
|
151
|
+
if (globalOpts.json) {
|
|
152
|
+
output(snapshot, { json: true, compact: globalOpts.compact });
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
console.log(chalk.bold('State Snapshot'));
|
|
156
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
157
|
+
console.log(`Project: ${chalk.cyan(snapshot.project.title)} (${projectId.slice(0, 8)}...)`);
|
|
158
|
+
console.log(`Format: ${snapshot.project.format}${snapshot.project.width && snapshot.project.height ? ` (${snapshot.project.width}x${snapshot.project.height})` : ''}`);
|
|
159
|
+
console.log(`Scenes: ${snapshot.timeline.sceneCount} across ${snapshot.timeline.trackCount} track(s)`);
|
|
160
|
+
console.log(`Duration: ${formatDuration(snapshot.timeline.totalDurationSeconds)}`);
|
|
161
|
+
console.log(`Validation: ${snapshot.validation.valid ? chalk.green('valid') : chalk.red('issues found')} (${snapshot.validation.errorCount} errors, ${snapshot.validation.warningCount} warnings)`);
|
|
162
|
+
console.log(`Active tasks: ${snapshot.activeTasks.length}`);
|
|
163
|
+
if (snapshot.resources) {
|
|
164
|
+
console.log(`Balance: $${Number(snapshot.resources.balance ?? 0).toFixed(2)} (${snapshot.resources.tierDisplayName || 'tier unknown'})`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
spinner?.stop();
|
|
169
|
+
handleStateError(err, globalOpts);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { loadConfig, hasAuth, getProjectId } from '../lib/config.js';
|
|
5
|
+
import { apiRequest } from '../lib/api.js';
|
|
6
|
+
import { error, formatDuration } from '../lib/output.js';
|
|
7
|
+
/**
|
|
8
|
+
* Render an ASCII timeline of scenes
|
|
9
|
+
*/
|
|
10
|
+
function renderTimeline(scenes, fps, width = 50) {
|
|
11
|
+
if (scenes.length === 0)
|
|
12
|
+
return [' (no scenes)'];
|
|
13
|
+
const getStartFrame = (scene) => scene.start ?? scene.props?.start ?? scene.startFrame ?? 0;
|
|
14
|
+
const getDurationFrames = (scene) => scene.duration ?? scene.durationInFrames ?? 0;
|
|
15
|
+
const getTrack = (scene) => scene.track ?? scene.order ?? 0;
|
|
16
|
+
const hasError = (scene) => Boolean(scene.compilationError ?? scene.compilation_error);
|
|
17
|
+
const getName = (scene) => scene.name ?? scene.data?.name ?? '';
|
|
18
|
+
// Calculate total duration
|
|
19
|
+
let totalFrames = 0;
|
|
20
|
+
scenes.forEach((s) => {
|
|
21
|
+
const end = getStartFrame(s) + getDurationFrames(s);
|
|
22
|
+
if (end > totalFrames)
|
|
23
|
+
totalFrames = end;
|
|
24
|
+
});
|
|
25
|
+
if (totalFrames === 0)
|
|
26
|
+
return [' (no duration)'];
|
|
27
|
+
const lines = [];
|
|
28
|
+
// Group scenes by track
|
|
29
|
+
const tracks = new Map();
|
|
30
|
+
scenes.forEach((s) => {
|
|
31
|
+
const track = getTrack(s);
|
|
32
|
+
if (!tracks.has(track))
|
|
33
|
+
tracks.set(track, []);
|
|
34
|
+
tracks.get(track).push(s);
|
|
35
|
+
});
|
|
36
|
+
// Sort tracks
|
|
37
|
+
const sortedTracks = Array.from(tracks.keys()).sort((a, b) => a - b);
|
|
38
|
+
// Render each track
|
|
39
|
+
for (const trackNum of sortedTracks) {
|
|
40
|
+
const trackScenes = tracks.get(trackNum);
|
|
41
|
+
const trackLabel = `Track ${trackNum}`;
|
|
42
|
+
// Create timeline bar
|
|
43
|
+
let bar = ' '.repeat(width);
|
|
44
|
+
for (const scene of trackScenes) {
|
|
45
|
+
const startFrame = getStartFrame(scene);
|
|
46
|
+
const duration = getDurationFrames(scene);
|
|
47
|
+
// Calculate position in the bar
|
|
48
|
+
const startPos = Math.floor((startFrame / totalFrames) * width);
|
|
49
|
+
const endPos = Math.min(Math.floor(((startFrame + duration) / totalFrames) * width), width);
|
|
50
|
+
const sceneWidth = Math.max(1, endPos - startPos);
|
|
51
|
+
// Get scene identifier (first 6 chars of name or "▒")
|
|
52
|
+
const name = getName(scene).slice(0, sceneWidth - 1) || '▓';
|
|
53
|
+
const displayName = name.padEnd(sceneWidth, '▓').slice(0, sceneWidth);
|
|
54
|
+
// Color based on status
|
|
55
|
+
const coloredName = hasError(scene) ? chalk.red(displayName) : chalk.green(displayName);
|
|
56
|
+
// Insert into bar
|
|
57
|
+
bar = bar.slice(0, startPos) + displayName + bar.slice(startPos + sceneWidth);
|
|
58
|
+
}
|
|
59
|
+
// Add track label and bar
|
|
60
|
+
lines.push(` ${chalk.dim(trackLabel.padEnd(8))} │${chalk.gray(bar)}│`);
|
|
61
|
+
}
|
|
62
|
+
// Add time axis
|
|
63
|
+
const totalSeconds = totalFrames / fps;
|
|
64
|
+
const midTime = formatDuration(totalSeconds / 2);
|
|
65
|
+
const endTime = formatDuration(totalSeconds);
|
|
66
|
+
const axisBar = '─'.repeat(width);
|
|
67
|
+
lines.push(` ${' '.repeat(8)} └${axisBar}┘`);
|
|
68
|
+
lines.push(` ${' '.repeat(8)} ${chalk.dim('0:00')}${' '.repeat(Math.floor(width / 2) - 4)}${chalk.dim(midTime)}${' '.repeat(Math.floor(width / 2) - midTime.length)}${chalk.dim(endTime)}`);
|
|
69
|
+
return lines;
|
|
70
|
+
}
|
|
71
|
+
export const statusCommand = new Command('status')
|
|
72
|
+
.description('Show project overview and timeline')
|
|
73
|
+
.action(async (options, cmd) => {
|
|
74
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
75
|
+
const config = loadConfig({
|
|
76
|
+
configPath: globalOpts.config,
|
|
77
|
+
apiUrl: globalOpts.apiUrl,
|
|
78
|
+
projectId: globalOpts.projectId,
|
|
79
|
+
});
|
|
80
|
+
if (!hasAuth(config)) {
|
|
81
|
+
if (globalOpts.json) {
|
|
82
|
+
console.log(JSON.stringify({
|
|
83
|
+
type: 'error',
|
|
84
|
+
code: 'AUTH_MISSING',
|
|
85
|
+
message: 'Not authenticated',
|
|
86
|
+
category: 'auth',
|
|
87
|
+
retryable: false,
|
|
88
|
+
transient: false,
|
|
89
|
+
exitCode: 13,
|
|
90
|
+
suggestion: 'Run: baz auth login <api-key>',
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
error('Not authenticated', 'Run: baz auth login <api-key>');
|
|
95
|
+
}
|
|
96
|
+
process.exit(13);
|
|
97
|
+
}
|
|
98
|
+
let projectId;
|
|
99
|
+
try {
|
|
100
|
+
projectId = getProjectId(config, globalOpts.projectId);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (globalOpts.json) {
|
|
104
|
+
console.log(JSON.stringify({
|
|
105
|
+
type: 'error',
|
|
106
|
+
code: 'VALIDATION',
|
|
107
|
+
message: err.message,
|
|
108
|
+
category: 'validation',
|
|
109
|
+
retryable: false,
|
|
110
|
+
transient: false,
|
|
111
|
+
exitCode: 64,
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
error(err.message);
|
|
116
|
+
}
|
|
117
|
+
process.exit(64);
|
|
118
|
+
}
|
|
119
|
+
const spinner = ora('Fetching project status...').start();
|
|
120
|
+
try {
|
|
121
|
+
// Use full project query as canonical source of scene state.
|
|
122
|
+
const projectResult = await apiRequest(config, 'project.getFullProject', {
|
|
123
|
+
id: projectId,
|
|
124
|
+
include: ['scenes'],
|
|
125
|
+
includeSceneCode: false,
|
|
126
|
+
});
|
|
127
|
+
spinner.stop();
|
|
128
|
+
const project = projectResult.project || projectResult;
|
|
129
|
+
const scenes = projectResult.scenes || [];
|
|
130
|
+
const fps = 30;
|
|
131
|
+
const getStartFrame = (scene) => scene.start ?? scene.props?.start ?? scene.startFrame ?? 0;
|
|
132
|
+
const getDurationFrames = (scene) => scene.duration ?? scene.durationInFrames ?? 0;
|
|
133
|
+
const getTrack = (scene) => scene.track ?? scene.order ?? 0;
|
|
134
|
+
const hasError = (scene) => Boolean(scene.compilationError ?? scene.compilation_error);
|
|
135
|
+
const getName = (scene) => scene.name ?? scene.data?.name ?? 'Untitled';
|
|
136
|
+
// Calculate stats
|
|
137
|
+
let totalFrames = 0;
|
|
138
|
+
let errorCount = 0;
|
|
139
|
+
const trackSet = new Set();
|
|
140
|
+
scenes.forEach((s) => {
|
|
141
|
+
const end = getStartFrame(s) + getDurationFrames(s);
|
|
142
|
+
if (end > totalFrames)
|
|
143
|
+
totalFrames = end;
|
|
144
|
+
if (hasError(s))
|
|
145
|
+
errorCount++;
|
|
146
|
+
trackSet.add(getTrack(s));
|
|
147
|
+
});
|
|
148
|
+
const totalDuration = totalFrames / fps;
|
|
149
|
+
const trackCount = trackSet.size || 1;
|
|
150
|
+
// JSON output
|
|
151
|
+
if (globalOpts.json) {
|
|
152
|
+
console.log(JSON.stringify({
|
|
153
|
+
projectId,
|
|
154
|
+
title: project.title || 'Untitled',
|
|
155
|
+
scenes: scenes.length,
|
|
156
|
+
tracks: trackCount,
|
|
157
|
+
totalDuration,
|
|
158
|
+
errors: errorCount,
|
|
159
|
+
sceneList: scenes.map((s) => ({
|
|
160
|
+
id: s.id,
|
|
161
|
+
name: getName(s),
|
|
162
|
+
track: getTrack(s),
|
|
163
|
+
startFrame: getStartFrame(s),
|
|
164
|
+
durationInFrames: getDurationFrames(s),
|
|
165
|
+
hasError: hasError(s),
|
|
166
|
+
})),
|
|
167
|
+
}, null, 2));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
// Pretty output
|
|
171
|
+
console.log();
|
|
172
|
+
console.log(chalk.bold('Project Status'));
|
|
173
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
174
|
+
console.log();
|
|
175
|
+
// Project info
|
|
176
|
+
console.log(` ${chalk.dim('Project:')} ${project.title || 'Untitled'}`);
|
|
177
|
+
console.log(` ${chalk.dim('ID:')} ${projectId.slice(0, 8)}...`);
|
|
178
|
+
console.log();
|
|
179
|
+
// Stats box
|
|
180
|
+
console.log(chalk.cyan('┌─────────────────────────────────────────────────────────┐'));
|
|
181
|
+
console.log(chalk.cyan('│') + ` Scenes: ${chalk.bold(String(scenes.length).padEnd(4))} Tracks: ${chalk.bold(String(trackCount).padEnd(4))} Duration: ${chalk.bold(formatDuration(totalDuration).padEnd(8))} ${errorCount > 0 ? chalk.red(`Errors: ${errorCount}`) : chalk.green('✓ No errors')}`.padEnd(56) + chalk.cyan('│'));
|
|
182
|
+
console.log(chalk.cyan('└─────────────────────────────────────────────────────────┘'));
|
|
183
|
+
console.log();
|
|
184
|
+
// Timeline
|
|
185
|
+
if (scenes.length > 0) {
|
|
186
|
+
console.log(chalk.dim(' Timeline:'));
|
|
187
|
+
const timelineLines = renderTimeline(scenes, fps, 50);
|
|
188
|
+
timelineLines.forEach(line => console.log(line));
|
|
189
|
+
console.log();
|
|
190
|
+
}
|
|
191
|
+
// Scene list
|
|
192
|
+
if (scenes.length > 0) {
|
|
193
|
+
console.log(chalk.dim(' Scenes:'));
|
|
194
|
+
scenes.forEach((s, i) => {
|
|
195
|
+
const startSec = getStartFrame(s) / fps;
|
|
196
|
+
const endSec = startSec + (getDurationFrames(s) / fps);
|
|
197
|
+
const timeRange = `${formatDuration(startSec)}-${formatDuration(endSec)}`;
|
|
198
|
+
const track = getTrack(s);
|
|
199
|
+
const status = hasError(s)
|
|
200
|
+
? chalk.red('✗')
|
|
201
|
+
: chalk.green('✓');
|
|
202
|
+
const trackBadge = chalk.dim(`T${track}`);
|
|
203
|
+
console.log(` ${status} ${chalk.gray(`${i + 1}.`)} ${getName(s).padEnd(25)} ${chalk.dim(timeRange.padEnd(12))} ${trackBadge}`);
|
|
204
|
+
});
|
|
205
|
+
console.log();
|
|
206
|
+
}
|
|
207
|
+
// Help text
|
|
208
|
+
console.log(chalk.gray(' Commands:'));
|
|
209
|
+
console.log(chalk.gray(' baz prompt "<instruction>" Send prompt to agent'));
|
|
210
|
+
console.log(chalk.gray(' baz scenes list List scenes'));
|
|
211
|
+
console.log(chalk.gray(' baz export start Export video'));
|
|
212
|
+
console.log();
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
spinner.stop();
|
|
216
|
+
error(err.message);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { loadConfig, hasAuth } from '../lib/config.js';
|
|
5
|
+
import { apiRequest, ApiError } from '../lib/api.js';
|
|
6
|
+
import { output, error, table } from '../lib/output.js';
|
|
7
|
+
export const templateCommand = new Command('template')
|
|
8
|
+
.description('Browse and manage templates');
|
|
9
|
+
/**
|
|
10
|
+
* baz template list
|
|
11
|
+
*/
|
|
12
|
+
templateCommand
|
|
13
|
+
.command('list')
|
|
14
|
+
.description('List available templates')
|
|
15
|
+
.option('--category <category>', 'Filter by category')
|
|
16
|
+
.option('--format <format>', 'Filter by format: landscape, portrait, square')
|
|
17
|
+
.option('--search <query>', 'Search templates by name/description')
|
|
18
|
+
.option('--limit <n>', 'Max results', '20')
|
|
19
|
+
.action(async (options, cmd) => {
|
|
20
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
21
|
+
const config = loadConfig({
|
|
22
|
+
configPath: globalOpts.config,
|
|
23
|
+
apiUrl: globalOpts.apiUrl,
|
|
24
|
+
});
|
|
25
|
+
if (!hasAuth(config)) {
|
|
26
|
+
if (globalOpts.json) {
|
|
27
|
+
output({
|
|
28
|
+
type: 'error',
|
|
29
|
+
code: 'AUTH_MISSING',
|
|
30
|
+
message: 'Not authenticated',
|
|
31
|
+
category: 'auth',
|
|
32
|
+
retryable: false,
|
|
33
|
+
transient: false,
|
|
34
|
+
exitCode: 13,
|
|
35
|
+
suggestion: 'Run: baz auth login <api-key>',
|
|
36
|
+
}, { json: true, compact: globalOpts.compact });
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
error('Not authenticated', 'Run: baz auth login <api-key>');
|
|
40
|
+
}
|
|
41
|
+
process.exit(13);
|
|
42
|
+
}
|
|
43
|
+
const spinner = globalOpts.json ? null : ora('Fetching templates...').start();
|
|
44
|
+
try {
|
|
45
|
+
const limit = Math.min(parseInt(options.limit, 10) || 20, 100);
|
|
46
|
+
const result = await apiRequest(config, 'templates.getAll', {
|
|
47
|
+
...(options.category && { category: options.category }),
|
|
48
|
+
...(options.format && { format: options.format }),
|
|
49
|
+
...(options.search && { search: options.search }),
|
|
50
|
+
limit,
|
|
51
|
+
});
|
|
52
|
+
spinner?.stop();
|
|
53
|
+
const items = result.items || [];
|
|
54
|
+
if (globalOpts.json) {
|
|
55
|
+
const lean = items.map(t => ({
|
|
56
|
+
id: t.id,
|
|
57
|
+
name: t.name,
|
|
58
|
+
description: t.description,
|
|
59
|
+
category: t.category || null,
|
|
60
|
+
formats: t.supportedFormats || [],
|
|
61
|
+
sceneCount: t.sceneCount,
|
|
62
|
+
duration: t.totalDuration || null,
|
|
63
|
+
tags: t.tags || [],
|
|
64
|
+
isOfficial: t.isOfficial,
|
|
65
|
+
}));
|
|
66
|
+
output({
|
|
67
|
+
items: lean,
|
|
68
|
+
count: lean.length,
|
|
69
|
+
hasMore: result.nextCursor !== undefined,
|
|
70
|
+
}, { json: true, compact: globalOpts.compact });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (items.length === 0) {
|
|
74
|
+
console.log(chalk.gray('No templates found.'));
|
|
75
|
+
if (options.search) {
|
|
76
|
+
console.log(chalk.gray(`Try a different search term or remove --search`));
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
console.log(`Templates (${items.length} results)`);
|
|
81
|
+
console.log();
|
|
82
|
+
const rows = items.map(t => {
|
|
83
|
+
const name = t.name.length > 30 ? t.name.slice(0, 27) + '...' : t.name;
|
|
84
|
+
const category = t.category || '—';
|
|
85
|
+
const formats = (t.supportedFormats || []).join(', ') || '—';
|
|
86
|
+
const scenes = String(t.sceneCount);
|
|
87
|
+
const official = t.isOfficial ? chalk.cyan('*') : ' ';
|
|
88
|
+
return [official, name, category, formats, scenes];
|
|
89
|
+
});
|
|
90
|
+
table(['', 'Name', 'Category', 'Formats', 'Scenes'], rows);
|
|
91
|
+
if (result.nextCursor !== undefined) {
|
|
92
|
+
console.log();
|
|
93
|
+
console.log(chalk.gray(`More results available. Use --limit to see more.`));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
spinner?.stop();
|
|
98
|
+
if (err instanceof ApiError) {
|
|
99
|
+
if (globalOpts.json) {
|
|
100
|
+
output(err.toJSON(), { json: true, compact: globalOpts.compact });
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
error(err.message, err.suggestion);
|
|
104
|
+
}
|
|
105
|
+
process.exit(err.exitCode);
|
|
106
|
+
}
|
|
107
|
+
if (globalOpts.json) {
|
|
108
|
+
output({
|
|
109
|
+
type: 'error',
|
|
110
|
+
code: 'UNKNOWN',
|
|
111
|
+
message: err.message,
|
|
112
|
+
category: 'fatal',
|
|
113
|
+
retryable: false,
|
|
114
|
+
transient: false,
|
|
115
|
+
exitCode: 1,
|
|
116
|
+
}, { json: true, compact: globalOpts.compact });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
error(err.message);
|
|
120
|
+
}
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { loadConfig, hasAuth, getProjectId } from '../lib/config.js';
|
|
5
|
+
import { apiRequest, ApiError } from '../lib/api.js';
|
|
6
|
+
import { error, output, formatDuration } from '../lib/output.js';
|
|
7
|
+
function handleVerifyError(err, globalOpts) {
|
|
8
|
+
if (err instanceof ApiError) {
|
|
9
|
+
if (globalOpts.json) {
|
|
10
|
+
output(err.toJSON(), { json: true, compact: globalOpts.compact });
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
error(err.message, err.suggestion);
|
|
14
|
+
}
|
|
15
|
+
process.exit(err.exitCode);
|
|
16
|
+
}
|
|
17
|
+
if (globalOpts.json) {
|
|
18
|
+
output({
|
|
19
|
+
type: 'error',
|
|
20
|
+
code: 'UNKNOWN',
|
|
21
|
+
message: err.message || 'Unknown error',
|
|
22
|
+
category: 'fatal',
|
|
23
|
+
retryable: false,
|
|
24
|
+
transient: false,
|
|
25
|
+
exitCode: 1,
|
|
26
|
+
}, { json: true, compact: globalOpts.compact });
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
error(err.message || 'Unknown error');
|
|
30
|
+
}
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
function parseCriteria(options) {
|
|
34
|
+
const criteria = [];
|
|
35
|
+
if (options.criteria) {
|
|
36
|
+
for (const value of options.criteria.split(',')) {
|
|
37
|
+
const trimmed = value.trim();
|
|
38
|
+
if (trimmed)
|
|
39
|
+
criteria.push(trimmed);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (options.query?.trim()) {
|
|
43
|
+
criteria.push(options.query.trim());
|
|
44
|
+
}
|
|
45
|
+
return criteria;
|
|
46
|
+
}
|
|
47
|
+
export const verifyCommand = new Command('verify')
|
|
48
|
+
.description('Verify current project against criteria')
|
|
49
|
+
.option('--criteria <list>', 'Comma-separated criteria to verify')
|
|
50
|
+
.option('--query <text>', 'Single verification query')
|
|
51
|
+
.option('--scene <id>', 'Scope verification to one scene ID (supports prefix)')
|
|
52
|
+
.action(async (options, cmd) => {
|
|
53
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
54
|
+
const config = loadConfig({
|
|
55
|
+
configPath: globalOpts.config,
|
|
56
|
+
apiUrl: globalOpts.apiUrl,
|
|
57
|
+
projectId: globalOpts.projectId,
|
|
58
|
+
});
|
|
59
|
+
if (!hasAuth(config)) {
|
|
60
|
+
if (globalOpts.json) {
|
|
61
|
+
output({
|
|
62
|
+
type: 'error',
|
|
63
|
+
code: 'AUTH_MISSING',
|
|
64
|
+
message: 'Not authenticated',
|
|
65
|
+
category: 'auth',
|
|
66
|
+
retryable: false,
|
|
67
|
+
transient: false,
|
|
68
|
+
exitCode: 13,
|
|
69
|
+
suggestion: 'Run: baz auth login <api-key>',
|
|
70
|
+
}, { json: true, compact: globalOpts.compact });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
error('Not authenticated', 'Run: baz auth login <api-key>');
|
|
74
|
+
}
|
|
75
|
+
process.exit(13);
|
|
76
|
+
}
|
|
77
|
+
const criteria = parseCriteria(options);
|
|
78
|
+
if (criteria.length === 0) {
|
|
79
|
+
if (globalOpts.json) {
|
|
80
|
+
output({
|
|
81
|
+
type: 'error',
|
|
82
|
+
code: 'VALIDATION',
|
|
83
|
+
message: 'Provide --criteria or --query',
|
|
84
|
+
category: 'validation',
|
|
85
|
+
retryable: false,
|
|
86
|
+
transient: false,
|
|
87
|
+
exitCode: 65,
|
|
88
|
+
}, { json: true, compact: globalOpts.compact });
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
error('Provide --criteria or --query');
|
|
92
|
+
}
|
|
93
|
+
process.exit(65);
|
|
94
|
+
}
|
|
95
|
+
let projectId;
|
|
96
|
+
try {
|
|
97
|
+
projectId = getProjectId(config, globalOpts.projectId);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
if (globalOpts.json) {
|
|
101
|
+
output({
|
|
102
|
+
type: 'error',
|
|
103
|
+
code: 'VALIDATION',
|
|
104
|
+
message: err.message,
|
|
105
|
+
category: 'validation',
|
|
106
|
+
retryable: false,
|
|
107
|
+
transient: false,
|
|
108
|
+
exitCode: 64,
|
|
109
|
+
}, { json: true, compact: globalOpts.compact });
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
error(err.message);
|
|
113
|
+
}
|
|
114
|
+
process.exit(64);
|
|
115
|
+
}
|
|
116
|
+
const spinner = globalOpts.json ? null : ora('Verifying criteria...').start();
|
|
117
|
+
try {
|
|
118
|
+
const response = await apiRequest(config, 'project.verify', {
|
|
119
|
+
projectId,
|
|
120
|
+
criteria,
|
|
121
|
+
...(options.scene ? { sceneId: options.scene } : {}),
|
|
122
|
+
});
|
|
123
|
+
spinner?.stop();
|
|
124
|
+
if (globalOpts.json) {
|
|
125
|
+
output(response, { json: true, compact: globalOpts.compact });
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const scopeText = response.scope?.sceneId
|
|
129
|
+
? `scene ${response.scope.sceneId}`
|
|
130
|
+
: `project ${projectId.slice(0, 8)}...`;
|
|
131
|
+
console.log(chalk.bold('Verification Result'));
|
|
132
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
133
|
+
console.log(`Scope: ${scopeText}`);
|
|
134
|
+
console.log(`Duration: ${formatDuration(response.totalDurationSeconds)}`);
|
|
135
|
+
console.log();
|
|
136
|
+
for (const item of response.criteria) {
|
|
137
|
+
const marker = item.passed ? chalk.green('✓') : chalk.red('✗');
|
|
138
|
+
console.log(`${marker} ${item.criterion}`);
|
|
139
|
+
console.log(chalk.gray(` ${item.evidence}`));
|
|
140
|
+
}
|
|
141
|
+
console.log();
|
|
142
|
+
console.log(response.passedAll ? chalk.green('All criteria passed.') : chalk.yellow('Some criteria failed.'));
|
|
143
|
+
}
|
|
144
|
+
process.exit(response.passedAll ? 0 : 65);
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
spinner?.stop();
|
|
148
|
+
handleVerifyError(err, globalOpts);
|
|
149
|
+
}
|
|
150
|
+
});
|
package/dist/index.d.ts
ADDED