@coreviz/cli 1.0.7 → 1.0.9
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 +4 -1
- package/bin/cli.js +247 -60
- package/examples/bulk_edit_with_nanobanana/62cc0c0ab7208b594ba989daa450cf54263667fc-3718x2400.jpg +0 -0
- package/examples/bulk_edit_with_nanobanana/DSC_0359.jpg +0 -0
- package/examples/bulk_edit_with_nanobanana/Elfreth's_Alley.jpeg +0 -0
- package/examples/bulk_edit_with_nanobanana/edited-1764197873666-62cc0c0ab7208b594ba989daa450cf54263667fc-3718x2400.jpg +0 -0
- package/examples/bulk_edit_with_nanobanana/edited-1764197885861-DSC_0359.jpg +0 -0
- package/examples/bulk_edit_with_nanobanana/edited-1764197898854-Elfreth's_Alley.jpeg +0 -0
- package/examples/bulk_edit_with_nanobanana/script.sh +13 -0
- package/examples/organize_by_jersey_number/2221019486.webp +0 -0
- package/examples/organize_by_jersey_number/84107528007.webp +0 -0
- package/examples/organize_by_jersey_number/DP5BW7BGSFEGTEANNGOOM6GXGA.jpg +0 -0
- package/examples/organize_by_jersey_number/Jalen Williams Oklahoma City Thunder.jpg +0 -0
- package/examples/organize_by_jersey_number/README.md +0 -0
- package/examples/organize_by_jersey_number/download-player.jpeg +0 -0
- package/examples/organize_by_jersey_number/download-thunder.jpeg +0 -0
- package/examples/organize_by_jersey_number/download.jpeg +0 -0
- package/examples/organize_by_jersey_number/okc-thunder copy.jpg +0 -0
- package/examples/organize_by_jersey_number/okc-thunder.jpg +0 -0
- package/examples/organize_by_jersey_number/script.sh +22 -0
- package/package.json +2 -2
- package/screenshots/search.png +0 -0
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ npx @coreviz/cli describe path/to/image.jpg
|
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
Edit an image with a text prompt:
|
|
55
|
+
Edit an image with a text prompt (🍌 Nano Banan + Flux Kontext in the CLI!):
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
58
|
npx @coreviz/cli edit path/to/image.jpg --prompt "make it cyberpunk style"
|
|
@@ -66,6 +66,9 @@ Search local images using natural language:
|
|
|
66
66
|
npx @coreviz/cli search "a person wearing a red t-shirt"
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
|
|
69
72
|
This will index the images in your current directory (creating a `.index.db` file) and return the top matches for your query.
|
|
70
73
|
|
|
71
74
|
## Development
|
package/bin/cli.js
CHANGED
|
@@ -195,25 +195,38 @@ program.command('whoami')
|
|
|
195
195
|
}
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
-
program.command('edit <image-path>')
|
|
198
|
+
program.command('edit <image-path> <prompt>')
|
|
199
199
|
.description('Edit an image using AI')
|
|
200
|
-
.option('
|
|
201
|
-
.action(async (imagePath, options) => {
|
|
202
|
-
|
|
200
|
+
.option('--quiet', 'Suppress UI output (for scripting)')
|
|
201
|
+
.action(async (imagePath, prompt, options) => {
|
|
202
|
+
if (!options.quiet) {
|
|
203
|
+
intro(chalk.bgHex('#663399').white('CoreViz'));
|
|
204
|
+
}
|
|
203
205
|
|
|
204
206
|
const session = config.get('session');
|
|
205
207
|
if (!session || !session.access_token) {
|
|
208
|
+
if (options.quiet) {
|
|
209
|
+
console.error('Not logged in.');
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
206
212
|
cancel('You are not logged in. Please run `coreviz login` first.');
|
|
207
213
|
process.exit(1);
|
|
208
214
|
}
|
|
209
215
|
|
|
210
216
|
if (!fs.existsSync(imagePath)) {
|
|
217
|
+
if (options.quiet) {
|
|
218
|
+
console.error(`File not found: ${imagePath}`);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
211
221
|
cancel(`File not found: ${imagePath}`);
|
|
212
222
|
process.exit(1);
|
|
213
223
|
}
|
|
214
224
|
|
|
215
|
-
let prompt = options.prompt;
|
|
216
225
|
if (!prompt) {
|
|
226
|
+
if (options.quiet) {
|
|
227
|
+
console.error('Prompt is required in quiet mode.');
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
217
230
|
prompt = await text({
|
|
218
231
|
message: 'What would you like to change in the image?',
|
|
219
232
|
placeholder: 'e.g., "Make it look like a painting" or "Add a red hat"',
|
|
@@ -228,8 +241,11 @@ program.command('edit <image-path>')
|
|
|
228
241
|
}
|
|
229
242
|
}
|
|
230
243
|
|
|
231
|
-
|
|
232
|
-
|
|
244
|
+
let spinner;
|
|
245
|
+
if (!options.quiet) {
|
|
246
|
+
spinner = yoctoSpinner({ text: "Processing image..." });
|
|
247
|
+
spinner.start();
|
|
248
|
+
}
|
|
233
249
|
|
|
234
250
|
try {
|
|
235
251
|
const base64Image = readImageAsBase64(imagePath);
|
|
@@ -239,74 +255,229 @@ program.command('edit <image-path>')
|
|
|
239
255
|
prompt
|
|
240
256
|
});
|
|
241
257
|
|
|
242
|
-
spinner.stop();
|
|
258
|
+
if (spinner) spinner.stop();
|
|
243
259
|
|
|
244
260
|
// Save result
|
|
245
261
|
const outputFilename = `edited-${Date.now()}-${path.basename(imagePath)}`;
|
|
246
262
|
const outputBuffer = Buffer.from(resultBase64.replace(/^data:image\/\w+;base64,/, ""), 'base64');
|
|
247
263
|
fs.writeFileSync(outputFilename, outputBuffer);
|
|
248
264
|
|
|
249
|
-
|
|
265
|
+
if (options.quiet) {
|
|
266
|
+
console.log(outputFilename);
|
|
267
|
+
} else {
|
|
268
|
+
outro(chalk.green(`✅ Image edited successfully! Saved as ${outputFilename}`));
|
|
269
|
+
}
|
|
250
270
|
|
|
251
271
|
} catch (error) {
|
|
252
|
-
spinner.stop();
|
|
253
|
-
|
|
272
|
+
if (spinner) spinner.stop();
|
|
273
|
+
const msg = error.message.includes('credits')
|
|
274
|
+
? 'Insufficient credits. Please add credits to your account on https://lab.coreviz.io.'
|
|
275
|
+
: `Failed to edit image: ${error.message}`;
|
|
276
|
+
if (options.quiet) {
|
|
277
|
+
console.error(msg);
|
|
278
|
+
} else {
|
|
279
|
+
cancel(msg);
|
|
280
|
+
}
|
|
254
281
|
process.exit(1);
|
|
255
282
|
}
|
|
256
283
|
});
|
|
257
284
|
|
|
258
285
|
program.command('describe <image-path>')
|
|
259
286
|
.description('Describe an image using AI')
|
|
260
|
-
.
|
|
261
|
-
|
|
287
|
+
.option('--quiet', 'Suppress UI output (for scripting)')
|
|
288
|
+
.action(async (imagePath, options) => {
|
|
289
|
+
if (!options.quiet) {
|
|
290
|
+
intro(chalk.bgHex('#663399').white('CoreViz'));
|
|
291
|
+
}
|
|
262
292
|
|
|
263
293
|
const session = config.get('session');
|
|
264
294
|
if (!session || !session.access_token) {
|
|
295
|
+
if (options.quiet) {
|
|
296
|
+
console.error('Not logged in.');
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
265
299
|
cancel('You are not logged in. Please run `coreviz login` first.');
|
|
266
300
|
process.exit(1);
|
|
267
301
|
}
|
|
268
302
|
|
|
269
303
|
if (!fs.existsSync(imagePath)) {
|
|
304
|
+
if (options.quiet) {
|
|
305
|
+
console.error(`File not found: ${imagePath}`);
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
270
308
|
cancel(`File not found: ${imagePath}`);
|
|
271
309
|
process.exit(1);
|
|
272
310
|
}
|
|
273
311
|
|
|
274
|
-
|
|
275
|
-
|
|
312
|
+
let spinner;
|
|
313
|
+
if (!options.quiet) {
|
|
314
|
+
spinner = yoctoSpinner({ text: "Analyzing image..." });
|
|
315
|
+
spinner.start();
|
|
316
|
+
}
|
|
276
317
|
|
|
277
318
|
try {
|
|
278
319
|
const base64Image = readImageAsBase64(imagePath);
|
|
279
320
|
const coreviz = new CoreViz({ token: session.access_token });
|
|
280
321
|
const description = await coreviz.describe(base64Image);
|
|
281
322
|
|
|
282
|
-
spinner.stop();
|
|
323
|
+
if (spinner) spinner.stop();
|
|
283
324
|
|
|
284
|
-
|
|
285
|
-
|
|
325
|
+
if (options.quiet) {
|
|
326
|
+
console.log(description);
|
|
327
|
+
} else {
|
|
328
|
+
outro(chalk.green('✅ Image description:'));
|
|
329
|
+
console.log(description);
|
|
330
|
+
}
|
|
286
331
|
} catch (error) {
|
|
287
|
-
spinner.stop();
|
|
288
|
-
|
|
289
|
-
|
|
332
|
+
if (spinner) spinner.stop();
|
|
333
|
+
const msg = error.message.includes('credits')
|
|
334
|
+
? 'Insufficient credits. Please add credits to your account on https://lab.coreviz.io.'
|
|
335
|
+
: `Failed to describe image: ${error.message}`;
|
|
336
|
+
|
|
337
|
+
if (options.quiet) {
|
|
338
|
+
console.error(msg);
|
|
339
|
+
} else {
|
|
340
|
+
cancel(msg);
|
|
341
|
+
}
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
program.command('tag <image-path> <prompt>')
|
|
347
|
+
.description('Generate tags for an image using AI')
|
|
348
|
+
.option('--choices <items>', 'Comma-separated list of possible tags to choose from (optional)', '')
|
|
349
|
+
.option('--single', 'Return only one tag', false)
|
|
350
|
+
.option('-m, --mode <mode>', 'The mode to use for tagging. Defaults to "api".', 'api')
|
|
351
|
+
.option('--quiet', 'Output raw text for scripting (suppresses UI)')
|
|
352
|
+
.action(async (imagePath, prompt, options) => {
|
|
353
|
+
if (!options.quiet) {
|
|
354
|
+
intro(chalk.bgHex('#663399').white('CoreViz'));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const session = config.get('session');
|
|
358
|
+
if (!session || !session.access_token) {
|
|
359
|
+
if (options.quiet) {
|
|
360
|
+
console.error('Not logged in.');
|
|
290
361
|
process.exit(1);
|
|
291
362
|
}
|
|
292
|
-
cancel(
|
|
363
|
+
cancel('You are not logged in. Please run `coreviz login` first.');
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (!fs.existsSync(imagePath)) {
|
|
368
|
+
if (options.quiet) {
|
|
369
|
+
console.error(`File not found: ${imagePath}`);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
cancel(`File not found: ${imagePath}`);
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let tagList = options.choices ? options.choices.split(',').map(s => s.trim()) : undefined;
|
|
377
|
+
|
|
378
|
+
if (!prompt) {
|
|
379
|
+
if (tagList && tagList.length > 0) {
|
|
380
|
+
prompt = "Select the best matching tags";
|
|
381
|
+
} else {
|
|
382
|
+
if (options.quiet) {
|
|
383
|
+
console.error('Prompt is required in quiet mode.');
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
prompt = await text({
|
|
387
|
+
message: 'What kind of tags do you want to generate?',
|
|
388
|
+
placeholder: 'e.g., "jersey number of the player", "color of the car", etc.',
|
|
389
|
+
validate(value) {
|
|
390
|
+
if (value.length === 0) return `Value is required!`;
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
if (isCancel(prompt)) {
|
|
395
|
+
cancel('Operation cancelled.');
|
|
396
|
+
process.exit(0);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
let spinner;
|
|
402
|
+
if (!options.quiet) {
|
|
403
|
+
setTimeout(() => {
|
|
404
|
+
if (spinner.isSpinning && options.mode === 'local') {
|
|
405
|
+
spinner.text = "On the first run, it might take a few minutes to load the local model, please wait...";
|
|
406
|
+
} else if (spinner.isSpinning && options.mode === 'api') {
|
|
407
|
+
spinner.text = "This might take a few seconds...";
|
|
408
|
+
}
|
|
409
|
+
}, 8000);
|
|
410
|
+
spinner = yoctoSpinner({ text: "Generating tags..." });
|
|
411
|
+
spinner.start();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
try {
|
|
415
|
+
const base64Image = readImageAsBase64(imagePath);
|
|
416
|
+
const coreviz = new CoreViz({ token: session.access_token });
|
|
417
|
+
|
|
418
|
+
const response = await coreviz.tag(base64Image, {
|
|
419
|
+
mode: options.mode,
|
|
420
|
+
prompt,
|
|
421
|
+
options: tagList,
|
|
422
|
+
multiple: !options.single
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
if (spinner) spinner.stop();
|
|
426
|
+
|
|
427
|
+
if (options.quiet) {
|
|
428
|
+
if (response.tags && response.tags.length > 0) {
|
|
429
|
+
console.log(response.tags.join('\n'));
|
|
430
|
+
}
|
|
431
|
+
} else {
|
|
432
|
+
if (response.tags && response.tags.length > 0) {
|
|
433
|
+
outro(chalk.green('✅ Tags generated:'));
|
|
434
|
+
response.tags.forEach(tag => console.log(chalk.blue(`• ${tag}`)));
|
|
435
|
+
} else {
|
|
436
|
+
outro(chalk.yellow('No tags generated.'));
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
} catch (error) {
|
|
441
|
+
if (spinner) spinner.stop();
|
|
442
|
+
const msg = error.message.includes('credits')
|
|
443
|
+
? 'Insufficient credits. Please add credits to your account on https://lab.coreviz.io.'
|
|
444
|
+
: `Failed to generate tags: ${error.message}`;
|
|
445
|
+
|
|
446
|
+
if (options.quiet) {
|
|
447
|
+
console.error(msg);
|
|
448
|
+
} else {
|
|
449
|
+
cancel(msg);
|
|
450
|
+
}
|
|
293
451
|
process.exit(1);
|
|
294
452
|
}
|
|
295
453
|
});
|
|
296
454
|
|
|
297
455
|
program.command('search <query>')
|
|
298
456
|
.description('Search for images in the current directory using AI')
|
|
299
|
-
.
|
|
300
|
-
|
|
457
|
+
.option('-m, --mode <mode>', 'The mode to use for embedding. Defaults to "local".', 'local')
|
|
458
|
+
.option('--quiet', 'Suppress UI output (for scripting)')
|
|
459
|
+
.action(async (query, options) => {
|
|
460
|
+
if (!options.quiet) {
|
|
461
|
+
intro(chalk.bgHex('#663399').white('CoreViz'));
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const mode = options.mode || 'local';
|
|
301
465
|
|
|
302
466
|
const session = config.get('session');
|
|
303
467
|
if (!session || !session.access_token) {
|
|
468
|
+
if (options.quiet) {
|
|
469
|
+
console.error('Not logged in.');
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
304
472
|
cancel('You are not logged in. Please run `coreviz login` first.');
|
|
305
473
|
process.exit(1);
|
|
306
474
|
}
|
|
307
475
|
|
|
308
|
-
|
|
309
|
-
|
|
476
|
+
let spinner;
|
|
477
|
+
if (!options.quiet) {
|
|
478
|
+
spinner = yoctoSpinner({ text: "Indexing directory..." });
|
|
479
|
+
spinner.start();
|
|
480
|
+
}
|
|
310
481
|
|
|
311
482
|
const dbPath = path.join(process.cwd(), '.index.db');
|
|
312
483
|
const db = new Database(dbPath);
|
|
@@ -325,7 +496,12 @@ program.command('search <query>')
|
|
|
325
496
|
.filter(file => imageExtensions.includes(path.extname(file).toLowerCase()));
|
|
326
497
|
|
|
327
498
|
if (files.length === 0) {
|
|
328
|
-
spinner.stop();
|
|
499
|
+
if (spinner) spinner.stop();
|
|
500
|
+
if (options.quiet) {
|
|
501
|
+
// No images found, just exit with 0 (empty result) or 1?
|
|
502
|
+
// Usually empty search is exit 0 with empty stdout.
|
|
503
|
+
process.exit(0);
|
|
504
|
+
}
|
|
329
505
|
cancel('No images found in the current directory.');
|
|
330
506
|
process.exit(0);
|
|
331
507
|
}
|
|
@@ -345,6 +521,17 @@ program.command('search <query>')
|
|
|
345
521
|
}
|
|
346
522
|
}
|
|
347
523
|
|
|
524
|
+
if (mode === 'local') {
|
|
525
|
+
// You're using the local model, it might take a few minutes for the model to load on the first run.
|
|
526
|
+
setTimeout(() => {
|
|
527
|
+
if (spinner.isSpinning && mode === 'local') {
|
|
528
|
+
spinner.text = "On the first run, it might take a few minutes to load the local model, please wait...";
|
|
529
|
+
}
|
|
530
|
+
}, 8000);
|
|
531
|
+
await coreviz.embed('text', { type: 'text', mode: mode });
|
|
532
|
+
if (spinner) spinner.stop();
|
|
533
|
+
}
|
|
534
|
+
|
|
348
535
|
for (const file of files) {
|
|
349
536
|
const filePath = path.join(process.cwd(), file);
|
|
350
537
|
const stats = fs.statSync(filePath);
|
|
@@ -357,23 +544,29 @@ program.command('search <query>')
|
|
|
357
544
|
continue;
|
|
358
545
|
}
|
|
359
546
|
|
|
360
|
-
spinner.text = `Indexing ${file}...`;
|
|
547
|
+
if (spinner) spinner.text = `Indexing ${file}...`;
|
|
361
548
|
|
|
362
549
|
try {
|
|
363
550
|
const base64Image = readImageAsBase64(filePath);
|
|
364
|
-
const { embedding } = await coreviz.embed(base64Image, { type: 'image', mode:
|
|
551
|
+
const { embedding } = await coreviz.embed(base64Image, { type: 'image', mode: mode });
|
|
365
552
|
|
|
366
553
|
upsertFile.run(file, mtime, JSON.stringify(embedding));
|
|
367
554
|
} catch (error) {
|
|
368
555
|
// Log error but continue
|
|
369
|
-
|
|
556
|
+
if (!options.quiet) {
|
|
557
|
+
if (error.message.includes('credits')) {
|
|
558
|
+
cancel('Insufficient credits. Please add credits to your account on https://lab.coreviz.io.');
|
|
559
|
+
process.exit(1);
|
|
560
|
+
}
|
|
561
|
+
console.error(`Failed to index ${file}: ${error.message}`);
|
|
562
|
+
}
|
|
370
563
|
}
|
|
371
564
|
}
|
|
372
565
|
|
|
373
|
-
spinner.text = "Processing search query...";
|
|
566
|
+
if (spinner) spinner.text = "Processing search query...";
|
|
374
567
|
|
|
375
568
|
try {
|
|
376
|
-
const { embedding: queryEmbedding } = await coreviz.embed(query, { type: 'text', mode:
|
|
569
|
+
const { embedding: queryEmbedding } = await coreviz.embed(query, { type: 'text', mode: mode });
|
|
377
570
|
|
|
378
571
|
const rows = db.prepare('SELECT path, embedding FROM images').all();
|
|
379
572
|
const results = [];
|
|
@@ -384,50 +577,44 @@ program.command('search <query>')
|
|
|
384
577
|
const fileEmbedding = JSON.parse(row.embedding);
|
|
385
578
|
|
|
386
579
|
// Calculate cosine similarity
|
|
387
|
-
const similarity =
|
|
580
|
+
const similarity = coreviz.similarity(queryEmbedding, fileEmbedding);
|
|
388
581
|
results.push({ file: row.path, similarity });
|
|
389
582
|
}
|
|
390
583
|
|
|
391
584
|
// Sort by similarity descending
|
|
392
585
|
results.sort((a, b) => b.similarity - a.similarity);
|
|
393
586
|
|
|
394
|
-
spinner.stop();
|
|
587
|
+
if (spinner) spinner.stop();
|
|
395
588
|
|
|
396
|
-
|
|
589
|
+
if (options.quiet) {
|
|
590
|
+
// Output raw file paths (top 5)
|
|
591
|
+
results.slice(0, 5).forEach(result => {
|
|
592
|
+
console.log(result.file);
|
|
593
|
+
});
|
|
594
|
+
} else {
|
|
595
|
+
outro(chalk.green(`✅ Search results for "${query}"`));
|
|
397
596
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
597
|
+
// Show top 5 results
|
|
598
|
+
results.slice(0, 5).forEach((result, i) => {
|
|
599
|
+
const score = (result.similarity * 100).toFixed(1);
|
|
600
|
+
console.log(`${i + 1}. ${chalk.bold(result.file)} ${chalk.gray(`(${score}%)`)}`);
|
|
601
|
+
});
|
|
602
|
+
}
|
|
403
603
|
|
|
404
604
|
} catch (error) {
|
|
405
|
-
spinner.stop();
|
|
406
|
-
|
|
605
|
+
if (spinner) spinner.stop();
|
|
606
|
+
const msg = `Search failed: ${error.message}`;
|
|
607
|
+
if (options.quiet) {
|
|
608
|
+
console.error(msg);
|
|
609
|
+
} else {
|
|
610
|
+
cancel(msg);
|
|
611
|
+
}
|
|
407
612
|
process.exit(1);
|
|
408
613
|
} finally {
|
|
409
614
|
db.close();
|
|
410
615
|
}
|
|
411
616
|
});
|
|
412
617
|
|
|
413
|
-
function cosineSimilarity(vecA, vecB) {
|
|
414
|
-
if (vecA.length !== vecB.length) return 0;
|
|
415
|
-
|
|
416
|
-
let dotProduct = 0;
|
|
417
|
-
let normA = 0;
|
|
418
|
-
let normB = 0;
|
|
419
|
-
|
|
420
|
-
for (let i = 0; i < vecA.length; i++) {
|
|
421
|
-
dotProduct += vecA[i] * vecB[i];
|
|
422
|
-
normA += vecA[i] * vecA[i];
|
|
423
|
-
normB += vecB[i] * vecB[i];
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
if (normA === 0 || normB === 0) return 0;
|
|
427
|
-
|
|
428
|
-
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
429
|
-
}
|
|
430
|
-
|
|
431
618
|
function readImageAsBase64(imagePath) {
|
|
432
619
|
const imageBuffer = fs.readFileSync(imagePath);
|
|
433
620
|
return `data:image/${path.extname(imagePath).slice(1) || 'jpeg'};base64,${imageBuffer.toString('base64')}`;
|
package/examples/bulk_edit_with_nanobanana/62cc0c0ab7208b594ba989daa450cf54263667fc-3718x2400.jpg
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Loop through all image files
|
|
4
|
+
for file in *.jpg *.png *.jpeg *.webp; do
|
|
5
|
+
[ -e "$file" ] || continue
|
|
6
|
+
|
|
7
|
+
echo "Processing $file..."
|
|
8
|
+
|
|
9
|
+
# Extract jersey number using coreviz tag with --quiet
|
|
10
|
+
jersey_number=$(npx @coreviz/cli edit "$file" --prompt "make it cyberpunk style")
|
|
11
|
+
|
|
12
|
+
echo " Edited image: $edited_image"
|
|
13
|
+
done
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Loop through all image files
|
|
4
|
+
for file in *.jpg *.png *.jpeg *.webp; do
|
|
5
|
+
[ -e "$file" ] || continue
|
|
6
|
+
|
|
7
|
+
echo "Processing $file..."
|
|
8
|
+
|
|
9
|
+
# Extract jersey number using coreviz tag with --quiet
|
|
10
|
+
jersey_number=$(npx @coreviz/cli tag "$file" "What is the player's jersey number? Return only the number." \
|
|
11
|
+
--single \
|
|
12
|
+
--mode local \
|
|
13
|
+
--quiet)
|
|
14
|
+
|
|
15
|
+
if [ -n "$jersey_number" ]; then
|
|
16
|
+
echo " Found jersey number: $jersey_number"
|
|
17
|
+
mkdir -p "player_$jersey_number"
|
|
18
|
+
mv "$file" "player_$jersey_number/"
|
|
19
|
+
else
|
|
20
|
+
echo " Could not detect jersey number for $file"
|
|
21
|
+
fi
|
|
22
|
+
done
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coreviz/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CoreViz CLI tool",
|
|
6
6
|
"main": "index.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"homepage": "https://github.com/CoreViz/cli#readme",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@clack/prompts": "^0.11.0",
|
|
30
|
-
"@coreviz/sdk": "^1.0.
|
|
30
|
+
"@coreviz/sdk": "^1.0.10",
|
|
31
31
|
"better-auth": "^1.4.2",
|
|
32
32
|
"better-sqlite3": "^12.4.6",
|
|
33
33
|
"chalk": "^5.6.2",
|
|
Binary file
|