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,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
|
+
});
|