@clipkit/cli 1.0.0
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/LICENSE +201 -0
- package/README.md +182 -0
- package/dist/commands/docs.d.ts +3 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +31 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/explain.d.ts +3 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +25 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +176 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +88 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +26 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/new.d.ts +3 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +77 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/preview.d.ts +3 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +69 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/render.d.ts +3 -0
- package/dist/commands/render.d.ts.map +1 -0
- package/dist/commands/render.js +213 -0
- package/dist/commands/render.js.map +1 -0
- package/dist/commands/schema.d.ts +3 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +20 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/still.d.ts +3 -0
- package/dist/commands/still.d.ts.map +1 -0
- package/dist/commands/still.js +58 -0
- package/dist/commands/still.js.map +1 -0
- package/dist/commands/transcribe.d.ts +3 -0
- package/dist/commands/transcribe.d.ts.map +1 -0
- package/dist/commands/transcribe.js +52 -0
- package/dist/commands/transcribe.js.map +1 -0
- package/dist/commands/validate.d.ts +3 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +46 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/lint.d.ts +8 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +75 -0
- package/dist/lint.js.map +1 -0
- package/dist/load-source.d.ts +5 -0
- package/dist/load-source.d.ts.map +1 -0
- package/dist/load-source.js +63 -0
- package/dist/load-source.js.map +1 -0
- package/dist/templates/agents-content.d.ts +3 -0
- package/dist/templates/agents-content.d.ts.map +1 -0
- package/dist/templates/agents-content.js +3282 -0
- package/dist/templates/agents-content.js.map +1 -0
- package/dist/util.d.ts +17 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +40 -0
- package/dist/util.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// `clipkit still <file> -o poster.png` — render ONE frame of a Source to a PNG,
|
|
2
|
+
// locally (headless Chrome via @clipkit/renderer). A fast visual sanity
|
|
3
|
+
// check / thumbnail without a full encode. Same engine + setup as
|
|
4
|
+
// `render --local` (the package is an optional peer; needs Google Chrome).
|
|
5
|
+
import { writeFile } from 'node:fs/promises';
|
|
6
|
+
import { validate } from '@clipkit/protocol';
|
|
7
|
+
import { loadSource } from '../load-source.js';
|
|
8
|
+
import { printValidationErrors, fmtBytes } from '../util.js';
|
|
9
|
+
export function stillCommand(program) {
|
|
10
|
+
program
|
|
11
|
+
.command('still <file>')
|
|
12
|
+
.description('Render one frame of a Source to a PNG (local headless Chrome)')
|
|
13
|
+
.option('-o, --out <path>', 'output PNG path', 'still.png')
|
|
14
|
+
.option('-t, --time <seconds>', 'composition time to capture', '0')
|
|
15
|
+
.option('-b, --backend <backend>', 'auto | webgpu | webgl2', 'auto')
|
|
16
|
+
.action(async (file, opts) => {
|
|
17
|
+
const { path, source } = await loadSource(file);
|
|
18
|
+
const result = validate(source);
|
|
19
|
+
if (!result.valid) {
|
|
20
|
+
printValidationErrors(path, result.errors);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const time = Number(opts.time);
|
|
24
|
+
if (!Number.isFinite(time) || time < 0) {
|
|
25
|
+
process.stderr.write(`✗ invalid --time "${opts.time}".\n`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
if (!isBackend(opts.backend)) {
|
|
29
|
+
process.stderr.write(`✗ invalid --backend "${opts.backend}" (expected auto | webgpu | webgl2).\n`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
let mod;
|
|
33
|
+
try {
|
|
34
|
+
mod = (await import('@clipkit/renderer'));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
process.stderr.write('Stills need the @clipkit/renderer engine (and Google Chrome).\n\n' +
|
|
38
|
+
' npm i -g @clipkit/renderer playwright\n');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
process.stderr.write(`Capturing frame at ${time}s…\n`);
|
|
42
|
+
let png;
|
|
43
|
+
try {
|
|
44
|
+
png = await mod.renderStill({ source: result.data, time, backend: opts.backend });
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
process.stderr.write(`✗ Still failed: ${e instanceof Error ? e.message : String(e)}\n` +
|
|
48
|
+
' (Local stills use your installed Google Chrome.)\n');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
await writeFile(opts.out, png);
|
|
52
|
+
process.stdout.write(`✓ Wrote ${opts.out} (${fmtBytes(png.length)})\n`);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function isBackend(v) {
|
|
56
|
+
return v === 'auto' || v === 'webgpu' || v === 'webgl2';
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=still.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"still.js","sourceRoot":"","sources":["../../src/commands/still.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcribe.d.ts","sourceRoot":"","sources":["../../src/commands/transcribe.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqCxD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// `clipkit transcribe <file>` — transcribe an audio/video file into
|
|
2
|
+
// word-timestamped captions (the protocol's `caption` element `words[]`).
|
|
3
|
+
//
|
|
4
|
+
// Runs Whisper locally via @clipkit/speech-to-text (no API, no key). Prints the
|
|
5
|
+
// words[] JSON by default, or a full caption element with --element. Progress
|
|
6
|
+
// goes to stderr so stdout stays clean, machine-readable JSON — the agent path.
|
|
7
|
+
//
|
|
8
|
+
// The model runs in an isolated worker (transcribeFile), so onnxruntime-node's
|
|
9
|
+
// harmless teardown abort never affects this process — clean exit 0 on success.
|
|
10
|
+
import { writeFile } from 'node:fs/promises';
|
|
11
|
+
import { toCaptionWords } from '@clipkit/speech-to-text';
|
|
12
|
+
import { transcribeFile } from '@clipkit/speech-to-text/node';
|
|
13
|
+
export function transcribeCommand(program) {
|
|
14
|
+
program
|
|
15
|
+
.command('transcribe <file>')
|
|
16
|
+
.description('Transcribe an audio/video file into caption words (local Whisper; needs ffmpeg)')
|
|
17
|
+
.option('-m, --model <id>', 'Whisper model id (Hugging Face)', 'Xenova/whisper-base')
|
|
18
|
+
.option('-l, --language <code>', 'force a language (e.g. en); default auto-detect')
|
|
19
|
+
.option('-o, --out <file>', 'write JSON here (default: stdout)')
|
|
20
|
+
.option('-e, --element', 'emit a full caption element instead of just words[]')
|
|
21
|
+
.option('-t, --layer <n>', 'layer for --element (lower = nearer front)', '3')
|
|
22
|
+
.action(async (file, opts) => {
|
|
23
|
+
const log = (s) => void process.stderr.write(s);
|
|
24
|
+
log(`transcribing ${file} (${opts.model})…\n`);
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
const result = await transcribeFile(file, {
|
|
27
|
+
model: opts.model,
|
|
28
|
+
language: opts.language,
|
|
29
|
+
onLog: (line) => {
|
|
30
|
+
// The worker logs " ↓ <file>" lines as the model downloads.
|
|
31
|
+
if (line.includes('↓') && !seen.has(line)) {
|
|
32
|
+
seen.add(line);
|
|
33
|
+
log(line);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
const words = toCaptionWords(result);
|
|
38
|
+
log(` ${words.length} words, ${result.duration.toFixed(1)}s\n`);
|
|
39
|
+
const payload = opts.element
|
|
40
|
+
? { type: 'caption', time: 0, layer: Number(opts.layer) || 3, words }
|
|
41
|
+
: words;
|
|
42
|
+
const json = JSON.stringify(payload, null, 2);
|
|
43
|
+
if (opts.out) {
|
|
44
|
+
await writeFile(opts.out, json + '\n');
|
|
45
|
+
log(`wrote ${opts.out}\n`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
process.stdout.write(json + '\n');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=transcribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcribe.js","sourceRoot":"","sources":["../../src/commands/transcribe.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,0EAA0E;AAC1E,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,gFAAgF;AAChF,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAGhF,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAU9D,MAAM,UAAU,iBAAiB,CAAC,OAAgB;IAChD,OAAO;SACJ,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,iFAAiF,CAAC;SAC9F,MAAM,CAAC,kBAAkB,EAAE,iCAAiC,EAAE,qBAAqB,CAAC;SACpF,MAAM,CAAC,uBAAuB,EAAE,iDAAiD,CAAC;SAClF,MAAM,CAAC,kBAAkB,EAAE,mCAAmC,CAAC;SAC/D,MAAM,CAAC,eAAe,EAAE,qDAAqD,CAAC;SAC9E,MAAM,CAAC,iBAAiB,EAAE,4CAA4C,EAAE,GAAG,CAAC;SAC5E,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,IAAU,EAAE,EAAE;QACzC,MAAM,GAAG,GAAG,CAAC,CAAS,EAAQ,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9D,GAAG,CAAC,gBAAgB,IAAI,KAAK,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC;QAE/C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE;YACxC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;gBACd,6DAA6D;gBAC7D,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;oBAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAAC,CAAC;YAC3E,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACrC,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,WAAW,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAEjE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO;YAC1B,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE;YACrE,CAAC,CAAC,KAAK,CAAC;QACV,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;YACvC,GAAG,CAAC,SAAS,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuCtD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// `clipkit validate <file>` — check a Source against the Clipkit Protocol.
|
|
2
|
+
//
|
|
3
|
+
// Loads the file (JSON or TS), runs @clipkit/protocol's validator, and reports
|
|
4
|
+
// any errors with their JSON path. Exits 0 on success, 1 on failure. With
|
|
5
|
+
// `--explain` it also surfaces protocol-aware warnings on success (things that
|
|
6
|
+
// validate but the runtime will silently drop/clip) and adds guidance on
|
|
7
|
+
// failure.
|
|
8
|
+
import { validate, CLIPKIT_PROTOCOL_VERSION } from '@clipkit/protocol';
|
|
9
|
+
import { lintSource } from '@clipkit/lint';
|
|
10
|
+
import { loadSource } from '../load-source.js';
|
|
11
|
+
import { printValidationErrors } from '../util.js';
|
|
12
|
+
export function validateCommand(program) {
|
|
13
|
+
program
|
|
14
|
+
.command('validate <file>')
|
|
15
|
+
.description('Validate a Clipkit Source against the protocol')
|
|
16
|
+
.option('--explain', 'add protocol-aware warnings (success) and guidance (failure)')
|
|
17
|
+
.action(async (file, opts) => {
|
|
18
|
+
const { path, source } = await loadSource(file);
|
|
19
|
+
const result = validate(source);
|
|
20
|
+
if (result.valid) {
|
|
21
|
+
process.stdout.write(`✓ ${path} is a valid Clipkit Protocol v${CLIPKIT_PROTOCOL_VERSION} document.\n`);
|
|
22
|
+
if (opts.explain) {
|
|
23
|
+
const warnings = lintSource(result.data);
|
|
24
|
+
if (warnings.length === 0) {
|
|
25
|
+
process.stdout.write("✓ No warnings — nothing the runtime will silently drop or clip.\n");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
process.stdout.write(`\n⚠ ${warnings.length} warning${warnings.length === 1 ? '' : 's'} (valid, but worth a look):\n`);
|
|
29
|
+
for (const w of warnings)
|
|
30
|
+
process.stdout.write(` - ${w.where}: ${w.message}\n`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
printValidationErrors(path, result.errors);
|
|
36
|
+
if (opts.explain) {
|
|
37
|
+
process.stderr.write('\nHints:\n' +
|
|
38
|
+
' - An error path like `elements.2.type` points at the 3rd element\'s `type` field.\n' +
|
|
39
|
+
' - "Invalid discriminator value" means a bad `type` — allowed: video, image, text,\n' +
|
|
40
|
+
' shape, audio, caption, group, particles.\n' +
|
|
41
|
+
' - Read the field rules: clipkit docs protocol\n');
|
|
42
|
+
}
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,+EAA+E;AAC/E,0EAA0E;AAC1E,+EAA+E;AAC/E,yEAAyE;AACzE,WAAW;AAGX,OAAO,EAAE,QAAQ,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAEvE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,gDAAgD,CAAC;SAC7D,MAAM,CAAC,WAAW,EAAE,8DAA8D,CAAC;SACnF,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,IAA2B,EAAE,EAAE;QAC1D,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAEhC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,IAAI,iCAAiC,wBAAwB,cAAc,CACjF,CAAC;YACF,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,IAAc,CAAC,CAAC;gBACnD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;gBAC5F,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,OAAO,QAAQ,CAAC,MAAM,WAAW,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,+BAA+B,CACjG,CAAC;oBACF,KAAK,MAAM,CAAC,IAAI,QAAQ;wBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;gBACnF,CAAC;YACH,CAAC;YACD,OAAO;QACT,CAAC;QAED,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,YAAY;gBACV,uFAAuF;gBACvF,uFAAuF;gBACvF,gDAAgD;gBAChD,oDAAoD,CACvD,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const DEFAULT_API_URL = "https://clipkit.dev";
|
|
2
|
+
export interface Config {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
/** Non-default API host, persisted only when the user overrides it. */
|
|
5
|
+
apiUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function configDir(): string;
|
|
8
|
+
export declare function configPath(): string;
|
|
9
|
+
export declare function readConfig(): Promise<Config>;
|
|
10
|
+
export declare function writeConfig(config: Config): Promise<void>;
|
|
11
|
+
export declare function clearConfig(): Promise<void>;
|
|
12
|
+
export declare function resolveApiKey(flag?: string): Promise<string | undefined>;
|
|
13
|
+
export declare function resolveApiUrl(flag?: string): Promise<string>;
|
|
14
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,eAAe,wBAAwB,CAAC;AAErD,MAAM,WAAW,MAAM;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,SAAS,IAAI,MAAM,CAGlC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CASlD;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/D;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjD;AAID,wBAAsB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAK9E;AAED,wBAAsB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOlE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Persistent CLI config — the API key (and optional API host) used by the
|
|
2
|
+
// cloud commands (`render --cloud`, and team-owned `share`). Stored at
|
|
3
|
+
// $XDG_CONFIG_HOME/clipkit/config.json (falls back to ~/.config/clipkit),
|
|
4
|
+
// chmod 0600 so the key isn't world-readable.
|
|
5
|
+
//
|
|
6
|
+
// Resolution order for both key and host: explicit flag → environment
|
|
7
|
+
// variable → config file → built-in default. The env vars (CLIPKIT_API_KEY /
|
|
8
|
+
// CLIPKIT_API_URL) are the same ones the MCP server reads, so a machine wired
|
|
9
|
+
// up for one is wired up for both.
|
|
10
|
+
import { readFile, writeFile, mkdir, rm, chmod } from 'node:fs/promises';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
export const DEFAULT_API_URL = 'https://clipkit.dev';
|
|
14
|
+
export function configDir() {
|
|
15
|
+
const base = process.env.XDG_CONFIG_HOME?.trim() || join(homedir(), '.config');
|
|
16
|
+
return join(base, 'clipkit');
|
|
17
|
+
}
|
|
18
|
+
export function configPath() {
|
|
19
|
+
return join(configDir(), 'config.json');
|
|
20
|
+
}
|
|
21
|
+
export async function readConfig() {
|
|
22
|
+
try {
|
|
23
|
+
const raw = await readFile(configPath(), 'utf8');
|
|
24
|
+
const parsed = JSON.parse(raw);
|
|
25
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Missing or unreadable file → empty config (the common first-run case).
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function writeConfig(config) {
|
|
33
|
+
await mkdir(configDir(), { recursive: true });
|
|
34
|
+
const path = configPath();
|
|
35
|
+
await writeFile(path, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
36
|
+
// Best-effort lock-down; filesystems that ignore mode bits (Windows) just won't.
|
|
37
|
+
await chmod(path, 0o600).catch(() => { });
|
|
38
|
+
}
|
|
39
|
+
export async function clearConfig() {
|
|
40
|
+
await rm(configPath(), { force: true });
|
|
41
|
+
}
|
|
42
|
+
// ── Resolution: flag → env → config file → default ──────────────────────────
|
|
43
|
+
export async function resolveApiKey(flag) {
|
|
44
|
+
if (flag?.trim())
|
|
45
|
+
return flag.trim();
|
|
46
|
+
const env = process.env.CLIPKIT_API_KEY?.trim();
|
|
47
|
+
if (env)
|
|
48
|
+
return env;
|
|
49
|
+
return (await readConfig()).apiKey;
|
|
50
|
+
}
|
|
51
|
+
export async function resolveApiUrl(flag) {
|
|
52
|
+
const pick = flag?.trim() ||
|
|
53
|
+
process.env.CLIPKIT_API_URL?.trim() ||
|
|
54
|
+
(await readConfig()).apiUrl ||
|
|
55
|
+
DEFAULT_API_URL;
|
|
56
|
+
return pick.replace(/\/+$/, '');
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,uEAAuE;AACvE,0EAA0E;AAC1E,8CAA8C;AAC9C,EAAE;AACF,sEAAsE;AACtE,6EAA6E;AAC7E,8EAA8E;AAC9E,mCAAmC;AAEnC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAQrD,MAAM,UAAU,SAAS;IACvB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;QAC1C,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAE,MAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;QACzE,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc;IAC9C,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IACtE,iFAAiF;IACjF,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,+EAA+E;AAE/E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAa;IAC/C,IAAI,IAAI,EAAE,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAChD,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,OAAO,CAAC,MAAM,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAa;IAC/C,MAAM,IAAI,GACR,IAAI,EAAE,IAAI,EAAE;QACZ,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE;QACnC,CAAC,MAAM,UAAU,EAAE,CAAC,CAAC,MAAM;QAC3B,eAAe,CAAC;IAClB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @clipkit/cli — local-first authoring + validation + rendering surface.
|
|
3
|
+
//
|
|
4
|
+
// Subcommands, each in its own file under ./commands:
|
|
5
|
+
// clipkit init [name] — scaffold a project
|
|
6
|
+
// clipkit new <template> — scaffold a Source from the pattern library
|
|
7
|
+
// clipkit validate <file> — schema-check a Source (--explain for hints)
|
|
8
|
+
// clipkit explain <file> — plain-language read-back + warnings
|
|
9
|
+
// clipkit preview <file> — open a Source in the web editor (share link)
|
|
10
|
+
// clipkit render <file> --out X — render to a video file (local or --cloud)
|
|
11
|
+
// clipkit still <file> --out X — render one frame to a PNG
|
|
12
|
+
// clipkit transcribe <file> — audio/video → caption words (local Whisper)
|
|
13
|
+
// clipkit docs [topic] — print the authoring docs / protocol spec
|
|
14
|
+
// clipkit schema — print the protocol JSON Schema
|
|
15
|
+
// clipkit mcp — run the MCP server (wire up an AI agent)
|
|
16
|
+
// clipkit login / logout — store an API key for the cloud commands
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import { CLIPKIT_PROTOCOL_VERSION } from '@clipkit/protocol';
|
|
19
|
+
import { initCommand } from './commands/init.js';
|
|
20
|
+
import { newCommand } from './commands/new.js';
|
|
21
|
+
import { validateCommand } from './commands/validate.js';
|
|
22
|
+
import { explainCommand } from './commands/explain.js';
|
|
23
|
+
import { previewCommand } from './commands/preview.js';
|
|
24
|
+
import { renderCommand } from './commands/render.js';
|
|
25
|
+
import { stillCommand } from './commands/still.js';
|
|
26
|
+
import { transcribeCommand } from './commands/transcribe.js';
|
|
27
|
+
import { docsCommand } from './commands/docs.js';
|
|
28
|
+
import { schemaCommand } from './commands/schema.js';
|
|
29
|
+
import { mcpCommand } from './commands/mcp.js';
|
|
30
|
+
import { loginCommand } from './commands/login.js';
|
|
31
|
+
const program = new Command();
|
|
32
|
+
program
|
|
33
|
+
.name('clipkit')
|
|
34
|
+
.description(`The Clipkit CLI — author and render Clipkit Protocol videos locally.\n` +
|
|
35
|
+
`Implements Clipkit Protocol v${CLIPKIT_PROTOCOL_VERSION} (CKP/${CLIPKIT_PROTOCOL_VERSION}).`)
|
|
36
|
+
.version('0.0.0', '-v, --version', 'print the CLI version');
|
|
37
|
+
initCommand(program);
|
|
38
|
+
newCommand(program);
|
|
39
|
+
validateCommand(program);
|
|
40
|
+
explainCommand(program);
|
|
41
|
+
previewCommand(program);
|
|
42
|
+
renderCommand(program);
|
|
43
|
+
stillCommand(program);
|
|
44
|
+
transcribeCommand(program);
|
|
45
|
+
docsCommand(program);
|
|
46
|
+
schemaCommand(program);
|
|
47
|
+
mcpCommand(program);
|
|
48
|
+
loginCommand(program);
|
|
49
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
50
|
+
process.stderr.write(`\nclipkit: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,yEAAyE;AACzE,EAAE;AACF,sDAAsD;AACtD,wDAAwD;AACxD,gFAAgF;AAChF,iFAAiF;AACjF,yEAAyE;AACzE,kFAAkF;AAClF,+EAA+E;AAC/E,+DAA+D;AAC/D,iFAAiF;AACjF,8EAA8E;AAC9E,oEAAoE;AACpE,8EAA8E;AAC9E,6EAA6E;AAE7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CACV,wEAAwE;IACtE,gCAAgC,wBAAwB,SAAS,wBAAwB,IAAI,CAChG;KACA,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,uBAAuB,CAAC,CAAC;AAE9D,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,UAAU,CAAC,OAAO,CAAC,CAAC;AACpB,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,cAAc,CAAC,OAAO,CAAC,CAAC;AACxB,cAAc,CAAC,OAAO,CAAC,CAAC;AACxB,aAAa,CAAC,OAAO,CAAC,CAAC;AACvB,YAAY,CAAC,OAAO,CAAC,CAAC;AACtB,iBAAiB,CAAC,OAAO,CAAC,CAAC;AAC3B,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,aAAa,CAAC,OAAO,CAAC,CAAC;AACvB,UAAU,CAAC,OAAO,CAAC,CAAC;AACpB,YAAY,CAAC,OAAO,CAAC,CAAC;AAEtB,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,cAAc,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CACnE,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/lint.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Source } from '@clipkit/protocol';
|
|
2
|
+
export interface LintWarning {
|
|
3
|
+
/** Element id, or '(source)' for composition-level issues. */
|
|
4
|
+
where: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function lintSource(source: Source): LintWarning[];
|
|
8
|
+
//# sourceMappingURL=lint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAWD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAmExD"}
|
package/dist/lint.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Protocol-aware "soft" checks — things that pass schema validation but will
|
|
2
|
+
// surprise you at render time. Shared by `clipkit explain` and
|
|
3
|
+
// `clipkit validate --explain`.
|
|
4
|
+
// The runtime text/caption renderer uses a fixed ASCII coverage-font atlas;
|
|
5
|
+
// any non-ASCII glyph (emoji, accents, smart quotes, CJK) is silently dropped.
|
|
6
|
+
const NON_ASCII = /[^\x00-\x7F]/;
|
|
7
|
+
function firstNonAscii(s) {
|
|
8
|
+
const m = NON_ASCII.exec(s);
|
|
9
|
+
return m ? m[0] : null;
|
|
10
|
+
}
|
|
11
|
+
export function lintSource(source) {
|
|
12
|
+
const warnings = [];
|
|
13
|
+
const comp = source;
|
|
14
|
+
const compDuration = typeof comp.duration === 'number' ? comp.duration : undefined;
|
|
15
|
+
if (compDuration === undefined) {
|
|
16
|
+
warnings.push({
|
|
17
|
+
where: '(source)',
|
|
18
|
+
message: "No top-level `duration` — the runtime can't tell how long the composition is. Set `duration` (seconds).",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const elements = Array.isArray(comp.elements) ? comp.elements : [];
|
|
22
|
+
for (const el of elements) {
|
|
23
|
+
const e = el;
|
|
24
|
+
const id = typeof e.id === 'string' ? e.id : `(${String(e.type ?? 'element')})`;
|
|
25
|
+
const time = typeof e.time === 'number' ? e.time : 0;
|
|
26
|
+
const dur = typeof e.duration === 'number' ? e.duration : undefined;
|
|
27
|
+
// Runs past the composition's end → it'll be cut off.
|
|
28
|
+
if (compDuration !== undefined && dur !== undefined && time + dur > compDuration + 1e-6) {
|
|
29
|
+
warnings.push({
|
|
30
|
+
where: id,
|
|
31
|
+
message: `Ends at ${(time + dur).toFixed(2)}s, past the composition's ${compDuration}s — it'll be cut off.`,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// Non-ASCII in text-bearing fields (dropped by the ASCII atlas).
|
|
35
|
+
if (e.type === 'text') {
|
|
36
|
+
const texts = [];
|
|
37
|
+
if (typeof e.text === 'string')
|
|
38
|
+
texts.push(e.text);
|
|
39
|
+
if (Array.isArray(e.spans)) {
|
|
40
|
+
for (const sp of e.spans) {
|
|
41
|
+
const t = sp?.text;
|
|
42
|
+
if (typeof t === 'string')
|
|
43
|
+
texts.push(t);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (const t of texts) {
|
|
47
|
+
const ch = firstNonAscii(t);
|
|
48
|
+
if (ch) {
|
|
49
|
+
warnings.push({
|
|
50
|
+
where: id,
|
|
51
|
+
message: `Text has a non-ASCII character ("${ch}") — the runtime's ASCII font atlas drops these (emoji, accents, smart quotes, CJK). Use plain ASCII.`,
|
|
52
|
+
});
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (e.type === 'caption' && Array.isArray(e.words)) {
|
|
58
|
+
for (const w of e.words) {
|
|
59
|
+
const t = w?.text;
|
|
60
|
+
if (typeof t === 'string') {
|
|
61
|
+
const ch = firstNonAscii(t);
|
|
62
|
+
if (ch) {
|
|
63
|
+
warnings.push({
|
|
64
|
+
where: id,
|
|
65
|
+
message: `Caption word has a non-ASCII character ("${ch}") — dropped by the runtime's ASCII atlas.`,
|
|
66
|
+
});
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return warnings;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=lint.js.map
|
package/dist/lint.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-source.d.ts","sourceRoot":"","sources":["../src/load-source.ts"],"names":[],"mappings":"AAkBA,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC,CAwBD"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Load a Clipkit Source from a file. Supports .json and .ts/.tsx/.mts/.cts.
|
|
2
|
+
//
|
|
3
|
+
// For TypeScript files, we use jiti — a small runtime TS loader that
|
|
4
|
+
// understands ESM + CJS + TS and is fast enough for one-off CLI runs.
|
|
5
|
+
// The file MUST export the Source as either a default export or a named
|
|
6
|
+
// export called `source`, `video`, or `default`.
|
|
7
|
+
import { readFile } from 'node:fs/promises';
|
|
8
|
+
import { resolve as resolvePath, extname } from 'node:path';
|
|
9
|
+
import { pathToFileURL } from 'node:url';
|
|
10
|
+
import { createJiti } from 'jiti';
|
|
11
|
+
const TS_EXTS = new Set(['.ts', '.tsx', '.mts', '.cts']);
|
|
12
|
+
const JSON_EXTS = new Set(['.json']);
|
|
13
|
+
const JS_EXTS = new Set(['.js', '.mjs', '.cjs']);
|
|
14
|
+
const CANDIDATE_NAMED_EXPORTS = ['source', 'video', 'project', 'composition'];
|
|
15
|
+
export async function loadSource(filePath) {
|
|
16
|
+
const abs = resolvePath(process.cwd(), filePath);
|
|
17
|
+
const ext = extname(abs).toLowerCase();
|
|
18
|
+
if (JSON_EXTS.has(ext)) {
|
|
19
|
+
const raw = await readFile(abs, 'utf8');
|
|
20
|
+
return { path: abs, source: JSON.parse(raw) };
|
|
21
|
+
}
|
|
22
|
+
if (TS_EXTS.has(ext) || JS_EXTS.has(ext)) {
|
|
23
|
+
const mod = await loadModule(abs);
|
|
24
|
+
const source = pickSource(mod);
|
|
25
|
+
if (source === undefined) {
|
|
26
|
+
throw new Error(`${filePath}: no Clipkit Source found. Export it as ` +
|
|
27
|
+
`\`default\` or one of: ${CANDIDATE_NAMED_EXPORTS.join(', ')}.`);
|
|
28
|
+
}
|
|
29
|
+
return { path: abs, source };
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`${filePath}: unsupported extension "${ext}". Expected one of: .json, .ts, .tsx, .mts, .cts, .js, .mjs, .cjs.`);
|
|
32
|
+
}
|
|
33
|
+
async function loadModule(abs) {
|
|
34
|
+
const ext = extname(abs).toLowerCase();
|
|
35
|
+
if (TS_EXTS.has(ext)) {
|
|
36
|
+
// Use jiti for TS — handles .ts in a Node process without a build step.
|
|
37
|
+
const jiti = createJiti(pathToFileURL(abs).href, {
|
|
38
|
+
interopDefault: true,
|
|
39
|
+
cache: false,
|
|
40
|
+
});
|
|
41
|
+
const mod = (await jiti.import(abs, {}));
|
|
42
|
+
return mod;
|
|
43
|
+
}
|
|
44
|
+
// Plain JS — native dynamic import.
|
|
45
|
+
return (await import(pathToFileURL(abs).href));
|
|
46
|
+
}
|
|
47
|
+
function pickSource(mod) {
|
|
48
|
+
// Prefer a named export from our candidate list.
|
|
49
|
+
for (const name of CANDIDATE_NAMED_EXPORTS) {
|
|
50
|
+
if (mod[name] !== undefined)
|
|
51
|
+
return mod[name];
|
|
52
|
+
}
|
|
53
|
+
// Fall back to `default`. With interopDefault: true this is unwrapped
|
|
54
|
+
// if the file used `export default`.
|
|
55
|
+
if (mod.default !== undefined)
|
|
56
|
+
return mod.default;
|
|
57
|
+
// Single named export — accept it.
|
|
58
|
+
const keys = Object.keys(mod).filter((k) => k !== 'default');
|
|
59
|
+
if (keys.length === 1)
|
|
60
|
+
return mod[keys[0]];
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=load-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-source.js","sourceRoot":"","sources":["../src/load-source.ts"],"names":[],"mappings":"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"}
|