@vivipilot/cli 0.1.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.
Files changed (58) hide show
  1. package/README.md +57 -0
  2. package/dist/api.d.ts +86 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +77 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/args.d.ts +11 -0
  7. package/dist/args.d.ts.map +1 -0
  8. package/dist/args.js +53 -0
  9. package/dist/args.js.map +1 -0
  10. package/dist/browser.d.ts +31 -0
  11. package/dist/browser.d.ts.map +1 -0
  12. package/dist/browser.js +162 -0
  13. package/dist/browser.js.map +1 -0
  14. package/dist/cli.d.ts +12 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +536 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/config.d.ts +15 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +58 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/errors.d.ts +6 -0
  23. package/dist/errors.d.ts.map +1 -0
  24. package/dist/errors.js +12 -0
  25. package/dist/errors.js.map +1 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +7 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/manifest.d.ts +40 -0
  31. package/dist/manifest.d.ts.map +1 -0
  32. package/dist/manifest.js +90 -0
  33. package/dist/manifest.js.map +1 -0
  34. package/dist/mcp.d.ts +13 -0
  35. package/dist/mcp.d.ts.map +1 -0
  36. package/dist/mcp.js +392 -0
  37. package/dist/mcp.js.map +1 -0
  38. package/dist/render.d.ts +21 -0
  39. package/dist/render.d.ts.map +1 -0
  40. package/dist/render.js +369 -0
  41. package/dist/render.js.map +1 -0
  42. package/package.json +42 -0
  43. package/src/api.ts +163 -0
  44. package/src/args.test.ts +21 -0
  45. package/src/args.ts +64 -0
  46. package/src/browser.test.ts +103 -0
  47. package/src/browser.ts +174 -0
  48. package/src/cli.ts +656 -0
  49. package/src/config.test.ts +30 -0
  50. package/src/config.ts +71 -0
  51. package/src/errors.ts +14 -0
  52. package/src/index.ts +25 -0
  53. package/src/manifest.test.ts +105 -0
  54. package/src/manifest.ts +126 -0
  55. package/src/mcp.test.ts +48 -0
  56. package/src/mcp.ts +438 -0
  57. package/src/render.ts +424 -0
  58. package/tsconfig.json +26 -0
