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,362 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { readFileSync, existsSync, statSync } from 'fs';
5
+ import { basename, extname } from 'path';
6
+ import { loadConfig, hasAuth, getProjectId } from '../lib/config.js';
7
+ import { apiRequest, uploadMedia } from '../lib/api.js';
8
+ import { error, output, table } from '../lib/output.js';
9
+ /**
10
+ * Format file size in human-readable format
11
+ */
12
+ function formatSize(bytes) {
13
+ if (bytes < 1024)
14
+ return `${bytes} B`;
15
+ if (bytes < 1024 * 1024)
16
+ return `${(bytes / 1024).toFixed(1)} KB`;
17
+ if (bytes < 1024 * 1024 * 1024)
18
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
19
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
20
+ }
21
+ /**
22
+ * Determine file type from extension
23
+ */
24
+ function getFileType(filename) {
25
+ const ext = extname(filename).toLowerCase().slice(1);
26
+ const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'heic', 'heif', 'tiff', 'bmp', 'svg'];
27
+ const videoExts = ['mp4', 'webm', 'mov', 'avi', 'mkv', 'm4v', 'wmv', 'flv'];
28
+ const audioExts = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma'];
29
+ if (imageExts.includes(ext))
30
+ return 'image';
31
+ if (videoExts.includes(ext))
32
+ return 'video';
33
+ if (audioExts.includes(ext))
34
+ return 'audio';
35
+ return 'unknown';
36
+ }
37
+ /**
38
+ * Get MIME type from filename
39
+ */
40
+ function getMimeType(filename) {
41
+ const ext = extname(filename).toLowerCase().slice(1);
42
+ const mimeTypes = {
43
+ // Images
44
+ jpg: 'image/jpeg',
45
+ jpeg: 'image/jpeg',
46
+ png: 'image/png',
47
+ gif: 'image/gif',
48
+ webp: 'image/webp',
49
+ avif: 'image/avif',
50
+ heic: 'image/heic',
51
+ heif: 'image/heif',
52
+ tiff: 'image/tiff',
53
+ bmp: 'image/bmp',
54
+ svg: 'image/svg+xml',
55
+ // Videos
56
+ mp4: 'video/mp4',
57
+ webm: 'video/webm',
58
+ mov: 'video/quicktime',
59
+ avi: 'video/x-msvideo',
60
+ mkv: 'video/x-matroska',
61
+ m4v: 'video/x-m4v',
62
+ wmv: 'video/x-ms-wmv',
63
+ flv: 'video/x-flv',
64
+ // Audio
65
+ mp3: 'audio/mpeg',
66
+ wav: 'audio/wav',
67
+ ogg: 'audio/ogg',
68
+ flac: 'audio/flac',
69
+ aac: 'audio/aac',
70
+ m4a: 'audio/mp4',
71
+ wma: 'audio/x-ms-wma',
72
+ };
73
+ return mimeTypes[ext] || 'application/octet-stream';
74
+ }
75
+ export const mediaCommand = new Command('media')
76
+ .description('Manage media files in your project');
77
+ // Upload subcommand
78
+ mediaCommand
79
+ .command('upload <file>')
80
+ .description('Upload an image, video, or audio file to the media library')
81
+ .option('--name <name>', 'Custom name for the uploaded file')
82
+ .action(async (filePath, options, cmd) => {
83
+ const globalOpts = cmd.optsWithGlobals();
84
+ const config = loadConfig({
85
+ configPath: globalOpts.config,
86
+ apiUrl: globalOpts.apiUrl,
87
+ projectId: globalOpts.projectId,
88
+ });
89
+ if (!hasAuth(config)) {
90
+ if (globalOpts.json) {
91
+ output({
92
+ type: 'error',
93
+ code: 'AUTH_MISSING',
94
+ message: 'Not authenticated',
95
+ category: 'auth',
96
+ retryable: false,
97
+ transient: false,
98
+ exitCode: 13,
99
+ suggestion: 'Run: baz auth login <api-key>',
100
+ }, { json: true, compact: globalOpts.compact });
101
+ }
102
+ else {
103
+ error('Not authenticated', 'Run: baz auth login <api-key>');
104
+ }
105
+ process.exit(13);
106
+ }
107
+ // Get project ID
108
+ let projectId;
109
+ try {
110
+ projectId = getProjectId(config, globalOpts.projectId);
111
+ }
112
+ catch (err) {
113
+ if (globalOpts.json) {
114
+ output({
115
+ type: 'error',
116
+ code: 'VALIDATION',
117
+ message: err.message,
118
+ category: 'validation',
119
+ retryable: false,
120
+ transient: false,
121
+ exitCode: 64,
122
+ }, { json: true, compact: globalOpts.compact });
123
+ }
124
+ else {
125
+ error(err.message);
126
+ }
127
+ process.exit(64);
128
+ }
129
+ // Validate file exists
130
+ if (!existsSync(filePath)) {
131
+ if (globalOpts.json) {
132
+ output({
133
+ type: 'error',
134
+ code: 'NOT_FOUND',
135
+ message: `File not found: ${filePath}`,
136
+ category: 'validation',
137
+ retryable: false,
138
+ transient: false,
139
+ exitCode: 64,
140
+ }, { json: true, compact: globalOpts.compact });
141
+ }
142
+ else {
143
+ error(`File not found: ${filePath}`);
144
+ }
145
+ process.exit(64);
146
+ }
147
+ // Get file info
148
+ const stats = statSync(filePath);
149
+ const filename = options.name || basename(filePath);
150
+ const fileType = getFileType(filePath);
151
+ const mimeType = getMimeType(filePath);
152
+ // Validate file type
153
+ if (fileType === 'unknown') {
154
+ if (globalOpts.json) {
155
+ output({
156
+ type: 'error',
157
+ code: 'VALIDATION',
158
+ message: `Unsupported file type: ${extname(filePath)}`,
159
+ category: 'validation',
160
+ retryable: false,
161
+ transient: false,
162
+ exitCode: 64,
163
+ }, { json: true, compact: globalOpts.compact });
164
+ }
165
+ else {
166
+ error(`Unsupported file type: ${extname(filePath)}`);
167
+ console.log(chalk.gray('\nSupported formats:'));
168
+ console.log(chalk.gray(' Images: jpg, png, gif, webp, avif, heic'));
169
+ console.log(chalk.gray(' Videos: mp4, webm, mov, avi, mkv'));
170
+ console.log(chalk.gray(' Audio: mp3, wav, ogg, flac, aac, m4a'));
171
+ }
172
+ process.exit(64);
173
+ }
174
+ // Check file size limits
175
+ const maxSizes = {
176
+ image: 50 * 1024 * 1024, // 50MB
177
+ video: 2 * 1024 * 1024 * 1024, // 2GB
178
+ audio: 500 * 1024 * 1024, // 500MB
179
+ };
180
+ if (stats.size > maxSizes[fileType]) {
181
+ if (globalOpts.json) {
182
+ output({
183
+ type: 'error',
184
+ code: 'VALIDATION',
185
+ message: `File too large: ${formatSize(stats.size)}`,
186
+ category: 'validation',
187
+ retryable: false,
188
+ transient: false,
189
+ exitCode: 64,
190
+ details: `Maximum size for ${fileType}: ${formatSize(maxSizes[fileType])}`,
191
+ }, { json: true, compact: globalOpts.compact });
192
+ }
193
+ else {
194
+ error(`File too large: ${formatSize(stats.size)}`);
195
+ console.log(chalk.gray(`Maximum size for ${fileType}: ${formatSize(maxSizes[fileType])}`));
196
+ }
197
+ process.exit(64);
198
+ }
199
+ // Show upload info
200
+ if (!globalOpts.json) {
201
+ console.log(chalk.cyan(`Uploading ${fileType}:`), chalk.white(filename));
202
+ console.log(chalk.gray(` Size: ${formatSize(stats.size)}`));
203
+ console.log(chalk.gray(` Type: ${mimeType}`));
204
+ console.log(chalk.gray(` Project: ${projectId}`));
205
+ console.log();
206
+ }
207
+ const spinner = ora('Uploading...').start();
208
+ try {
209
+ const buffer = readFileSync(filePath);
210
+ const result = await uploadMedia(config, {
211
+ projectId,
212
+ buffer,
213
+ filename,
214
+ mimeType,
215
+ });
216
+ spinner.succeed('Upload complete');
217
+ if (globalOpts.json) {
218
+ output(result, { json: true });
219
+ }
220
+ else {
221
+ console.log();
222
+ console.log(chalk.green('┌' + '─'.repeat(48) + '┐'));
223
+ console.log(chalk.green('│') + chalk.green.bold(' ✓ File uploaded successfully').padEnd(48) + chalk.green('│'));
224
+ console.log(chalk.green('└' + '─'.repeat(48) + '┘'));
225
+ console.log();
226
+ console.log(chalk.gray('URL:'), chalk.cyan(result.url));
227
+ console.log(chalk.gray('Key:'), chalk.dim(result.key));
228
+ console.log();
229
+ console.log(chalk.dim('Use this URL in your prompts to reference the file.'));
230
+ }
231
+ }
232
+ catch (err) {
233
+ spinner.fail('Upload failed');
234
+ if (globalOpts.json) {
235
+ output({
236
+ type: 'error',
237
+ code: 'UNKNOWN',
238
+ message: err.message,
239
+ category: 'fatal',
240
+ retryable: false,
241
+ transient: false,
242
+ exitCode: 1,
243
+ }, { json: true, compact: globalOpts.compact });
244
+ }
245
+ else {
246
+ error(err.message);
247
+ }
248
+ process.exit(1);
249
+ }
250
+ });
251
+ // List subcommand
252
+ mediaCommand
253
+ .command('list')
254
+ .description('List uploaded media files for the active project')
255
+ .option('--type <type>', 'Filter by type: image, video, audio, logo')
256
+ .option('--limit <n>', 'Max items to show', '50')
257
+ .action(async (options, cmd) => {
258
+ const globalOpts = cmd.optsWithGlobals();
259
+ const config = loadConfig({
260
+ configPath: globalOpts.config,
261
+ apiUrl: globalOpts.apiUrl,
262
+ projectId: globalOpts.projectId,
263
+ });
264
+ if (!hasAuth(config)) {
265
+ if (globalOpts.json) {
266
+ output({
267
+ type: 'error',
268
+ code: 'AUTH_MISSING',
269
+ message: 'Not authenticated',
270
+ category: 'auth',
271
+ retryable: false,
272
+ transient: false,
273
+ exitCode: 13,
274
+ suggestion: 'Run: baz auth login <api-key>',
275
+ }, { json: true, compact: globalOpts.compact });
276
+ }
277
+ else {
278
+ error('Not authenticated', 'Run: baz auth login <api-key>');
279
+ }
280
+ process.exit(13);
281
+ }
282
+ let projectId;
283
+ try {
284
+ projectId = getProjectId(config, globalOpts.projectId);
285
+ }
286
+ catch (err) {
287
+ if (globalOpts.json) {
288
+ output({
289
+ type: 'error',
290
+ code: 'VALIDATION',
291
+ message: err.message,
292
+ category: 'validation',
293
+ retryable: false,
294
+ transient: false,
295
+ exitCode: 64,
296
+ }, { json: true, compact: globalOpts.compact });
297
+ }
298
+ else {
299
+ error(err.message);
300
+ }
301
+ process.exit(64);
302
+ }
303
+ const spinner = globalOpts.json ? null : ora('Fetching media...').start();
304
+ try {
305
+ const result = await apiRequest(config, 'media.getMediaContext', { projectId });
306
+ spinner?.stop();
307
+ let items = result.allMedia || [];
308
+ // Filter by type
309
+ if (options.type) {
310
+ const filterType = options.type.toLowerCase();
311
+ items = items.filter(m => m.type === filterType);
312
+ }
313
+ // Apply limit
314
+ const limit = parseInt(options.limit, 10) || 50;
315
+ items = items.slice(0, limit);
316
+ if (globalOpts.json) {
317
+ const lean = items.map(m => ({
318
+ id: m.id,
319
+ url: m.url,
320
+ type: m.type,
321
+ originalName: m.originalName || null,
322
+ fileSize: m.fileSize || null,
323
+ duration: m.duration || null,
324
+ tags: m.tags || [],
325
+ }));
326
+ output(lean, { json: true, compact: globalOpts.compact });
327
+ return;
328
+ }
329
+ if (items.length === 0) {
330
+ console.log(chalk.gray('No media files found.'));
331
+ console.log(chalk.gray('Upload files with: baz media upload <file>'));
332
+ return;
333
+ }
334
+ console.log(`Media Files (${items.length} items)`);
335
+ console.log();
336
+ const rows = items.map(m => {
337
+ const name = m.originalName || '—';
338
+ const size = m.fileSize ? formatSize(m.fileSize) : '—';
339
+ const tags = (m.tags || []).slice(0, 3).join(', ') || '—';
340
+ return [m.type, name.length > 30 ? name.slice(0, 27) + '...' : name, size, tags];
341
+ });
342
+ table(['Type', 'Name', 'Size', 'Tags'], rows);
343
+ }
344
+ catch (err) {
345
+ spinner?.stop();
346
+ if (globalOpts.json) {
347
+ output({
348
+ type: 'error',
349
+ code: 'UNKNOWN',
350
+ message: err.message,
351
+ category: 'fatal',
352
+ retryable: false,
353
+ transient: false,
354
+ exitCode: 1,
355
+ }, { json: true, compact: globalOpts.compact });
356
+ }
357
+ else {
358
+ error(err.message);
359
+ }
360
+ process.exit(1);
361
+ }
362
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const projectCommand: Command;