imprint-mcp 0.3.0 → 0.4.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/README.md +42 -2
- package/examples/google-flights/get_flight_calendar_prices/index.ts +2 -2
- package/examples/google-flights/get_flight_calendar_prices/workflow.json +4 -2
- package/examples/southwest/README.md +3 -2
- package/examples/southwest/search_southwest_flights/index.ts +18 -1
- package/examples/southwest/search_southwest_flights/workflow.json +18 -1
- package/package.json +1 -1
- package/src/cli.ts +152 -1
- package/src/imprint/cdp-browser-fetch.ts +4 -1
- package/src/imprint/chromium.ts +279 -8
- package/src/imprint/compile-tools.ts +11 -3
- package/src/imprint/cron.ts +3 -0
- package/src/imprint/doctor.ts +9 -3
- package/src/imprint/export-archive.ts +355 -0
- package/src/imprint/install.ts +79 -4
- package/src/imprint/integrations.ts +1 -1
- package/src/imprint/mcp-maintenance.ts +15 -13
- package/src/imprint/mcp-server.ts +1 -1
- package/src/imprint/stealth-chromium.ts +6 -8
- package/src/imprint/teach-state.ts +37 -0
- package/src/imprint/teach.ts +62 -29
package/src/imprint/chromium.ts
CHANGED
|
@@ -2,11 +2,20 @@
|
|
|
2
2
|
* bundled Chromium (unmanaged) over system Chrome (corporate policy
|
|
3
3
|
* often blocks --remote-debugging-port). $CHROMIUM_PATH overrides. */
|
|
4
4
|
|
|
5
|
-
import { type ChildProcess, spawn } from 'node:child_process';
|
|
6
|
-
import {
|
|
5
|
+
import { type ChildProcess, spawnSync as nodeSpawnSync, spawn } from 'node:child_process';
|
|
6
|
+
import {
|
|
7
|
+
closeSync,
|
|
8
|
+
existsSync,
|
|
9
|
+
openSync,
|
|
10
|
+
readFileSync,
|
|
11
|
+
readdirSync,
|
|
12
|
+
statSync,
|
|
13
|
+
unlinkSync,
|
|
14
|
+
} from 'node:fs';
|
|
15
|
+
import { createRequire } from 'node:module';
|
|
7
16
|
import { createServer } from 'node:net';
|
|
8
17
|
import { homedir, tmpdir } from 'node:os';
|
|
9
|
-
import { join as pathJoin } from 'node:path';
|
|
18
|
+
import { dirname as pathDirname, join as pathJoin } from 'node:path';
|
|
10
19
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
11
20
|
import { isDebug } from './log.ts';
|
|
12
21
|
|
|
@@ -62,6 +71,13 @@ export function chromeProxyArg(proxy: string): string | null {
|
|
|
62
71
|
return /^[\w.-]+:\d+$/.test(proxy) ? proxy : null;
|
|
63
72
|
}
|
|
64
73
|
|
|
74
|
+
export function shouldDisableChromiumSandbox(): boolean {
|
|
75
|
+
const override = process.env.IMPRINT_CHROMIUM_NO_SANDBOX?.trim().toLowerCase();
|
|
76
|
+
if (override === '1' || override === 'true' || override === 'yes') return true;
|
|
77
|
+
if (override === '0' || override === 'false' || override === 'no') return false;
|
|
78
|
+
return process.platform === 'linux' && existsSync('/.dockerenv');
|
|
79
|
+
}
|
|
80
|
+
|
|
65
81
|
interface LaunchedChromium {
|
|
66
82
|
process: ChildProcess;
|
|
67
83
|
port: number;
|
|
@@ -77,14 +93,36 @@ const LINUX_CANDIDATES = [
|
|
|
77
93
|
'/usr/bin/chromium',
|
|
78
94
|
'/usr/bin/chromium-browser',
|
|
79
95
|
];
|
|
96
|
+
const require = createRequire(import.meta.url);
|
|
97
|
+
|
|
98
|
+
export function defaultPlaywrightBrowsersPath(): string | undefined {
|
|
99
|
+
const hermesHome = process.env.HERMES_HOME?.trim();
|
|
100
|
+
if (hermesHome) return pathJoin(hermesHome, '.cache', 'ms-playwright');
|
|
101
|
+
const explicit = process.env.PLAYWRIGHT_BROWSERS_PATH?.trim();
|
|
102
|
+
if (explicit) return explicit;
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function playwrightInstallEnv(): NodeJS.ProcessEnv {
|
|
107
|
+
const env = { ...process.env };
|
|
108
|
+
const browsersPath = defaultPlaywrightBrowsersPath();
|
|
109
|
+
if (browsersPath) env.PLAYWRIGHT_BROWSERS_PATH = browsersPath;
|
|
110
|
+
return env;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function playwrightChromiumCacheRoots(): string[] {
|
|
114
|
+
const roots = [
|
|
115
|
+
defaultPlaywrightBrowsersPath(),
|
|
116
|
+
pathJoin(homedir(), 'Library/Caches/ms-playwright'),
|
|
117
|
+
pathJoin(homedir(), '.cache/ms-playwright'),
|
|
118
|
+
].filter((root): root is string => Boolean(root));
|
|
119
|
+
return [...new Set(roots)];
|
|
120
|
+
}
|
|
80
121
|
|
|
81
122
|
/** Find Playwright's "Google Chrome for Testing" — newest version wins
|
|
82
123
|
* if multiple are installed. */
|
|
83
124
|
function findPlaywrightChromium(): string | null {
|
|
84
|
-
const cacheRoots =
|
|
85
|
-
pathJoin(homedir(), 'Library/Caches/ms-playwright'),
|
|
86
|
-
pathJoin(homedir(), '.cache/ms-playwright'),
|
|
87
|
-
];
|
|
125
|
+
const cacheRoots = playwrightChromiumCacheRoots();
|
|
88
126
|
for (const root of cacheRoots) {
|
|
89
127
|
if (!existsSync(root)) continue;
|
|
90
128
|
let dirs: string[];
|
|
@@ -122,6 +160,7 @@ function findPlaywrightChromium(): string | null {
|
|
|
122
160
|
'Google Chrome for Testing',
|
|
123
161
|
),
|
|
124
162
|
// Linux layout
|
|
163
|
+
pathJoin(root, dir, 'chrome-linux64', 'chrome'),
|
|
125
164
|
pathJoin(root, dir, 'chrome-linux', 'chrome'),
|
|
126
165
|
];
|
|
127
166
|
for (const c of candidates) {
|
|
@@ -136,7 +175,236 @@ function findPlaywrightChromium(): string | null {
|
|
|
136
175
|
return null;
|
|
137
176
|
}
|
|
138
177
|
|
|
178
|
+
function playwrightInstallCommand(): string[] {
|
|
179
|
+
const playwrightCli = resolvePlaywrightCli();
|
|
180
|
+
if (playwrightCli) return ['node', playwrightCli, 'install', 'chromium'];
|
|
181
|
+
if (process.versions.bun) return [process.execPath, 'x', 'playwright', 'install', 'chromium'];
|
|
182
|
+
return ['bunx', 'playwright', 'install', 'chromium'];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function resolvePlaywrightCli(): string | null {
|
|
186
|
+
try {
|
|
187
|
+
const packageJson = require.resolve('playwright/package.json');
|
|
188
|
+
const cli = pathJoin(pathDirname(packageJson), 'cli.js');
|
|
189
|
+
return existsSync(cli) ? cli : null;
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function quoteShellArg(value: string): string {
|
|
196
|
+
if (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) return value;
|
|
197
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function commandText(command: string[], env: NodeJS.ProcessEnv): string {
|
|
201
|
+
const prefix = env.PLAYWRIGHT_BROWSERS_PATH
|
|
202
|
+
? `PLAYWRIGHT_BROWSERS_PATH=${quoteShellArg(env.PLAYWRIGHT_BROWSERS_PATH)} `
|
|
203
|
+
: '';
|
|
204
|
+
return `${prefix}${command.map(quoteShellArg).join(' ')}`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
interface InstallerResult {
|
|
208
|
+
exitCode: number | null;
|
|
209
|
+
stdout?: string;
|
|
210
|
+
stderr?: string;
|
|
211
|
+
signal?: NodeJS.Signals | null;
|
|
212
|
+
error?: string;
|
|
213
|
+
timedOut?: boolean;
|
|
214
|
+
timeoutMs?: number;
|
|
215
|
+
logPath?: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
type ChromiumInstaller = (command: string[], env: NodeJS.ProcessEnv) => InstallerResult;
|
|
219
|
+
|
|
220
|
+
let chromiumInstallerForTest: ChromiumInstaller | null = null;
|
|
221
|
+
let chromiumFinderForTest: (() => string | null) | null = null;
|
|
222
|
+
let verifiedChromiumPath: string | null = null;
|
|
223
|
+
|
|
224
|
+
export function __setPlaywrightChromiumInstallerForTest(installer: ChromiumInstaller | null): void {
|
|
225
|
+
chromiumInstallerForTest = installer;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function __setChromiumFinderForTest(finder: (() => string | null) | null): void {
|
|
229
|
+
chromiumFinderForTest = finder;
|
|
230
|
+
verifiedChromiumPath = null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function runPlaywrightChromiumInstall(command: string[], env: NodeJS.ProcessEnv): InstallerResult {
|
|
234
|
+
if (chromiumInstallerForTest) return chromiumInstallerForTest(command, env);
|
|
235
|
+
const timeoutMs = playwrightInstallTimeoutMs();
|
|
236
|
+
const logPath = pathJoin(tmpdir(), `imprint-playwright-install-${process.pid}-${Date.now()}.log`);
|
|
237
|
+
let logFd: number | null = null;
|
|
238
|
+
try {
|
|
239
|
+
logFd = openSync(logPath, 'w');
|
|
240
|
+
const result = nodeSpawnSync(command[0] ?? '', command.slice(1), {
|
|
241
|
+
env,
|
|
242
|
+
// Playwright emits frequent progress lines. Send them to a file so parent
|
|
243
|
+
// command runners that capture stderr without draining it cannot block.
|
|
244
|
+
stdio: ['ignore', logFd, logFd],
|
|
245
|
+
timeout: timeoutMs,
|
|
246
|
+
});
|
|
247
|
+
const failed = result.status !== 0 || Boolean(result.error);
|
|
248
|
+
if (!failed) unlinkInstallerLog(logPath);
|
|
249
|
+
return formatSpawnResult(
|
|
250
|
+
result,
|
|
251
|
+
timeoutMs,
|
|
252
|
+
failed ? readInstallerLog(logPath) : undefined,
|
|
253
|
+
logPath,
|
|
254
|
+
);
|
|
255
|
+
} finally {
|
|
256
|
+
if (logFd !== null) closeSync(logFd);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function formatSpawnResult(
|
|
261
|
+
result: ReturnType<typeof nodeSpawnSync>,
|
|
262
|
+
timeoutMs: number,
|
|
263
|
+
output: string | undefined,
|
|
264
|
+
logPath: string,
|
|
265
|
+
): InstallerResult {
|
|
266
|
+
const error = result.error as (Error & { code?: string }) | undefined;
|
|
267
|
+
return {
|
|
268
|
+
exitCode: result.status,
|
|
269
|
+
stderr: output,
|
|
270
|
+
signal: result.signal,
|
|
271
|
+
error: error?.message,
|
|
272
|
+
timedOut: error?.code === 'ETIMEDOUT',
|
|
273
|
+
timeoutMs,
|
|
274
|
+
logPath,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function readInstallerLog(logPath: string): string | undefined {
|
|
279
|
+
try {
|
|
280
|
+
const output = readFileSync(logPath, 'utf8').trim();
|
|
281
|
+
const maxChars = 50_000;
|
|
282
|
+
if (output.length <= maxChars) return output;
|
|
283
|
+
return `[last ${maxChars} chars of ${logPath}]\n${output.slice(-maxChars)}`;
|
|
284
|
+
} catch {
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function unlinkInstallerLog(logPath: string): void {
|
|
290
|
+
try {
|
|
291
|
+
unlinkSync(logPath);
|
|
292
|
+
} catch {
|
|
293
|
+
// best effort cleanup
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function playwrightInstallTimeoutMs(): number {
|
|
298
|
+
const raw = process.env.IMPRINT_PLAYWRIGHT_INSTALL_TIMEOUT_MS?.trim();
|
|
299
|
+
if (!raw) return 10 * 60 * 1000;
|
|
300
|
+
const parsed = Number(raw);
|
|
301
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 10 * 60 * 1000;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function formatInstallerFailure(result: InstallerResult): string | undefined {
|
|
305
|
+
const lines: string[] = [];
|
|
306
|
+
if (result.timedOut) {
|
|
307
|
+
lines.push(`Timed out after ${Math.round((result.timeoutMs ?? 0) / 1000)}s.`);
|
|
308
|
+
}
|
|
309
|
+
if (result.signal) lines.push(`Terminated by signal: ${result.signal}`);
|
|
310
|
+
if (result.error) lines.push(`Error: ${result.error}`);
|
|
311
|
+
const output = [result.stderr, result.stdout].filter(Boolean).join('\n').trim();
|
|
312
|
+
if (output) lines.push(`Output:\n${output}`);
|
|
313
|
+
else if (result.logPath) lines.push(`Installer log: ${result.logPath}`);
|
|
314
|
+
return lines.length > 0 ? lines.join('\n') : undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
interface EnsureChromiumResult {
|
|
318
|
+
path: string;
|
|
319
|
+
installed: boolean;
|
|
320
|
+
command?: string;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function verifyChromiumExecutable(path: string): void {
|
|
324
|
+
if (verifiedChromiumPath === path) return;
|
|
325
|
+
const result = Bun.spawnSync([path, '--version'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
326
|
+
if (result.exitCode === 0) {
|
|
327
|
+
verifiedChromiumPath = path;
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const output = `${result.stderr.toString()}\n${result.stdout.toString()}`.trim();
|
|
331
|
+
throw new Error(
|
|
332
|
+
[
|
|
333
|
+
`Chromium was found at ${path}, but it could not start.`,
|
|
334
|
+
output ? `Output:\n${output}` : undefined,
|
|
335
|
+
process.platform === 'linux'
|
|
336
|
+
? 'Install missing Linux browser libraries with: bunx playwright install --with-deps chromium'
|
|
337
|
+
: undefined,
|
|
338
|
+
]
|
|
339
|
+
.filter((line): line is string => Boolean(line))
|
|
340
|
+
.join('\n'),
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function ensurePlaywrightChromiumInstalled(
|
|
345
|
+
opts: {
|
|
346
|
+
log?: (message: string) => void;
|
|
347
|
+
} = {},
|
|
348
|
+
): EnsureChromiumResult {
|
|
349
|
+
let existingPath: string | null = null;
|
|
350
|
+
try {
|
|
351
|
+
existingPath = findChromium();
|
|
352
|
+
} catch {
|
|
353
|
+
// Install below.
|
|
354
|
+
}
|
|
355
|
+
if (existingPath) {
|
|
356
|
+
verifyChromiumExecutable(existingPath);
|
|
357
|
+
return { path: existingPath, installed: false };
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const env = playwrightInstallEnv();
|
|
361
|
+
if (env.PLAYWRIGHT_BROWSERS_PATH) {
|
|
362
|
+
process.env.PLAYWRIGHT_BROWSERS_PATH = env.PLAYWRIGHT_BROWSERS_PATH;
|
|
363
|
+
}
|
|
364
|
+
const command = playwrightInstallCommand();
|
|
365
|
+
const displayCommand = commandText(command, env);
|
|
366
|
+
opts.log?.(`Chromium not found; installing Playwright Chromium with: ${displayCommand}`);
|
|
367
|
+
const result = runPlaywrightChromiumInstall(command, env);
|
|
368
|
+
if (result.exitCode !== 0 || result.error) {
|
|
369
|
+
const failure = formatInstallerFailure(result);
|
|
370
|
+
throw new Error(
|
|
371
|
+
[
|
|
372
|
+
'Could not install Playwright Chromium automatically.',
|
|
373
|
+
`Command: ${displayCommand}`,
|
|
374
|
+
failure,
|
|
375
|
+
'',
|
|
376
|
+
'Retry manually with the command above.',
|
|
377
|
+
process.platform === 'linux'
|
|
378
|
+
? 'If Chromium is installed but cannot launch in a fresh Linux image, install OS browser libraries with: bunx playwright install --with-deps chromium'
|
|
379
|
+
: undefined,
|
|
380
|
+
]
|
|
381
|
+
.filter((line): line is string => Boolean(line))
|
|
382
|
+
.join('\n'),
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
const path = findChromium();
|
|
388
|
+
verifyChromiumExecutable(path);
|
|
389
|
+
return { path, installed: true, command: displayCommand };
|
|
390
|
+
} catch (err) {
|
|
391
|
+
throw new Error(
|
|
392
|
+
[
|
|
393
|
+
'Playwright Chromium install completed, but Imprint still could not locate or start the Chromium binary.',
|
|
394
|
+
`Command: ${displayCommand}`,
|
|
395
|
+
err instanceof Error ? err.message : String(err),
|
|
396
|
+
].join('\n'),
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
139
401
|
export function findChromium(): string {
|
|
402
|
+
if (chromiumFinderForTest) {
|
|
403
|
+
const path = chromiumFinderForTest();
|
|
404
|
+
if (path) return path;
|
|
405
|
+
throw new Error('Could not locate Chromium.');
|
|
406
|
+
}
|
|
407
|
+
|
|
140
408
|
const explicit = process.env.CHROMIUM_PATH;
|
|
141
409
|
if (explicit && existsSync(explicit)) return explicit;
|
|
142
410
|
|
|
@@ -272,7 +540,9 @@ async function waitForCdp(port: number, timeoutMs = 10_000): Promise<void> {
|
|
|
272
540
|
}
|
|
273
541
|
|
|
274
542
|
export async function launchChromium(opts: LaunchOptions = {}): Promise<LaunchedChromium> {
|
|
275
|
-
const exe =
|
|
543
|
+
const exe = ensurePlaywrightChromiumInstalled({
|
|
544
|
+
log: (message) => process.stderr.write(`[imprint] ${message}\n`),
|
|
545
|
+
}).path;
|
|
276
546
|
const port = opts.port ?? (await pickFreePort());
|
|
277
547
|
const userDataDir =
|
|
278
548
|
opts.userDataDir ?? pathJoin(tmpdir(), `imprint-chrome-${Date.now()}-${process.pid}`);
|
|
@@ -286,6 +556,7 @@ export async function launchChromium(opts: LaunchOptions = {}): Promise<Launched
|
|
|
286
556
|
'--disable-popup-blocking',
|
|
287
557
|
'--use-mock-keychain',
|
|
288
558
|
];
|
|
559
|
+
if (shouldDisableChromiumSandbox()) args.push('--no-sandbox');
|
|
289
560
|
if (opts.headless) args.push('--headless=new');
|
|
290
561
|
const proxy = opts.proxy ?? proxyUrl();
|
|
291
562
|
if (proxy) {
|
|
@@ -7,7 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { spawn } from 'node:child_process';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
existsSync,
|
|
12
|
+
mkdirSync,
|
|
13
|
+
readFileSync,
|
|
14
|
+
realpathSync,
|
|
15
|
+
unlinkSync,
|
|
16
|
+
writeFileSync,
|
|
17
|
+
} from 'node:fs';
|
|
11
18
|
import { basename, dirname, join as pathJoin, relative as pathRelative } from 'node:path';
|
|
12
19
|
import type { AgentTool } from './agent.ts';
|
|
13
20
|
import { inferAppApiHosts } from './app-api-hosts.ts';
|
|
@@ -1234,8 +1241,9 @@ export async function typecheckArtifacts(
|
|
|
1234
1241
|
includes: string[],
|
|
1235
1242
|
): Promise<{ stdout: string; stderr: string; exitCode: number; timedOut: boolean }> {
|
|
1236
1243
|
const configPath = pathJoin(dir, '.imprint-typecheck.tsconfig.json');
|
|
1237
|
-
const rootTsconfig = pathJoin(REPO_ROOT, 'tsconfig.json');
|
|
1238
|
-
const
|
|
1244
|
+
const rootTsconfig = realpathSync(pathJoin(REPO_ROOT, 'tsconfig.json'));
|
|
1245
|
+
const configDir = realpathSync(dir);
|
|
1246
|
+
const extendsPath = normalizeTsconfigPath(pathRelative(configDir, rootTsconfig));
|
|
1239
1247
|
|
|
1240
1248
|
writeFileSync(
|
|
1241
1249
|
configPath,
|
package/src/imprint/cron.ts
CHANGED
|
@@ -77,6 +77,7 @@ async function runOnce(
|
|
|
77
77
|
ladder: ConcreteBackend[],
|
|
78
78
|
assetRoot: string,
|
|
79
79
|
stealthCache: Map<string, StealthFetch>,
|
|
80
|
+
skipBootstrapSplice: boolean,
|
|
80
81
|
): Promise<ToolResult> {
|
|
81
82
|
const startedAt = new Date();
|
|
82
83
|
log(
|
|
@@ -90,6 +91,7 @@ async function runOnce(
|
|
|
90
91
|
params,
|
|
91
92
|
assetRoot,
|
|
92
93
|
stealthCache,
|
|
94
|
+
{ skipBootstrapSplice },
|
|
93
95
|
);
|
|
94
96
|
|
|
95
97
|
const elapsed = Date.now() - t0;
|
|
@@ -274,6 +276,7 @@ async function runCronImpl(opts: RunCronOptions): Promise<void> {
|
|
|
274
276
|
ladder,
|
|
275
277
|
assetRoot,
|
|
276
278
|
stealthCache,
|
|
279
|
+
Boolean(cached?.preferredOrder.length),
|
|
277
280
|
] as const;
|
|
278
281
|
|
|
279
282
|
if (opts.once) {
|
package/src/imprint/doctor.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import { join as pathJoin } from 'node:path';
|
|
8
8
|
import { findChromium } from './chromium.ts';
|
|
9
|
+
import { defaultHermesConfigPath } from './install.ts';
|
|
9
10
|
import { getProviderStatuses } from './llm.ts';
|
|
10
11
|
import { VERSION } from './version.ts';
|
|
11
12
|
|
|
@@ -63,9 +64,13 @@ function checkPlaywrightChromium(): CheckResult {
|
|
|
63
64
|
// useful as a separate line so users see whether the Playwright path
|
|
64
65
|
// specifically is set up (matters for stealth-fetch + playbook backends).
|
|
65
66
|
const cacheRoots = [
|
|
67
|
+
process.env.PLAYWRIGHT_BROWSERS_PATH,
|
|
68
|
+
process.env.HERMES_HOME
|
|
69
|
+
? pathJoin(process.env.HERMES_HOME, '.cache', 'ms-playwright')
|
|
70
|
+
: undefined,
|
|
66
71
|
pathJoin(homedir(), 'Library/Caches/ms-playwright'),
|
|
67
72
|
pathJoin(homedir(), '.cache/ms-playwright'),
|
|
68
|
-
];
|
|
73
|
+
].filter((root): root is string => Boolean(root));
|
|
69
74
|
for (const root of cacheRoots) {
|
|
70
75
|
if (!existsSync(root)) continue;
|
|
71
76
|
try {
|
|
@@ -84,7 +89,8 @@ function checkPlaywrightChromium(): CheckResult {
|
|
|
84
89
|
return {
|
|
85
90
|
name: 'Playwright Chromium',
|
|
86
91
|
ok: false,
|
|
87
|
-
detail:
|
|
92
|
+
detail:
|
|
93
|
+
'no chromium-* install under PLAYWRIGHT_BROWSERS_PATH, $HERMES_HOME/.cache/ms-playwright, ~/Library/Caches/ms-playwright, or ~/.cache/ms-playwright',
|
|
88
94
|
fix: 'run: bunx playwright install chromium (needed for stealth-fetch + playbook)',
|
|
89
95
|
};
|
|
90
96
|
}
|
|
@@ -209,7 +215,7 @@ function checkClaudeCode(): CheckResult {
|
|
|
209
215
|
}
|
|
210
216
|
|
|
211
217
|
function checkHermes(): CheckResult {
|
|
212
|
-
const configPath =
|
|
218
|
+
const configPath = defaultHermesConfigPath();
|
|
213
219
|
if (!existsSync(configPath)) {
|
|
214
220
|
return {
|
|
215
221
|
name: 'Hermes Agent',
|