@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,88 @@
|
|
|
1
|
+
// `clipkit login` stores an API key for the cloud commands; `clipkit logout`
|
|
2
|
+
// removes it. Auth reuses the dashboard `ck_live_…` / `ck_test_…` keys the API
|
|
3
|
+
// already accepts (see verify-key.ts) — there's no separate CLI token system.
|
|
4
|
+
//
|
|
5
|
+
// Flow: open <host>/keys in a browser, paste the key, store it (chmod 0600)
|
|
6
|
+
// after a cheap liveness check. The check needs no dedicated endpoint: an
|
|
7
|
+
// authed GET of a non-existent render returns 401 for a bad/unknown key and
|
|
8
|
+
// 404 for a good one, so 401 ⇒ reject, anything else ⇒ accept.
|
|
9
|
+
import { createInterface } from 'node:readline/promises';
|
|
10
|
+
import { stdin, stdout } from 'node:process';
|
|
11
|
+
import { readConfig, writeConfig, clearConfig, configPath, resolveApiUrl, } from '../config.js';
|
|
12
|
+
import { openUrl } from '../util.js';
|
|
13
|
+
const KEY_RE = /^ck_(?:live|test)_[A-Za-z0-9_-]+$/;
|
|
14
|
+
const ZERO_UUID = '00000000-0000-0000-0000-000000000000';
|
|
15
|
+
// 'ok' | 'bad' | 'unknown' — 'unknown' means we couldn't reach the host, so we
|
|
16
|
+
// store the key anyway rather than block login on a flaky network.
|
|
17
|
+
async function checkKey(apiUrl, key) {
|
|
18
|
+
try {
|
|
19
|
+
const res = await fetch(`${apiUrl}/api/v1/renders/${ZERO_UUID}`, {
|
|
20
|
+
headers: { authorization: `Bearer ${key}` },
|
|
21
|
+
});
|
|
22
|
+
return res.status === 401 ? 'bad' : 'ok';
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return 'unknown';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function loginCommand(program) {
|
|
29
|
+
program
|
|
30
|
+
.command('login')
|
|
31
|
+
.description('Store an API key for cloud commands (render --cloud, team previews)')
|
|
32
|
+
.option('--api-key <key>', 'set the key non-interactively (CI-friendly)')
|
|
33
|
+
.option('--api-url <url>', 'override the API host (e.g. http://localhost:3000)')
|
|
34
|
+
.action(async (opts) => {
|
|
35
|
+
const apiUrl = await resolveApiUrl(opts.apiUrl);
|
|
36
|
+
let key = opts.apiKey?.trim();
|
|
37
|
+
if (!key) {
|
|
38
|
+
const keysUrl = `${apiUrl}/keys`;
|
|
39
|
+
process.stdout.write(`Create an API key here (opening your browser):\n ${keysUrl}\n\n`);
|
|
40
|
+
openUrl(keysUrl);
|
|
41
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
42
|
+
try {
|
|
43
|
+
key = (await rl.question('Paste your key (ck_live_… or ck_test_…): ')).trim();
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
rl.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!key || !KEY_RE.test(key)) {
|
|
50
|
+
process.stderr.write('✗ That doesn’t look like a Clipkit API key (expected ck_live_… or ck_test_…).\n');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const status = await checkKey(apiUrl, key);
|
|
54
|
+
if (status === 'bad') {
|
|
55
|
+
process.stderr.write('✗ That key was rejected by the API. Check you copied it whole and it isn’t revoked.\n');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
const next = await readConfig();
|
|
59
|
+
next.apiKey = key;
|
|
60
|
+
// Persist a non-default host so the file stays portable across machines.
|
|
61
|
+
if (opts.apiUrl)
|
|
62
|
+
next.apiUrl = apiUrl;
|
|
63
|
+
await writeConfig(next);
|
|
64
|
+
const masked = `${key.slice(0, 11)}…${key.slice(-4)}`;
|
|
65
|
+
process.stdout.write(`✓ Logged in${status === 'unknown' ? ' (couldn’t reach the API to verify — key stored anyway)' : ''}.\n` +
|
|
66
|
+
` key: ${masked}\n` +
|
|
67
|
+
` host: ${apiUrl}\n` +
|
|
68
|
+
` file: ${configPath()}\n\n` +
|
|
69
|
+
`Try it: clipkit render <file> --cloud\n`);
|
|
70
|
+
});
|
|
71
|
+
program
|
|
72
|
+
.command('logout')
|
|
73
|
+
.description('Remove the stored API key')
|
|
74
|
+
.action(async () => {
|
|
75
|
+
const cfg = await readConfig();
|
|
76
|
+
if (!cfg.apiKey) {
|
|
77
|
+
process.stdout.write('Already logged out.\n');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
delete cfg.apiKey;
|
|
81
|
+
if (Object.keys(cfg).length === 0)
|
|
82
|
+
await clearConfig();
|
|
83
|
+
else
|
|
84
|
+
await writeConfig(cfg);
|
|
85
|
+
process.stdout.write('✓ Logged out — removed the stored key.\n');
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAejD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// `clipkit mcp` — run the Clipkit MCP server over stdio, so any agent (Claude
|
|
2
|
+
// Desktop, Cursor, Cline, …) gets the full Clipkit toolset from one command:
|
|
3
|
+
//
|
|
4
|
+
// { "command": "npx", "args": ["-y", "@clipkit/cli", "mcp"] }
|
|
5
|
+
//
|
|
6
|
+
// The server speaks JSON-RPC on stdin/stdout, so this command must keep stdout
|
|
7
|
+
// clean — we import the package's `/stdio` entry, whose module self-starts: it
|
|
8
|
+
// connects a StdioServerTransport and takes over the stream. (The bare
|
|
9
|
+
// `@clipkit/mcp-server` entry is the library build and does NOT self-start.)
|
|
10
|
+
// Diagnostics (incl. the server's "ready" line) go to stderr.
|
|
11
|
+
export function mcpCommand(program) {
|
|
12
|
+
program
|
|
13
|
+
.command('mcp')
|
|
14
|
+
.description('Run the Clipkit MCP server over stdio (wire up an AI agent)')
|
|
15
|
+
.action(async () => {
|
|
16
|
+
try {
|
|
17
|
+
await import('@clipkit/mcp-server/stdio');
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
process.stderr.write(`✗ Could not start the MCP server: ${e instanceof Error ? e.message : String(e)}\n` +
|
|
21
|
+
' Install it alongside the CLI: npm i -g @clipkit/mcp-server\n');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,EAAE;AACF,gEAAgE;AAChE,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,uEAAuE;AACvE,6EAA6E;AAC7E,8DAA8D;AAI9D,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,6DAA6D,CAAC;SAC1E,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qCAAqC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;gBACjF,iEAAiE,CACpE,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmDpC,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6CjD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// `clipkit new <template>` — scaffold a known-good, render-tested Source from
|
|
2
|
+
// the @clipkit/patterns library. An idiomatic starting point beats authoring
|
|
3
|
+
// from a blank file: fewer schema errors, better-looking output. Writes JSON to
|
|
4
|
+
// stdout (pipeable) or to --out. This is the CLI twin of the MCP create_promo
|
|
5
|
+
// composer.
|
|
6
|
+
import { writeFile } from 'node:fs/promises';
|
|
7
|
+
import { promo, heroReveal, kineticHeadline, introCard, ctaOutro, } from '@clipkit/patterns';
|
|
8
|
+
const COLOR = 'green';
|
|
9
|
+
const THEMES = ['cinematic', 'mux', 'minimal'];
|
|
10
|
+
function base(ctx) {
|
|
11
|
+
return {
|
|
12
|
+
id: ctx.id,
|
|
13
|
+
theme: ctx.theme,
|
|
14
|
+
time: ctx.time,
|
|
15
|
+
duration: ctx.duration,
|
|
16
|
+
layer: ctx.layer,
|
|
17
|
+
color: COLOR,
|
|
18
|
+
canvasWidth: ctx.canvasWidth,
|
|
19
|
+
canvasHeight: ctx.canvasHeight,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function heroScene(wordmark, tagline) {
|
|
23
|
+
return { duration: 2.6, build: (ctx) => heroReveal({ ...base(ctx), wordmark, tagline }) };
|
|
24
|
+
}
|
|
25
|
+
function kineticScene(text, subtitle) {
|
|
26
|
+
return { duration: 2.2, build: (ctx) => kineticHeadline({ ...base(ctx), text, subtitle }) };
|
|
27
|
+
}
|
|
28
|
+
function titleScene(headline, kicker, subtitle) {
|
|
29
|
+
return { duration: 2.4, build: (ctx) => introCard({ ...base(ctx), headline, kicker, subtitle }) };
|
|
30
|
+
}
|
|
31
|
+
function ctaScene(wordmark, tagline, cta) {
|
|
32
|
+
return { duration: 2.0, build: (ctx) => ctaOutro({ ...base(ctx), wordmark, tagline, cta }) };
|
|
33
|
+
}
|
|
34
|
+
const TEMPLATES = {
|
|
35
|
+
promo: () => [heroScene('ACME', 'Your tagline here'), ctaScene('ACME', 'Your tagline here', 'Get started')],
|
|
36
|
+
hero: () => [heroScene('ACME', 'Your tagline here')],
|
|
37
|
+
kinetic: () => [kineticScene('Make it move', 'with Clipkit')],
|
|
38
|
+
title: () => [titleScene('Your headline', 'KICKER', 'A supporting subtitle')],
|
|
39
|
+
cta: () => [ctaScene('ACME', 'Your tagline here', 'Get started')],
|
|
40
|
+
};
|
|
41
|
+
export function newCommand(program) {
|
|
42
|
+
program
|
|
43
|
+
.command('new <template>')
|
|
44
|
+
.description(`Scaffold a Source from the pattern library (${Object.keys(TEMPLATES).join(' | ')})`)
|
|
45
|
+
.option('-o, --out <path>', 'write to a file (default: print to stdout)')
|
|
46
|
+
.option('--theme <theme>', `${THEMES.join(' | ')}`, 'cinematic')
|
|
47
|
+
.option('--width <px>', 'composition width', '1280')
|
|
48
|
+
.option('--height <px>', 'composition height', '720')
|
|
49
|
+
.action(async (template, opts) => {
|
|
50
|
+
const make = TEMPLATES[template];
|
|
51
|
+
if (!make) {
|
|
52
|
+
process.stderr.write(`✗ unknown template "${template}". Try one of: ${Object.keys(TEMPLATES).join(', ')}.\n`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
if (!THEMES.includes(opts.theme)) {
|
|
56
|
+
process.stderr.write(`✗ invalid --theme "${opts.theme}" (expected ${THEMES.join(' | ')}).\n`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const width = Number(opts.width);
|
|
60
|
+
const height = Number(opts.height);
|
|
61
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
62
|
+
process.stderr.write('✗ --width / --height must be positive numbers.\n');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
const source = promo({ theme: opts.theme, scenes: make(), width, height });
|
|
66
|
+
const json = JSON.stringify(source, null, 2);
|
|
67
|
+
if (opts.out) {
|
|
68
|
+
await writeFile(opts.out, `${json}\n`);
|
|
69
|
+
process.stderr.write(`✓ Wrote ${template} template → ${opts.out} (${source.elements.length} elements, ${source.duration}s).\n` +
|
|
70
|
+
` Edit the placeholder text, then preview it: clipkit preview ${opts.out}\n`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
process.stdout.write(`${json}\n`);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/commands/preview.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiErD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// `clipkit preview <file>` — open a Source in the Clipkit web editor.
|
|
2
|
+
//
|
|
3
|
+
// Validates the Source, POSTs it to /api/projects, and opens the returned
|
|
4
|
+
// /editor?id=… link in your browser (it's a real, shareable link — copy it,
|
|
5
|
+
// send it, embed it). Keyless: with no API key the link is anonymous and
|
|
6
|
+
// expires in 7 days; with one (clipkit login / CLIPKIT_API_KEY) it's owned by
|
|
7
|
+
// your team and permanent on paid plans. This is the CLI twin of the MCP
|
|
8
|
+
// `share_video` tool — a live, editable preview with zero local setup (no
|
|
9
|
+
// Chrome, no render, no credits — the editor renders it in the browser).
|
|
10
|
+
import { validate } from '@clipkit/protocol';
|
|
11
|
+
import { loadSource } from '../load-source.js';
|
|
12
|
+
import { resolveApiKey, resolveApiUrl } from '../config.js';
|
|
13
|
+
import { openUrl, printValidationErrors } from '../util.js';
|
|
14
|
+
export function previewCommand(program) {
|
|
15
|
+
program
|
|
16
|
+
.command('preview <file>')
|
|
17
|
+
.description('Open a Source in the web editor (creates a shareable link)')
|
|
18
|
+
.option('--no-open', "just print the link, don't open a browser")
|
|
19
|
+
.option('--api-key <key>', 'API key (defaults to login / CLIPKIT_API_KEY)')
|
|
20
|
+
.option('--api-url <url>', 'override the API host')
|
|
21
|
+
.action(async (file, opts) => {
|
|
22
|
+
const { path, source } = await loadSource(file);
|
|
23
|
+
const result = validate(source);
|
|
24
|
+
if (!result.valid) {
|
|
25
|
+
printValidationErrors(path, result.errors);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const apiUrl = await resolveApiUrl(opts.apiUrl);
|
|
29
|
+
const apiKey = await resolveApiKey(opts.apiKey);
|
|
30
|
+
const headers = { 'content-type': 'application/json' };
|
|
31
|
+
if (apiKey)
|
|
32
|
+
headers.authorization = `Bearer ${apiKey}`;
|
|
33
|
+
let res;
|
|
34
|
+
try {
|
|
35
|
+
res = await fetch(`${apiUrl}/api/projects`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers,
|
|
38
|
+
body: JSON.stringify({ source: result.data }),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
process.stderr.write(`✗ Could not reach ${apiUrl}: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
if (res.status === 429) {
|
|
46
|
+
process.stderr.write('✗ Rate limited — wait a minute and try again (logging in raises the limit).\n');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
if (res.status === 413) {
|
|
50
|
+
process.stderr.write('✗ Source too large (2 MB max).\n');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const body = await res.text().catch(() => '');
|
|
55
|
+
process.stderr.write(`✗ Preview failed (${res.status}). ${body.slice(0, 300)}\n`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
const data = (await res.json());
|
|
59
|
+
if (!data.url) {
|
|
60
|
+
process.stderr.write('✗ Share API returned no URL.\n');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const scope = apiKey ? 'team-owned' : 'anonymous, expires in 7 days';
|
|
64
|
+
process.stdout.write(`✓ Preview ready (${scope}):\n\n${data.url}\n`);
|
|
65
|
+
if (opts.open)
|
|
66
|
+
openUrl(data.url);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/commands/preview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/commands/render.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4CpC,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuCpD"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// `clipkit render <file> -o out.mp4` — render a Source to a video file.
|
|
2
|
+
//
|
|
3
|
+
// Two engines:
|
|
4
|
+
// • local (default): headless Chrome via @clipkit/renderer — free, runs
|
|
5
|
+
// on your machine, needs Google Chrome installed. The package is an optional
|
|
6
|
+
// peer; if it (or Playwright) isn't present we say so and point at --cloud.
|
|
7
|
+
// • --cloud: the hosted GPU path. POSTs to /api/v1/renders, polls the job to
|
|
8
|
+
// completion, downloads the signed output. Needs an API key (clipkit login)
|
|
9
|
+
// and consumes render credits.
|
|
10
|
+
import { writeFile } from 'node:fs/promises';
|
|
11
|
+
import { validate } from '@clipkit/protocol';
|
|
12
|
+
import { loadSource } from '../load-source.js';
|
|
13
|
+
import { resolveApiKey, resolveApiUrl } from '../config.js';
|
|
14
|
+
import { printValidationErrors, fmtBytes, sleep } from '../util.js';
|
|
15
|
+
const RESOLUTIONS = ['source', '720p', '1080p', '1440p', '4k'];
|
|
16
|
+
const POLL_INTERVAL_MS = 1500;
|
|
17
|
+
const POLL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
18
|
+
export function renderCommand(program) {
|
|
19
|
+
program
|
|
20
|
+
.command('render <file>')
|
|
21
|
+
.description('Render a Source to a video file (local Chrome by default; --cloud for hosted)')
|
|
22
|
+
.option('-o, --out <path>', 'output file path', 'output.mp4')
|
|
23
|
+
.option('--cloud', 'render on Clipkit’s servers (needs login; uses credits)', false)
|
|
24
|
+
.option('--local', 'render locally with headless Chrome (the default)', false)
|
|
25
|
+
.option('-r, --resolution <res>', 'source | 720p | 1080p | 1440p | 4k', 'source')
|
|
26
|
+
.option('-f, --format <fmt>', 'output format (mp4 default; pro formats are cloud-only)')
|
|
27
|
+
.option('--bitrate <bps>', 'override video bitrate in bits/second')
|
|
28
|
+
.option('-b, --backend <backend>', 'local engine backend: auto | webgpu | webgl2', 'auto')
|
|
29
|
+
.option('--api-key <key>', 'API key for --cloud (defaults to login / CLIPKIT_API_KEY)')
|
|
30
|
+
.option('--api-url <url>', 'override the API host')
|
|
31
|
+
.action(async (file, opts) => {
|
|
32
|
+
const { path, source } = await loadSource(file);
|
|
33
|
+
const result = validate(source);
|
|
34
|
+
if (!result.valid) {
|
|
35
|
+
printValidationErrors(path, result.errors);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
if (!RESOLUTIONS.includes(opts.resolution)) {
|
|
39
|
+
process.stderr.write(`✗ invalid --resolution "${opts.resolution}" (expected ${RESOLUTIONS.join(' | ')}).\n`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
let bitrate;
|
|
43
|
+
if (opts.bitrate) {
|
|
44
|
+
bitrate = Number(opts.bitrate);
|
|
45
|
+
if (!Number.isFinite(bitrate) || bitrate <= 0) {
|
|
46
|
+
process.stderr.write(`✗ invalid --bitrate "${opts.bitrate}".\n`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const validated = result.data;
|
|
51
|
+
if (opts.cloud)
|
|
52
|
+
await renderCloud(validated, opts, bitrate);
|
|
53
|
+
else
|
|
54
|
+
await renderLocal(validated, opts, bitrate);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function renderLocal(source, opts, bitrate) {
|
|
58
|
+
if (!isBackend(opts.backend)) {
|
|
59
|
+
process.stderr.write(`✗ invalid --backend "${opts.backend}" (expected auto | webgpu | webgl2).\n`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
if (opts.format && opts.format !== 'mp4') {
|
|
63
|
+
process.stderr.write(`✗ "${opts.format}" is a cloud-only format — local rendering outputs mp4.\n` +
|
|
64
|
+
` Render it on the cloud: clipkit render <file> --cloud --format ${opts.format}\n`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
let mod;
|
|
68
|
+
try {
|
|
69
|
+
mod = (await import('@clipkit/renderer'));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
process.stderr.write('Local rendering needs the @clipkit/renderer engine (and Google Chrome).\n\n' +
|
|
73
|
+
' npm i -g @clipkit/renderer playwright\n\n' +
|
|
74
|
+
'Then re-run, or render in the cloud instead:\n' +
|
|
75
|
+
' clipkit render <file> --cloud\n');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
process.stderr.write('Rendering locally (headless Chrome)…\n');
|
|
79
|
+
let res;
|
|
80
|
+
try {
|
|
81
|
+
res = await mod.render({
|
|
82
|
+
source,
|
|
83
|
+
backend: opts.backend,
|
|
84
|
+
resolution: opts.resolution,
|
|
85
|
+
...(bitrate ? { bitrate } : {}),
|
|
86
|
+
onProgress: (frame, total) => drawProgress(frame, total),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
clearProgress();
|
|
91
|
+
process.stderr.write(`✗ Local render failed: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
92
|
+
process.stderr.write(' (Local rendering uses your installed Google Chrome. Install it, or use --cloud.)\n');
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
clearProgress();
|
|
96
|
+
await writeFile(opts.out, res.buffer);
|
|
97
|
+
process.stdout.write(`✓ Rendered ${res.width}×${res.height}, ${res.durationSec.toFixed(1)}s → ${opts.out} (${fmtBytes(res.buffer.length)})\n`);
|
|
98
|
+
}
|
|
99
|
+
async function renderCloud(source, opts, bitrate) {
|
|
100
|
+
const apiUrl = await resolveApiUrl(opts.apiUrl);
|
|
101
|
+
const apiKey = await resolveApiKey(opts.apiKey);
|
|
102
|
+
if (!apiKey) {
|
|
103
|
+
process.stderr.write('Cloud rendering needs an API key.\n\n clipkit login\n\n' +
|
|
104
|
+
'or set CLIPKIT_API_KEY / pass --api-key. (Local rendering is free — just drop --cloud.)\n');
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
const body = { source };
|
|
108
|
+
if (opts.resolution && opts.resolution !== 'source')
|
|
109
|
+
body.resolution = opts.resolution;
|
|
110
|
+
if (opts.format)
|
|
111
|
+
body.format = opts.format;
|
|
112
|
+
if (bitrate)
|
|
113
|
+
body.bitrate = bitrate;
|
|
114
|
+
// ── Submit (enqueue). The route returns immediately with a job id.
|
|
115
|
+
let submit;
|
|
116
|
+
try {
|
|
117
|
+
submit = await fetch(`${apiUrl}/api/v1/renders`, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },
|
|
120
|
+
body: JSON.stringify(body),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
process.stderr.write(`✗ Could not reach ${apiUrl}: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
if (submit.status === 401) {
|
|
128
|
+
process.stderr.write('✗ Unauthorized — your key was rejected. Run `clipkit login` again.\n');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
if (submit.status === 402) {
|
|
132
|
+
const data = (await submit.json().catch(() => ({})));
|
|
133
|
+
process.stderr.write(`✗ ${data.message ?? 'Out of render credits.'}\n`);
|
|
134
|
+
if (data.upgrade_url)
|
|
135
|
+
process.stderr.write(` Upgrade: ${apiUrl}${data.upgrade_url}\n`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
if (submit.status === 413) {
|
|
139
|
+
process.stderr.write('✗ Source too large (2 MB max).\n');
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
if (!submit.ok) {
|
|
143
|
+
const t = await submit.text().catch(() => '');
|
|
144
|
+
process.stderr.write(`✗ Render submission failed (${submit.status}). ${t.slice(0, 300)}\n`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
const queued = (await submit.json());
|
|
148
|
+
process.stderr.write(`Queued render ${queued.id}${queued.credits_reserved ? ` (${queued.credits_reserved} credits reserved)` : ''}. Waiting…\n`);
|
|
149
|
+
// ── Poll until done/failed.
|
|
150
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
151
|
+
let outputUrl = null;
|
|
152
|
+
for (;;) {
|
|
153
|
+
if (Date.now() > deadline) {
|
|
154
|
+
clearProgress();
|
|
155
|
+
process.stderr.write('\n✗ Timed out waiting for the render (10 min). Check the dashboard.\n');
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
await sleep(POLL_INTERVAL_MS);
|
|
159
|
+
let poll;
|
|
160
|
+
try {
|
|
161
|
+
poll = await fetch(`${apiUrl}/api/v1/renders/${queued.id}`, {
|
|
162
|
+
headers: { authorization: `Bearer ${apiKey}` },
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
continue; // transient — keep trying until the deadline
|
|
167
|
+
}
|
|
168
|
+
if (!poll.ok)
|
|
169
|
+
continue;
|
|
170
|
+
const s = (await poll.json());
|
|
171
|
+
if (s.status === 'done') {
|
|
172
|
+
clearProgress();
|
|
173
|
+
outputUrl = s.output_url ?? null;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
if (s.status === 'failed') {
|
|
177
|
+
clearProgress();
|
|
178
|
+
process.stderr.write(`\n✗ Render failed: ${s.error ?? 'unknown error'}\n`);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
drawProgress(Math.round((s.progress ?? 0) * 100), 100);
|
|
182
|
+
}
|
|
183
|
+
if (!outputUrl) {
|
|
184
|
+
process.stderr.write('✗ Render finished but returned no download URL.\n');
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
// ── Download the signed output.
|
|
188
|
+
process.stderr.write('Downloading…\n');
|
|
189
|
+
const dl = await fetch(outputUrl);
|
|
190
|
+
if (!dl.ok) {
|
|
191
|
+
process.stderr.write(`✗ Download failed (${dl.status}).\n`);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
const buf = Buffer.from(await dl.arrayBuffer());
|
|
195
|
+
await writeFile(opts.out, buf);
|
|
196
|
+
process.stdout.write(`✓ Rendered in the cloud → ${opts.out} (${fmtBytes(buf.length)})\n`);
|
|
197
|
+
}
|
|
198
|
+
function isBackend(v) {
|
|
199
|
+
return v === 'auto' || v === 'webgpu' || v === 'webgl2';
|
|
200
|
+
}
|
|
201
|
+
function drawProgress(frame, total) {
|
|
202
|
+
if (!process.stderr.isTTY)
|
|
203
|
+
return;
|
|
204
|
+
const pct = total > 0 ? Math.min(100, Math.round((frame / total) * 100)) : 0;
|
|
205
|
+
const width = 24;
|
|
206
|
+
const filled = Math.round((pct / 100) * width);
|
|
207
|
+
process.stderr.write(`\r [${'█'.repeat(filled)}${'░'.repeat(width - filled)}] ${pct}%${total === 100 ? '' : ` (${frame}/${total})`}`);
|
|
208
|
+
}
|
|
209
|
+
function clearProgress() {
|
|
210
|
+
if (process.stderr.isTTY)
|
|
211
|
+
process.stderr.write(`\r${' '.repeat(60)}\r`);
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/commands/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/commands/schema.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAWpD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// `clipkit schema` — emit the Clipkit Protocol as a JSON Schema, derived from
|
|
2
|
+
// the canonical Zod schema (the single source of truth). Useful for constrained
|
|
3
|
+
// / structured LLM generation, editor autocomplete, and external validators:
|
|
4
|
+
//
|
|
5
|
+
// clipkit schema > clipkit.schema.json
|
|
6
|
+
import { sourceSchema } from '@clipkit/protocol';
|
|
7
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
8
|
+
export function schemaCommand(program) {
|
|
9
|
+
program
|
|
10
|
+
.command('schema')
|
|
11
|
+
.description('Print the protocol JSON Schema (generated from the Zod source of truth)')
|
|
12
|
+
.action(() => {
|
|
13
|
+
const json = zodToJsonSchema(sourceSchema, {
|
|
14
|
+
name: 'ClipkitSource',
|
|
15
|
+
$refStrategy: 'root',
|
|
16
|
+
});
|
|
17
|
+
process.stdout.write(`${JSON.stringify(json, null, 2)}\n`);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/commands/schema.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,gFAAgF;AAChF,6EAA6E;AAC7E,EAAE;AACF,yCAAyC;AAGzC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrD,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,yEAAyE,CAAC;SACtF,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,IAAI,GAAG,eAAe,CAAC,YAA0B,EAAE;YACvD,IAAI,EAAE,eAAe;YACrB,YAAY,EAAE,MAAM;SACrB,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"still.d.ts","sourceRoot":"","sources":["../../src/commands/still.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoBpC,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkDnD"}
|