@@ -0,0 +1,21 @@
1
+ import type { CliConfig, Env } from "./config.js";
2
+ export type RenderOptions = {
3
+ manifestPath: string;
4
+ outPath: string;
5
+ format?: "mp4" | "webm" | "gif" | "mov";
6
+ width?: number;
7
+ height?: number;
8
+ fps?: number;
9
+ scale?: number;
10
+ transparent?: boolean;
11
+ verifyOnly?: boolean;
12
+ };
13
+ type RenderResult = {
14
+ ok: boolean;
15
+ outPath: string;
16
+ size: number;
17
+ manifestId: string;
18
+ };
19
+ export declare function renderManifest(options: RenderOptions, config: CliConfig, env?: Env): Promise<RenderResult>;
20
+ export {};
21
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAKlD,MAAM,MAAM,aAAa,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,KAAK,YAAY,GAAG;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACnB,CAAC;AA+GF,wBAAsB,cAAc,CACnC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,SAAS,EACjB,GAAG,GAAE,GAAiB,GACpB,OAAO,CAAC,YAAY,CAAC,CAmPvB"}
package/dist/render.js ADDED
@@ -0,0 +1,369 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createServer } from "node:http";
3
+ import { readFile, writeFile, access, stat, mkdir } from "node:fs/promises";
4
+ import { constants, openSync, writeSync, closeSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { createHash } from "node:crypto";
7
+ import { CliError } from "./errors.js";
8
+ import { resolveHeadlessBrowser, headlessBrowserArgs, BrowserNotFoundError } from "./browser.js";
9
+ import { verifyManifestFile, checkManifestRevocationOnline, assertNotRevoked, checkRenderEntitlement } from "./manifest.js";
10
+ const DEFAULT_RENDER_URL = "http://localhost:4001/headless-render";
11
+ const DEFAULT_TIMEOUT_MS = 300_000;
12
+ function startCallbackServer(outPath) {
13
+ return new Promise((resolve, reject) => {
14
+ let renderedBlob = null;
15
+ let renderError = null;
16
+ const server = createServer(async (req, res) => {
17
+ res.setHeader("Access-Control-Allow-Origin", "*");
18
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
19
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
20
+ if (req.method === "OPTIONS") {
21
+ res.writeHead(204);
22
+ res.end();
23
+ return;
24
+ }
25
+ const url = new URL(req.url ?? "/", `http://localhost`);
26
+ if (url.pathname === "/manifest.json" && req.method === "GET") {
27
+ try {
28
+ const manifestJson = await readFile(outPath.replace(/\.mp4$|\.webm$|\.gif$|\.mov$/, ".vivi.json"), "utf8");
29
+ res.writeHead(200, { "content-type": "application/json" });
30
+ res.end(manifestJson);
31
+ }
32
+ catch {
33
+ res.writeHead(404);
34
+ res.end("Manifest not found");
35
+ }
36
+ return;
37
+ }
38
+ if (url.pathname === "/result" && req.method === "POST") {
39
+ const chunks = [];
40
+ req.on("data", (chunk) => chunks.push(chunk));
41
+ req.on("end", async () => {
42
+ renderedBlob = Buffer.concat(chunks);
43
+ try {
44
+ await writeFile(outPath, renderedBlob);
45
+ res.writeHead(200, { "content-type": "application/json" });
46
+ res.end(JSON.stringify({ ok: true, size: renderedBlob.length }));
47
+ }
48
+ catch (err) {
49
+ res.writeHead(500, { "content-type": "application/json" });
50
+ res.end(JSON.stringify({ ok: false, error: err instanceof Error ? err.message : String(err) }));
51
+ }
52
+ });
53
+ return;
54
+ }
55
+ if (url.pathname === "/error" && req.method === "POST") {
56
+ const chunks = [];
57
+ req.on("data", (chunk) => chunks.push(chunk));
58
+ req.on("end", () => {
59
+ try {
60
+ const body = JSON.parse(Buffer.concat(chunks).toString());
61
+ renderError = body.message ?? "Unknown render error";
62
+ }
63
+ catch {
64
+ renderError = Buffer.concat(chunks).toString() || "Unknown render error";
65
+ }
66
+ res.writeHead(200, { "content-type": "application/json" });
67
+ res.end(JSON.stringify({ ok: true }));
68
+ });
69
+ return;
70
+ }
71
+ if (url.pathname === "/status" && req.method === "GET") {
72
+ res.writeHead(200, { "content-type": "application/json" });
73
+ res.end(JSON.stringify({
74
+ done: renderedBlob !== null || renderError !== null,
75
+ error: renderError,
76
+ hasBlob: renderedBlob !== null,
77
+ blobSize: renderedBlob?.length ?? 0,
78
+ }));
79
+ return;
80
+ }
81
+ res.writeHead(404);
82
+ res.end("Not found");
83
+ });
84
+ server.on("error", reject);
85
+ server.listen(0, "127.0.0.1", () => {
86
+ const addr = server.address();
87
+ if (!addr || typeof addr === "string") {
88
+ reject(new Error("Failed to bind callback server"));
89
+ return;
90
+ }
91
+ resolve({ server, port: addr.port });
92
+ });
93
+ });
94
+ }
95
+ async function waitForResult(port, timeoutMs) {
96
+ const deadline = Date.now() + timeoutMs;
97
+ while (Date.now() < deadline) {
98
+ try {
99
+ const response = await fetch(`http://127.0.0.1:${port}/status`);
100
+ const status = await response.json();
101
+ if (status.done) {
102
+ if (status.error)
103
+ return { size: 0, error: status.error };
104
+ return { size: status.blobSize, error: null };
105
+ }
106
+ }
107
+ catch { /* keep polling */ }
108
+ await new Promise(r => setTimeout(r, 1000));
109
+ }
110
+ return { size: 0, error: `Render timed out after ${timeoutMs / 1000}s` };
111
+ }
112
+ export async function renderManifest(options, config, env = process.env) {
113
+ const { manifestPath, outPath } = options;
114
+ // 1. Verify manifest signature
115
+ const verification = await verifyManifestFile(manifestPath, config, env);
116
+ if (!verification.ok) {
117
+ throw new CliError(`Manifest verification failed: ${verification.message}`, 1);
118
+ }
119
+ // 2. Online revocation check
120
+ const revocation = await checkManifestRevocationOnline(verification.manifest, config, env);
121
+ assertNotRevoked(revocation);
122
+ // 3. Entitlement check
123
+ const entitlementRequest = {
124
+ ...(options.format ? { format: options.format } : {}),
125
+ ...(options.width ? { width: options.width } : {}),
126
+ ...(options.height ? { height: options.height } : {}),
127
+ };
128
+ const entitlement = checkRenderEntitlement(verification.manifest, entitlementRequest);
129
+ if (!entitlement.ok)
130
+ throw new CliError(entitlement.message, 1);
131
+ if (options.verifyOnly) {
132
+ return { ok: true, outPath, size: 0, manifestId: verification.manifest.manifestId };
133
+ }
134
+ // 4. Resolve headless browser
135
+ let browser;
136
+ try {
137
+ browser = await resolveHeadlessBrowser(env);
138
+ }
139
+ catch (err) {
140
+ if (err instanceof BrowserNotFoundError) {
141
+ throw new CliError(err.message, 1);
142
+ }
143
+ throw err;
144
+ }
145
+ // 5. Read manifest JSON
146
+ const manifestJson = await readFile(manifestPath, "utf8");
147
+ const manifest = JSON.parse(manifestJson);
148
+ const canvas = manifest.render.canvas;
149
+ // 5.5. Cache external assets locally
150
+ let cacheDir = "";
151
+ if (manifest.render.assets && Array.isArray(manifest.render.assets)) {
152
+ try {
153
+ cacheDir = await cacheAssets(manifest.render.assets);
154
+ }
155
+ catch (err) {
156
+ throw new CliError(`Failed to cache assets: ${err instanceof Error ? err.message : String(err)}`, 1);
157
+ }
158
+ }
159
+ // 6. Start callback server (serves manifest + receives rendered blob)
160
+ // We need a temp path for the manifest that the server can serve.
161
+ // Instead of writing to a temp file, we'll serve the manifest directly.
162
+ const { server } = await startCallbackServer(outPath);
163
+ // Patch the server to serve the manifest from memory instead of from outPath
164
+ // Actually, let's use a simpler approach: override the /manifest.json handler
165
+ // by writing the manifest to a known temp location.
166
+ // For now, the server reads from outPath.replace(ext, ".vivi.json") which
167
+ // isn't what we want. Let me fix this properly.
168
+ // Actually, let's just re-create the server with the manifest in memory.
169
+ server.close();
170
+ let renderError = null;
171
+ let hasBlob = false;
172
+ let blobSize = 0;
173
+ const manifestServer = createServer((req, res) => {
174
+ res.setHeader("Access-Control-Allow-Origin", "*");
175
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
176
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
177
+ if (req.method === "OPTIONS") {
178
+ res.writeHead(204);
179
+ res.end();
180
+ return;
181
+ }
182
+ const url = new URL(req.url ?? "/", `http://localhost`);
183
+ if (cacheDir && url.pathname.startsWith("/assets/") && req.method === "GET") {
184
+ const sha256 = url.pathname.slice(8);
185
+ const cachedPath = join(cacheDir, sha256);
186
+ readFile(cachedPath)
187
+ .then((buffer) => {
188
+ const asset = manifest.render.assets?.find((a) => a.sha256 === sha256);
189
+ res.writeHead(200, { "content-type": asset?.mimeType || "application/octet-stream" });
190
+ res.end(buffer);
191
+ })
192
+ .catch(() => {
193
+ res.writeHead(404);
194
+ res.end("Asset not found");
195
+ });
196
+ return;
197
+ }
198
+ if (url.pathname === "/manifest.json" && req.method === "GET") {
199
+ const manifestObj = { ...manifest };
200
+ if (manifestObj.render?.assets) {
201
+ manifestObj.render.assets = manifestObj.render.assets.map((asset) => ({
202
+ ...asset,
203
+ url: `http://127.0.0.1:${serverPort}/assets/${asset.sha256}`,
204
+ }));
205
+ }
206
+ res.writeHead(200, { "content-type": "application/json" });
207
+ res.end(JSON.stringify(manifestObj));
208
+ return;
209
+ }
210
+ if (url.pathname === "/result" && req.method === "POST") {
211
+ const chunks = [];
212
+ req.on("data", (chunk) => chunks.push(chunk));
213
+ req.on("end", async () => {
214
+ const blob = Buffer.concat(chunks);
215
+ try {
216
+ await writeFile(outPath, blob);
217
+ hasBlob = true;
218
+ blobSize = blob.length;
219
+ res.writeHead(200, { "content-type": "application/json" });
220
+ res.end(JSON.stringify({ ok: true, size: blob.length }));
221
+ }
222
+ catch (err) {
223
+ renderError = err instanceof Error ? err.message : String(err);
224
+ res.writeHead(500, { "content-type": "application/json" });
225
+ res.end(JSON.stringify({ ok: false, error: renderError }));
226
+ }
227
+ });
228
+ return;
229
+ }
230
+ if (url.pathname === "/error" && req.method === "POST") {
231
+ const chunks = [];
232
+ req.on("data", (chunk) => chunks.push(chunk));
233
+ req.on("end", () => {
234
+ let errorMsg = "Unknown render error";
235
+ try {
236
+ const body = JSON.parse(Buffer.concat(chunks).toString());
237
+ errorMsg = body.message ?? errorMsg;
238
+ }
239
+ catch {
240
+ errorMsg = Buffer.concat(chunks).toString() || errorMsg;
241
+ }
242
+ renderError = errorMsg;
243
+ res.writeHead(200, { "content-type": "application/json" });
244
+ res.end(JSON.stringify({ ok: true, error: errorMsg }));
245
+ });
246
+ return;
247
+ }
248
+ if (url.pathname === "/status" && req.method === "GET") {
249
+ res.writeHead(200, { "content-type": "application/json" });
250
+ res.end(JSON.stringify({
251
+ done: hasBlob || renderError !== null,
252
+ error: renderError,
253
+ hasBlob,
254
+ blobSize,
255
+ }));
256
+ return;
257
+ }
258
+ res.writeHead(404);
259
+ res.end("Not found");
260
+ });
261
+ const serverPort = await new Promise((resolve, reject) => {
262
+ manifestServer.on("error", reject);
263
+ manifestServer.listen(0, "127.0.0.1", () => {
264
+ const addr = manifestServer.address();
265
+ if (!addr || typeof addr === "string") {
266
+ reject(new Error("Failed to bind manifest server"));
267
+ return;
268
+ }
269
+ resolve(addr.port);
270
+ });
271
+ });
272
+ // 7. Build render URL — points at the host app's /headless-render page
273
+ const renderBaseUrl = env.VIVIPILOT_RENDER_URL ?? DEFAULT_RENDER_URL;
274
+ const params = new URLSearchParams({
275
+ manifest: `http://127.0.0.1:${serverPort}/manifest.json`,
276
+ format: options.format ?? "mp4",
277
+ headless: "true",
278
+ callback: `http://127.0.0.1:${serverPort}/result`,
279
+ });
280
+ if (options.scale)
281
+ params.set("scale", String(options.scale));
282
+ if (options.transparent)
283
+ params.set("transparent", "true");
284
+ if (options.fps)
285
+ params.set("fps", String(options.fps));
286
+ const fullRenderUrl = `${renderBaseUrl}?${params.toString()}`;
287
+ // 8. Spawn headless browser
288
+ const args = headlessBrowserArgs(fullRenderUrl, {
289
+ windowSize: { width: Math.max(canvas.width, 1280), height: Math.max(canvas.height, 720) },
290
+ });
291
+ const child = spawn(browser.executablePath, args, {
292
+ stdio: ["ignore", "pipe", "pipe"],
293
+ env: { ...env, DISPLAY: env.DISPLAY ?? ":99" },
294
+ });
295
+ const chromeLogPath = join(process.cwd(), "host", "chrome_browser.log");
296
+ const chromeLogFd = openSync(chromeLogPath, "w");
297
+ let browserStderr = "";
298
+ let browserStdout = "";
299
+ child.stderr?.on("data", (data) => {
300
+ browserStderr += data.toString();
301
+ try {
302
+ writeSync(chromeLogFd, data);
303
+ }
304
+ catch { }
305
+ });
306
+ child.stdout?.on("data", (data) => {
307
+ browserStdout += data.toString();
308
+ try {
309
+ writeSync(chromeLogFd, data);
310
+ }
311
+ catch { }
312
+ });
313
+ // 9. Wait for the blob to arrive at /result, or timeout
314
+ const timeoutMs = parseInt(env.VIVIPILOT_RENDER_TIMEOUT_MS ?? String(DEFAULT_TIMEOUT_MS), 10);
315
+ const result = await waitForResult(serverPort, timeoutMs);
316
+ // 10. Clean up
317
+ child.kill("SIGTERM");
318
+ setTimeout(() => {
319
+ try {
320
+ child.kill("SIGKILL");
321
+ }
322
+ catch { /* already dead */ }
323
+ }, 5000);
324
+ manifestServer.close();
325
+ try {
326
+ closeSync(chromeLogFd);
327
+ }
328
+ catch { }
329
+ if (result.error) {
330
+ throw new CliError(`Render failed: ${result.error}\nBrowser stderr (last 2KB):\n${browserStderr.slice(-2000)}\nBrowser stdout (last 1KB):\n${browserStdout.slice(-1000)}`, 1);
331
+ }
332
+ // 11. Verify output file exists
333
+ try {
334
+ await access(outPath, constants.R_OK);
335
+ const stats = await stat(outPath);
336
+ return { ok: true, outPath, size: stats.size, manifestId: verification.manifest.manifestId };
337
+ }
338
+ catch {
339
+ throw new CliError(`Render completed but output file not found at ${outPath}`, 1);
340
+ }
341
+ }
342
+ async function cacheAssets(assets) {
343
+ const cacheDir = join(process.env.HOME || process.env.USERPROFILE || process.cwd(), ".cache", "vivipilot", "assets");
344
+ await mkdir(cacheDir, { recursive: true });
345
+ for (const asset of assets) {
346
+ if (!asset.sha256 || !asset.url)
347
+ continue;
348
+ const cachedPath = join(cacheDir, asset.sha256);
349
+ try {
350
+ await access(cachedPath);
351
+ continue;
352
+ }
353
+ catch {
354
+ // Asset not in cache, download it
355
+ }
356
+ const res = await fetch(asset.url);
357
+ if (!res.ok) {
358
+ throw new Error(`Failed to download asset ${asset.url}: HTTP ${res.status}`);
359
+ }
360
+ const buffer = Buffer.from(await res.arrayBuffer());
361
+ const hash = createHash("sha256").update(buffer).digest("hex");
362
+ if (hash !== asset.sha256) {
363
+ throw new Error(`Asset hash mismatch for ${asset.url}: expected ${asset.sha256}, got ${hash}`);
364
+ }
365
+ await writeFile(cachedPath, buffer);
366
+ }
367
+ return cacheDir;
368
+ }
369
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@vivipilot/cli",
3
+ "version": "0.1.0",
4
+ "description": "Vivipilot paid-only CLI and MCP entrypoint for local rendering workflows",
5
+ "type": "module",
6
+ "bin": {
7
+ "vivipilot": "dist/cli.js"
8
+ },
9
+ "main": "dist/cli.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./cli": {
17
+ "types": "./dist/cli.d.ts",
18
+ "default": "./dist/cli.js"
19
+ }
20
+ },
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "vitest run"
25
+ },
26
+ "keywords": [
27
+ "vivipilot",
28
+ "cli",
29
+ "mcp",
30
+ "local-rendering"
31
+ ],
32
+ "author": "",
33
+ "license": "ISC",
34
+ "dependencies": {
35
+ "@vivipilot/render-manifest": "*"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^25.2.0",
39
+ "typescript": "^5.3.0",
40
+ "vitest": "4.0.18"
41
+ }
42
+ }
package/src/api.ts ADDED
@@ -0,0 +1,163 @@
1
+ import { CliError } from "./errors.js";
2
+ import { type CliConfig, type Env, resolveApiKey, resolveApiUrl } from "./config.js";
3
+
4
+ export type ApiClientOptions = {
5
+ config: CliConfig;
6
+ env?: Env;
7
+ fetchImpl?: typeof fetch;
8
+ };
9
+
10
+ export type BalanceResponse = {
11
+ balance: number;
12
+ paidBalance?: number;
13
+ currency?: "credits";
14
+ topupUrl?: string;
15
+ };
16
+
17
+ export type EstimateRequest = {
18
+ prompt: string;
19
+ canvas?: { width?: number; height?: number; fps?: number };
20
+ durationSeconds?: number;
21
+ enginePreference?: "pixi" | "three" | "auto";
22
+ };
23
+
24
+ export type EstimateResponse = {
25
+ estimatedCredits: number;
26
+ engine: string;
27
+ paidOnly: true;
28
+ warnings?: string[];
29
+ };
30
+
31
+ export type GenerateRequest = EstimateRequest & {
32
+ outputFormat?: "manifest";
33
+ };
34
+
35
+ export type GenerateResponse = {
36
+ generationId: string;
37
+ manifest: unknown;
38
+ creditsCharged?: number;
39
+ };
40
+
41
+ export type StartGenerateResponse = {
42
+ generationId: string;
43
+ status: string;
44
+ estimatedCredits: number;
45
+ engine: string;
46
+ paidOnly: true;
47
+ idempotentReplay?: boolean;
48
+ };
49
+
50
+ export type ProgressResponse = {
51
+ generationId: string;
52
+ status: "pending" | "running" | "completed" | "failed" | "refunded";
53
+ workflowStatus: string | null;
54
+ workflowStage: string | null;
55
+ progressMessage: string | null;
56
+ creditsCharged: number | null;
57
+ estimatedCredits: number | null;
58
+ engine: string | null;
59
+ manifest: {
60
+ manifestId: string;
61
+ manifestHash: string;
62
+ } | null;
63
+ manifestData: unknown;
64
+ error: string | null;
65
+ };
66
+
67
+ export type StatusResponse = {
68
+ generationId: string;
69
+ status: "pending" | "running" | "completed" | "failed";
70
+ manifest?: unknown;
71
+ error?: string;
72
+ };
73
+
74
+ export class VivipilotApiClient {
75
+ private readonly config: CliConfig;
76
+ private readonly env: Env;
77
+ private readonly fetchImpl: typeof fetch;
78
+
79
+ constructor(options: ApiClientOptions) {
80
+ this.config = options.config;
81
+ this.env = options.env ?? process.env;
82
+ this.fetchImpl = options.fetchImpl ?? fetch;
83
+ }
84
+
85
+ private apiUrl(): string {
86
+ return resolveApiUrl(this.config, this.env);
87
+ }
88
+
89
+ private apiKey(): string {
90
+ const apiKey = resolveApiKey(this.config, this.env);
91
+ if (!apiKey) {
92
+ throw new CliError("Vivipilot CLI/MCP generation is paid-only. Set VIVIPILOT_API_KEY or run `vivipilot login --api-key <key>`.", 2);
93
+ }
94
+ return apiKey;
95
+ }
96
+
97
+ private async requestJson<T>(path: string, init: RequestInit = {}): Promise<T> {
98
+ const headers = new Headers(init.headers);
99
+ headers.set("accept", "application/json");
100
+ headers.set("authorization", `Bearer ${this.apiKey()}`);
101
+ headers.set("x-vivipilot-client", "@vivipilot/cli");
102
+ if (init.body && !headers.has("content-type")) headers.set("content-type", "application/json");
103
+
104
+ const response = await this.fetchImpl(`${this.apiUrl()}${path}`, { ...init, headers });
105
+ const text = await response.text();
106
+ const payload = text.length > 0 ? JSON.parse(text) as unknown : {};
107
+
108
+ if (!response.ok) {
109
+ const message = payload && typeof payload === "object" && "error" in payload
110
+ ? String((payload as { error?: unknown }).error)
111
+ : `Vivipilot API request failed with status ${response.status}`;
112
+ const suffix = response.status === 402 ? " Run `vivipilot topup` to buy credits." : "";
113
+ throw new CliError(`${message}${suffix}`, response.status === 401 ? 2 : 1);
114
+ }
115
+
116
+ return payload as T;
117
+ }
118
+
119
+ async whoami(): Promise<unknown> {
120
+ return this.requestJson<unknown>("/api/v1/me");
121
+ }
122
+
123
+ async balance(): Promise<BalanceResponse> {
124
+ return this.requestJson<BalanceResponse>("/api/v1/credits/balance");
125
+ }
126
+
127
+ async estimate(request: EstimateRequest): Promise<EstimateResponse> {
128
+ return this.requestJson<EstimateResponse>("/api/v1/layouts/estimate", {
129
+ method: "POST",
130
+ body: JSON.stringify(request),
131
+ });
132
+ }
133
+
134
+ /** Original synchronous generate — blocks until completion */
135
+ async generate(request: GenerateRequest, idempotencyKey: string): Promise<GenerateResponse> {
136
+ return this.requestJson<GenerateResponse>("/api/v1/layouts/generate", {
137
+ method: "POST",
138
+ headers: { "idempotency-key": idempotencyKey },
139
+ body: JSON.stringify({ ...request, outputFormat: "manifest" }),
140
+ });
141
+ }
142
+
143
+ /** Start generation asynchronously — returns immediately with generationId */
144
+ async startGenerate(request: GenerateRequest, idempotencyKey: string): Promise<StartGenerateResponse> {
145
+ return this.requestJson<StartGenerateResponse>("/api/v1/layouts/generate/start", {
146
+ method: "POST",
147
+ headers: { "idempotency-key": idempotencyKey },
148
+ body: JSON.stringify({ ...request, outputFormat: "manifest" }),
149
+ });
150
+ }
151
+
152
+ /** Poll generation status with workflow progress info */
153
+ async generationProgress(generationId: string): Promise<ProgressResponse> {
154
+ return this.requestJson<ProgressResponse>(
155
+ `/api/v1/layouts/${encodeURIComponent(generationId)}?progress=true`,
156
+ );
157
+ }
158
+
159
+ async generationStatus(generationId: string): Promise<StatusResponse> {
160
+ return this.requestJson<StatusResponse>(`/api/v1/layouts/${encodeURIComponent(generationId)}`);
161
+ }
162
+ }
163
+
@@ -0,0 +1,21 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { flagBoolean, flagNumber, flagString, parseArgv } from "./args.js";
3
+
4
+ describe("parseArgv", () => {
5
+ it("parses command, flags, and positionals", () => {
6
+ const parsed = parseArgv(["generate", "--prompt", "hello", "--width=1280", "extra", "--dry-run"]);
7
+
8
+ expect(parsed.command).toBe("generate");
9
+ expect(flagString(parsed, ["prompt"])).toBe("hello");
10
+ expect(flagNumber(parsed, ["width"])).toBe(1280);
11
+ expect(flagBoolean(parsed, ["dry-run"])).toBe(true);
12
+ expect(parsed.positionals).toEqual(["extra"]);
13
+ });
14
+
15
+ it("keeps tokens after separator as positionals", () => {
16
+ const parsed = parseArgv(["verify", "--", "--not-a-flag"]);
17
+
18
+ expect(parsed.command).toBe("verify");
19
+ expect(parsed.positionals).toEqual(["--not-a-flag"]);
20
+ });
21
+ });