@vivipilot/cli 0.1.0 → 0.1.4
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/dist/chunk-FGUKEVKT.js +1 -0
- package/dist/chunk-FN7FHZ3D.js +1 -0
- package/dist/chunk-JJDAKVXA.js +8 -0
- package/dist/chunk-L3C7EOPY.js +2 -0
- package/dist/chunk-ZIATVRTF.js +3 -0
- package/dist/chunk-ZZ72PPC3.js +1 -0
- package/dist/cli.d.ts +5 -4
- package/dist/cli.js +57 -495
- package/dist/config-DAG58pP-.d.ts +16 -0
- package/dist/index.d.ts +121 -7
- package/dist/index.js +1 -7
- package/dist/manifest-TDCIHZ46.js +1 -0
- package/dist/mcp-DTGU64NI.js +1 -0
- package/dist/render-CVXYAUBY.js +1 -0
- package/package.json +6 -2
- package/dist/api.d.ts +0 -86
- package/dist/api.d.ts.map +0 -1
- package/dist/api.js +0 -77
- package/dist/api.js.map +0 -1
- package/dist/args.d.ts +0 -11
- package/dist/args.d.ts.map +0 -1
- package/dist/args.js +0 -53
- package/dist/args.js.map +0 -1
- package/dist/browser.d.ts +0 -31
- package/dist/browser.d.ts.map +0 -1
- package/dist/browser.js +0 -162
- package/dist/browser.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/config.d.ts +0 -15
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -58
- package/dist/config.js.map +0 -1
- package/dist/errors.d.ts +0 -6
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -12
- package/dist/errors.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/manifest.d.ts +0 -40
- package/dist/manifest.d.ts.map +0 -1
- package/dist/manifest.js +0 -90
- package/dist/manifest.js.map +0 -1
- package/dist/mcp.d.ts +0 -13
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js +0 -392
- package/dist/mcp.js.map +0 -1
- package/dist/render.d.ts +0 -21
- package/dist/render.d.ts.map +0 -1
- package/dist/render.js +0 -369
- package/dist/render.js.map +0 -1
- package/src/api.ts +0 -163
- package/src/args.test.ts +0 -21
- package/src/args.ts +0 -64
- package/src/browser.test.ts +0 -103
- package/src/browser.ts +0 -174
- package/src/cli.ts +0 -656
- package/src/config.test.ts +0 -30
- package/src/config.ts +0 -71
- package/src/errors.ts +0 -14
- package/src/index.ts +0 -25
- package/src/manifest.test.ts +0 -105
- package/src/manifest.ts +0 -126
- package/src/mcp.test.ts +0 -48
- package/src/mcp.ts +0 -438
- package/src/render.ts +0 -424
- package/tsconfig.json +0 -26
package/src/browser.test.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { headlessBrowserArgs, BrowserNotFoundError, resolveHeadlessBrowser } from "./browser.js";
|
|
3
|
-
|
|
4
|
-
let mockNoBrowsers = false;
|
|
5
|
-
|
|
6
|
-
vi.mock("node:fs/promises", async (importOriginal) => {
|
|
7
|
-
const original = await importOriginal<typeof import("node:fs/promises")>();
|
|
8
|
-
return {
|
|
9
|
-
...original,
|
|
10
|
-
access: async (path: string, mode?: number) => {
|
|
11
|
-
if (mockNoBrowsers) {
|
|
12
|
-
throw new Error("Mocked: File not executable");
|
|
13
|
-
}
|
|
14
|
-
return original.access(path, mode);
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
describe("headlessBrowserArgs", () => {
|
|
20
|
-
it("includes headless flag and render URL", () => {
|
|
21
|
-
const args = headlessBrowserArgs("http://localhost:4001/headless-render");
|
|
22
|
-
expect(args).toContain("--headless=new");
|
|
23
|
-
expect(args).toContain("http://localhost:4001/headless-render");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("includes GPU/WebGL enablement flags", () => {
|
|
27
|
-
const args = headlessBrowserArgs("http://example.com");
|
|
28
|
-
expect(args).toContain("--use-gl=angle");
|
|
29
|
-
expect(args).toContain("--use-angle=swiftshader");
|
|
30
|
-
expect(args).toContain("--enable-unsafe-swiftshader");
|
|
31
|
-
expect(args).toContain("--ignore-gpu-blocklist");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("includes no-sandbox flags for CI", () => {
|
|
35
|
-
const args = headlessBrowserArgs("http://example.com");
|
|
36
|
-
expect(args).toContain("--no-sandbox");
|
|
37
|
-
expect(args).toContain("--disable-gpu-sandbox");
|
|
38
|
-
expect(args).toContain("--disable-dev-shm-usage");
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("sets window size from options", () => {
|
|
42
|
-
const args = headlessBrowserArgs("http://example.com", {
|
|
43
|
-
windowSize: { width: 1920, height: 1080 },
|
|
44
|
-
});
|
|
45
|
-
expect(args).toContain("--window-size=1920,1080");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("defaults to 1920x1080", () => {
|
|
49
|
-
const args = headlessBrowserArgs("http://example.com");
|
|
50
|
-
expect(args).toContain("--window-size=1920,1080");
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("appends extra args", () => {
|
|
54
|
-
const args = headlessBrowserArgs("http://example.com", {
|
|
55
|
-
extraArgs: ["--remote-debugging-port=9222"],
|
|
56
|
-
});
|
|
57
|
-
expect(args).toContain("--remote-debugging-port=9222");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("URL is the last argument", () => {
|
|
61
|
-
const url = "http://localhost:1234/render";
|
|
62
|
-
const args = headlessBrowserArgs(url);
|
|
63
|
-
expect(args[args.length - 1]).toBe(url);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe("BrowserNotFoundError", () => {
|
|
68
|
-
it("is an Error subclass", () => {
|
|
69
|
-
const err = new BrowserNotFoundError("not found");
|
|
70
|
-
expect(err).toBeInstanceOf(Error);
|
|
71
|
-
expect(err.name).toBe("BrowserNotFoundError");
|
|
72
|
-
expect(err.message).toBe("not found");
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe("resolveHeadlessBrowser", () => {
|
|
77
|
-
beforeEach(() => {
|
|
78
|
-
mockNoBrowsers = false;
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
afterEach(() => {
|
|
82
|
-
mockNoBrowsers = false;
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("throws BrowserNotFoundError when no browser is available", async () => {
|
|
86
|
-
mockNoBrowsers = true;
|
|
87
|
-
const env: Record<string, string | undefined> = {
|
|
88
|
-
VIVIPILOT_HEADLESS_BROWSER: undefined,
|
|
89
|
-
PUPPETEER_EXECUTABLE_PATH: undefined,
|
|
90
|
-
HOME: "/nonexistent-home-path-for-testing",
|
|
91
|
-
};
|
|
92
|
-
await expect(resolveHeadlessBrowser(env)).rejects.toBeInstanceOf(BrowserNotFoundError);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("uses VIVIPILOT_HEADLESS_BROWSER when set to an executable", async () => {
|
|
96
|
-
const env: Record<string, string | undefined> = {
|
|
97
|
-
VIVIPILOT_HEADLESS_BROWSER: "/bin/true",
|
|
98
|
-
};
|
|
99
|
-
const result = await resolveHeadlessBrowser(env);
|
|
100
|
-
expect(result.executablePath).toBe("/bin/true");
|
|
101
|
-
expect(result.source).toBe("env");
|
|
102
|
-
});
|
|
103
|
-
});
|
package/src/browser.ts
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { access, constants } from "node:fs/promises";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
export type BrowserResolution = {
|
|
7
|
-
executablePath: string;
|
|
8
|
-
source: "env" | "chrome-headless-shell" | "system-chrome" | "system-chromium";
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export class BrowserNotFoundError extends Error {
|
|
12
|
-
constructor(message: string) {
|
|
13
|
-
super(message);
|
|
14
|
-
this.name = "BrowserNotFoundError";
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Resolve a browser binary for headless rendering.
|
|
20
|
-
*
|
|
21
|
-
* Priority:
|
|
22
|
-
* 1. `VIVIPILOT_HEADLESS_BROWSER` env var (explicit override)
|
|
23
|
-
* 2. `chrome-headless-shell` in PATH or common install locations
|
|
24
|
-
* 3. System Chrome / Chromium in common install locations
|
|
25
|
-
*
|
|
26
|
-
* Returns the path + source. Throws `BrowserNotFoundError` when no browser is found.
|
|
27
|
-
*/
|
|
28
|
-
export async function resolveHeadlessBrowser(env: NodeJS.ProcessEnv = process.env): Promise<BrowserResolution> {
|
|
29
|
-
const envPath = env.VIVIPILOT_HEADLESS_BROWSER;
|
|
30
|
-
if (envPath && await isExecutable(envPath)) {
|
|
31
|
-
return { executablePath: envPath, source: "env" };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const shell = await findChromeHeadlessShell();
|
|
35
|
-
if (shell) return { executablePath: shell, source: "chrome-headless-shell" };
|
|
36
|
-
|
|
37
|
-
const chrome = await findSystemChrome();
|
|
38
|
-
if (chrome) return { executablePath: chrome, source: "system-chrome" };
|
|
39
|
-
|
|
40
|
-
const chromium = await findSystemChromium();
|
|
41
|
-
if (chromium) return { executablePath: chromium, source: "system-chromium" };
|
|
42
|
-
|
|
43
|
-
throw new BrowserNotFoundError(
|
|
44
|
-
"No headless browser found. Install chrome-headless-shell or Chrome/Chromium, or set VIVIPILOT_HEADLESS_BROWSER to the binary path.",
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function isExecutable(path: string): Promise<boolean> {
|
|
49
|
-
try {
|
|
50
|
-
await access(path, constants.X_OK);
|
|
51
|
-
return true;
|
|
52
|
-
} catch {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function findChromeHeadlessShell(): Promise<string | null> {
|
|
58
|
-
const candidates = [
|
|
59
|
-
process.env.PUPPETEER_EXECUTABLE_PATH,
|
|
60
|
-
join(homedir(), ".cache", "puppeteer", "chrome-headless-shell"),
|
|
61
|
-
"/usr/bin/chrome-headless-shell",
|
|
62
|
-
"/usr/local/bin/chrome-headless-shell",
|
|
63
|
-
"/snap/bin/chrome-headless-shell",
|
|
64
|
-
join(homedir(), ".local", "share", "chrome-headless-shell", "chrome-headless-shell"),
|
|
65
|
-
"/opt/chrome-headless-shell/chrome-headless-shell",
|
|
66
|
-
// macOS
|
|
67
|
-
"/Applications/Google Chrome Headless Shell.app/Contents/MacOS/Google Chrome Headless Shell",
|
|
68
|
-
// Windows
|
|
69
|
-
join(homedir(), "AppData", "Local", "chrome-headless-shell", "chrome-headless-shell.exe"),
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
for (const candidate of candidates) {
|
|
73
|
-
if (!candidate) continue;
|
|
74
|
-
if (await isExecutable(candidate)) return candidate;
|
|
75
|
-
// Some install dirs contain a versioned subfolder
|
|
76
|
-
if (existsSync(candidate) && !candidate.endsWith("chrome-headless-shell") && !candidate.endsWith(".exe")) {
|
|
77
|
-
// Search for the binary inside versioned subdirs
|
|
78
|
-
const { readdirSync } = await import("node:fs");
|
|
79
|
-
try {
|
|
80
|
-
const entries = readdirSync(candidate, { withFileTypes: true });
|
|
81
|
-
for (const entry of entries) {
|
|
82
|
-
if (!entry.isDirectory()) continue;
|
|
83
|
-
const inner = join(candidate, entry.name, "chrome-headless-shell");
|
|
84
|
-
if (await isExecutable(inner)) return inner;
|
|
85
|
-
const innerExe = join(candidate, entry.name, "chrome-headless-shell.exe");
|
|
86
|
-
if (await isExecutable(innerExe)) return innerExe;
|
|
87
|
-
}
|
|
88
|
-
} catch { /* ignore */ }
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function findSystemChrome(): Promise<string | null> {
|
|
95
|
-
const candidates = [
|
|
96
|
-
"/usr/bin/google-chrome",
|
|
97
|
-
"/usr/bin/google-chrome-stable",
|
|
98
|
-
"/usr/bin/google-chrome-unstable",
|
|
99
|
-
"/usr/local/bin/google-chrome",
|
|
100
|
-
"/opt/google/chrome/chrome",
|
|
101
|
-
"/snap/bin/google-chrome",
|
|
102
|
-
// macOS
|
|
103
|
-
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
104
|
-
// Windows
|
|
105
|
-
join(homedir(), "AppData", "Local", "Google", "Chrome", "Application", "chrome.exe"),
|
|
106
|
-
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
|
107
|
-
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
|
|
108
|
-
];
|
|
109
|
-
|
|
110
|
-
for (const candidate of candidates) {
|
|
111
|
-
if (await isExecutable(candidate)) return candidate;
|
|
112
|
-
}
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function findSystemChromium(): Promise<string | null> {
|
|
117
|
-
const candidates = [
|
|
118
|
-
"/usr/bin/chromium",
|
|
119
|
-
"/usr/bin/chromium-browser",
|
|
120
|
-
"/usr/local/bin/chromium",
|
|
121
|
-
"/snap/bin/chromium",
|
|
122
|
-
"/usr/bin/brave-browser",
|
|
123
|
-
"/usr/bin/brave",
|
|
124
|
-
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
125
|
-
join(homedir(), "AppData", "Local", "Chromium", "Application", "chromium.exe"),
|
|
126
|
-
"C:\\Program Files\\Chromium\\Application\\chromium.exe",
|
|
127
|
-
];
|
|
128
|
-
|
|
129
|
-
for (const candidate of candidates) {
|
|
130
|
-
if (await isExecutable(candidate)) return candidate;
|
|
131
|
-
}
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Build the Chrome/Chromium CLI flags for headless rendering with WebGL/WebGPU support.
|
|
137
|
-
* These flags ensure the GPU is available (or SwiftShader fallback) so Pixi v8 can
|
|
138
|
-
* initialise its renderer inside a headless browser.
|
|
139
|
-
*/
|
|
140
|
-
export function headlessBrowserArgs(renderUrl: string, options?: {
|
|
141
|
-
windowSize?: { width: number; height: number };
|
|
142
|
-
extraArgs?: string[];
|
|
143
|
-
}): string[] {
|
|
144
|
-
const width = options?.windowSize?.width ?? 1920;
|
|
145
|
-
const height = options?.windowSize?.height ?? 1080;
|
|
146
|
-
|
|
147
|
-
return [
|
|
148
|
-
"--headless=new",
|
|
149
|
-
"--no-sandbox",
|
|
150
|
-
"--disable-gpu-sandbox",
|
|
151
|
-
"--disable-dev-shm-usage",
|
|
152
|
-
"--disable-extensions",
|
|
153
|
-
"--disable-background-networking",
|
|
154
|
-
"--disable-sync",
|
|
155
|
-
"--disable-translate",
|
|
156
|
-
"--disable-default-apps",
|
|
157
|
-
"--disable-popup-blocking",
|
|
158
|
-
"--disable-component-update",
|
|
159
|
-
"--disable-metrics",
|
|
160
|
-
"--no-first-run",
|
|
161
|
-
"--mute-audio",
|
|
162
|
-
"--enable-logging=stderr",
|
|
163
|
-
"--log-level=0",
|
|
164
|
-
"--v=1",
|
|
165
|
-
`--window-size=${width},${height}`,
|
|
166
|
-
"--enable-features=Vulkan",
|
|
167
|
-
"--use-gl=angle",
|
|
168
|
-
"--use-angle=swiftshader",
|
|
169
|
-
"--enable-unsafe-swiftshader",
|
|
170
|
-
"--ignore-gpu-blocklist",
|
|
171
|
-
...(options?.extraArgs ?? []),
|
|
172
|
-
renderUrl,
|
|
173
|
-
];
|
|
174
|
-
}
|