86 lines
2.8 KiB
TypeScript
86 lines
2.8 KiB
TypeScript
#!/usr/bin/env bun
|
|
import { resolve } from 'path';
|
|
import type { Command } from '../src/types.ts';
|
|
import { run } from '../src/index.ts';
|
|
|
|
// Load .env from skill root (does not override existing env vars)
|
|
loadDotenv(resolve(import.meta.dir, '../.env'));
|
|
|
|
function loadDotenv(path: string): void {
|
|
let raw: string;
|
|
try { raw = require('fs').readFileSync(path, 'utf-8'); } catch { return; }
|
|
for (const line of raw.split('\n')) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
const eq = trimmed.indexOf('=');
|
|
if (eq < 0) continue;
|
|
const key = trimmed.slice(0, eq).trim();
|
|
const val = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, '');
|
|
if (key && !(key in process.env)) process.env[key] = val;
|
|
}
|
|
}
|
|
|
|
function printUsage(): void {
|
|
console.error(`Usage:
|
|
bun scripts/run.ts [--api-base=<url>] <command> [args...] [--dry-run]
|
|
|
|
Commands:
|
|
session
|
|
Get auth session token
|
|
|
|
detect <video-path> [options]
|
|
Extract frames and detect ecommerce product snapshots
|
|
Options:
|
|
--interval=<seconds> Frame sampling interval (default: 3)
|
|
--max-frames=<n> Max frames to analyze (default: 30)
|
|
--output-dir=<dir> Where to save snapshots (default: next to video)
|
|
--min-confidence=<0-1> Minimum detection confidence (default: 0.7)
|
|
--concurrency=<n> Parallel Vision API calls per chunk (default: 5)
|
|
|
|
search <image-path>
|
|
Search for products using an image via the ecom image-search API
|
|
|
|
detect-and-search <video-path> [options]
|
|
Detect best product snapshot from video then run image search
|
|
|
|
Examples:
|
|
bun scripts/run.ts detect ./demo.mp4
|
|
bun scripts/run.ts detect ./demo.mp4 --interval=5 --max-frames=20
|
|
bun scripts/run.ts search ./snapshot.jpg
|
|
bun scripts/run.ts detect-and-search ./demo.mp4 --min-confidence=0.8
|
|
|
|
Config: ANTHROPIC_API_KEY env var required for detection.
|
|
auth-rt in PATH required for search commands.
|
|
`);
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
const positionals: string[] = [];
|
|
let dryRun = false;
|
|
|
|
for (const arg of process.argv.slice(2)) {
|
|
if (arg === '--dry-run') {
|
|
dryRun = true;
|
|
} else if (arg.startsWith('--api-base=')) {
|
|
process.env.API_BASE = arg.slice('--api-base='.length).trim();
|
|
} else if (arg === '-h' || arg === '--help') {
|
|
printUsage(); process.exit(0);
|
|
} else {
|
|
positionals.push(arg);
|
|
}
|
|
}
|
|
|
|
if (positionals.length < 1) { printUsage(); process.exit(1); }
|
|
|
|
const result = await run(positionals[0] as Command, positionals.slice(1), dryRun);
|
|
console.log(JSON.stringify(result, null, 2));
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(JSON.stringify({
|
|
status: 'failed',
|
|
error: err instanceof Error ? err.message : String(err),
|
|
}, null, 2));
|
|
process.exit(1);
|
|
});
|