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.
Files changed (57) hide show
  1. package/README.md +485 -3
  2. package/bin/baz.js +6 -1
  3. package/dist/commands/auth.d.ts +2 -0
  4. package/dist/commands/auth.js +109 -0
  5. package/dist/commands/capabilities.d.ts +2 -0
  6. package/dist/commands/capabilities.js +44 -0
  7. package/dist/commands/context.d.ts +13 -0
  8. package/dist/commands/context.js +498 -0
  9. package/dist/commands/export.d.ts +2 -0
  10. package/dist/commands/export.js +360 -0
  11. package/dist/commands/logs.d.ts +2 -0
  12. package/dist/commands/logs.js +180 -0
  13. package/dist/commands/loop.d.ts +2 -0
  14. package/dist/commands/loop.js +538 -0
  15. package/dist/commands/mcp.d.ts +2 -0
  16. package/dist/commands/mcp.js +143 -0
  17. package/dist/commands/media.d.ts +2 -0
  18. package/dist/commands/media.js +362 -0
  19. package/dist/commands/project.d.ts +2 -0
  20. package/dist/commands/project.js +786 -0
  21. package/dist/commands/prompt.d.ts +2 -0
  22. package/dist/commands/prompt.js +540 -0
  23. package/dist/commands/recipe.d.ts +15 -0
  24. package/dist/commands/recipe.js +607 -0
  25. package/dist/commands/review.d.ts +17 -0
  26. package/dist/commands/review.js +345 -0
  27. package/dist/commands/scenes.d.ts +2 -0
  28. package/dist/commands/scenes.js +481 -0
  29. package/dist/commands/share.d.ts +2 -0
  30. package/dist/commands/share.js +226 -0
  31. package/dist/commands/state.d.ts +2 -0
  32. package/dist/commands/state.js +171 -0
  33. package/dist/commands/status.d.ts +2 -0
  34. package/dist/commands/status.js +219 -0
  35. package/dist/commands/template.d.ts +2 -0
  36. package/dist/commands/template.js +123 -0
  37. package/dist/commands/verify.d.ts +2 -0
  38. package/dist/commands/verify.js +150 -0
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.js +124 -0
  41. package/dist/lib/api.d.ts +188 -0
  42. package/dist/lib/api.js +719 -0
  43. package/dist/lib/banner.d.ts +12 -0
  44. package/dist/lib/banner.js +69 -0
  45. package/dist/lib/config.d.ts +33 -0
  46. package/dist/lib/config.js +99 -0
  47. package/dist/lib/output.d.ts +52 -0
  48. package/dist/lib/output.js +162 -0
  49. package/dist/lib/project-state.d.ts +52 -0
  50. package/dist/lib/project-state.js +178 -0
  51. package/dist/lib/sse.d.ts +168 -0
  52. package/dist/lib/sse.js +227 -0
  53. package/dist/lib/version.d.ts +1 -0
  54. package/dist/lib/version.js +3 -0
  55. package/dist/repl.d.ts +4 -0
  56. package/dist/repl.js +764 -0
  57. package/package.json +32 -5
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const stateCommand: Command;
@@ -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,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const statusCommand: Command;
@@ -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,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const templateCommand: Command;
@@ -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,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const verifyCommand: Command;
@@ -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
+ });
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};