local-operator-ui 0.25.0 → 0.25.2
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/out/main/index.js +206 -20
- package/package.json +5 -2
package/out/main/index.js
CHANGED
|
@@ -14216,6 +14216,51 @@ function registerDevDriverIPC(options) {
|
|
|
14216
14216
|
};
|
|
14217
14217
|
});
|
|
14218
14218
|
}
|
|
14219
|
+
const LAUNCHER_MISSES_BEFORE_GONE = 2;
|
|
14220
|
+
function createLauncherWatch(input) {
|
|
14221
|
+
const threshold = input.missesBeforeGone ?? LAUNCHER_MISSES_BEFORE_GONE;
|
|
14222
|
+
let misses = 0;
|
|
14223
|
+
let fired = false;
|
|
14224
|
+
let stopped = false;
|
|
14225
|
+
return {
|
|
14226
|
+
poll: () => {
|
|
14227
|
+
if (fired || stopped) return false;
|
|
14228
|
+
const parent = input.parentPid();
|
|
14229
|
+
const reparented = parent !== input.launcherPid;
|
|
14230
|
+
misses = reparented || !input.isAlive(input.launcherPid) ? misses + 1 : 0;
|
|
14231
|
+
if (misses < threshold) return false;
|
|
14232
|
+
fired = true;
|
|
14233
|
+
input.onLauncherGone({
|
|
14234
|
+
message: `the process that launched this run (pid ${input.launcherPid}) has exited`,
|
|
14235
|
+
detail: reparented ? `pid ${input.launcherPid} is gone (this process was reparented to pid ${parent})` : `pid ${input.launcherPid} is gone`
|
|
14236
|
+
});
|
|
14237
|
+
return true;
|
|
14238
|
+
},
|
|
14239
|
+
stop: () => {
|
|
14240
|
+
stopped = true;
|
|
14241
|
+
}
|
|
14242
|
+
};
|
|
14243
|
+
}
|
|
14244
|
+
function startLauncherWatch(input) {
|
|
14245
|
+
const watch = createLauncherWatch(input);
|
|
14246
|
+
const timer = setInterval(() => watch.poll(), input.intervalMs);
|
|
14247
|
+
timer.unref?.();
|
|
14248
|
+
return {
|
|
14249
|
+
poll: watch.poll,
|
|
14250
|
+
stop: () => {
|
|
14251
|
+
clearInterval(timer);
|
|
14252
|
+
watch.stop();
|
|
14253
|
+
}
|
|
14254
|
+
};
|
|
14255
|
+
}
|
|
14256
|
+
function isProcessAlive(pid) {
|
|
14257
|
+
try {
|
|
14258
|
+
process.kill(pid, 0);
|
|
14259
|
+
return true;
|
|
14260
|
+
} catch (error) {
|
|
14261
|
+
return error.code !== "ESRCH";
|
|
14262
|
+
}
|
|
14263
|
+
}
|
|
14219
14264
|
const lastPickedDirectory = /* @__PURE__ */ new Map();
|
|
14220
14265
|
function withRememberedDirectory(kind, options, fallbackDirectory) {
|
|
14221
14266
|
if (options.defaultPath) return options;
|
|
@@ -17108,15 +17153,35 @@ const AGENT_LAUNCH_FLAGS = [
|
|
|
17108
17153
|
"--user-data-dir",
|
|
17109
17154
|
"--remote-debugging-port"
|
|
17110
17155
|
];
|
|
17156
|
+
const LAUNCHER_KEEP_ALIVE_ENV = "LOCAL_OPERATOR_UI_HEADLESS_KEEP_ALIVE";
|
|
17157
|
+
const LAUNCHER_POLL_INTERVAL_MS = 2e3;
|
|
17158
|
+
const LAUNCHER_EXIT_DEADLINE_MS = 1e4;
|
|
17111
17159
|
const WINDOW_MIN_WIDTH = 800;
|
|
17112
17160
|
const WINDOW_MIN_HEIGHT = 600;
|
|
17113
17161
|
const WINDOW_MAX_EDGE = 16384;
|
|
17114
17162
|
const DEFAULT_WINDOW_WIDTH = 1380;
|
|
17115
17163
|
const DEFAULT_WINDOW_HEIGHT = 900;
|
|
17116
17164
|
const WINDOW_BEHAVIOUR = {
|
|
17117
|
-
normal: {
|
|
17118
|
-
|
|
17119
|
-
|
|
17165
|
+
normal: {
|
|
17166
|
+
show: "focus",
|
|
17167
|
+
focusable: true,
|
|
17168
|
+
backgroundThrottling: true,
|
|
17169
|
+
hideDock: false
|
|
17170
|
+
},
|
|
17171
|
+
inactive: {
|
|
17172
|
+
show: "inactive",
|
|
17173
|
+
focusable: true,
|
|
17174
|
+
backgroundThrottling: false,
|
|
17175
|
+
// Visible on purpose, so it keeps its tile: a run somebody wants to
|
|
17176
|
+
// watch has to be findable in the Dock.
|
|
17177
|
+
hideDock: false
|
|
17178
|
+
},
|
|
17179
|
+
headless: {
|
|
17180
|
+
show: "never",
|
|
17181
|
+
focusable: false,
|
|
17182
|
+
backgroundThrottling: false,
|
|
17183
|
+
hideDock: true
|
|
17184
|
+
}
|
|
17120
17185
|
};
|
|
17121
17186
|
function readFlag(argv, name) {
|
|
17122
17187
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -17154,8 +17219,11 @@ function resolveWindowLaunchPlan(input = {}) {
|
|
|
17154
17219
|
const modeValue = modeFlag.found ? modeFlag.value : env[WINDOW_MODE_ENV];
|
|
17155
17220
|
const modeRaw = typeof modeValue === "string" && modeValue.trim() === "" ? void 0 : modeValue;
|
|
17156
17221
|
const parsedMode = parseWindowMode(modeRaw);
|
|
17157
|
-
const
|
|
17158
|
-
const
|
|
17222
|
+
const unnamed = !modeFlag.found && modeRaw === void 0;
|
|
17223
|
+
const agentFlag = unnamed ? AGENT_LAUNCH_FLAGS.find((flag) => readFlag(argv, flag).found) : void 0;
|
|
17224
|
+
const platform = input.platform ?? process.platform;
|
|
17225
|
+
const driven = unnamed && agentFlag === void 0 && input.packaged === false && platform !== "win32" && input.stdinIsTTY !== true && input.stdoutIsTTY !== true;
|
|
17226
|
+
const assumed = agentFlag ? `${agentFlag} marks an agent-driven launch, and no window mode was named` : driven ? "the launch is not a packaged app and has no terminal on either stream, so it is a driven run rather than the operator using the app" : null;
|
|
17159
17227
|
if (modeFlag.found && modeFlag.value === void 0) {
|
|
17160
17228
|
problems.push(
|
|
17161
17229
|
`${WINDOW_MODE_FLAG} needs a value: ${WINDOW_MODES.join("|")}`
|
|
@@ -17206,10 +17274,45 @@ function resolveWindowLaunchPlan(input = {}) {
|
|
|
17206
17274
|
problems
|
|
17207
17275
|
};
|
|
17208
17276
|
}
|
|
17209
|
-
|
|
17277
|
+
const KEEP_ALIVE_VALUES = ["1", "true", "yes", "on"];
|
|
17278
|
+
function resolveLauncherWatchPlan(input) {
|
|
17279
|
+
const env = input.env ?? {};
|
|
17280
|
+
const keepAlive = (env[LAUNCHER_KEEP_ALIVE_ENV] ?? "").trim().toLowerCase();
|
|
17281
|
+
if (KEEP_ALIVE_VALUES.includes(keepAlive)) {
|
|
17282
|
+
return {
|
|
17283
|
+
watch: false,
|
|
17284
|
+
launcherPid: null,
|
|
17285
|
+
reason: `${LAUNCHER_KEEP_ALIVE_ENV}=${keepAlive} was set, so this run outlives its launcher`
|
|
17286
|
+
};
|
|
17287
|
+
}
|
|
17288
|
+
if (input.mode !== "headless") {
|
|
17289
|
+
return {
|
|
17290
|
+
watch: false,
|
|
17291
|
+
launcherPid: null,
|
|
17292
|
+
reason: `window mode ${input.mode} is not launcher-bound: a person can close it`
|
|
17293
|
+
};
|
|
17294
|
+
}
|
|
17295
|
+
if (!Number.isInteger(input.launcherPid) || input.launcherPid <= 1) {
|
|
17296
|
+
return {
|
|
17297
|
+
watch: false,
|
|
17298
|
+
launcherPid: null,
|
|
17299
|
+
reason: "headless run already detached (no launcher to outlive)"
|
|
17300
|
+
};
|
|
17301
|
+
}
|
|
17302
|
+
return {
|
|
17303
|
+
watch: true,
|
|
17304
|
+
launcherPid: input.launcherPid,
|
|
17305
|
+
reason: `headless run launched by pid ${input.launcherPid}; it quits when that process goes`
|
|
17306
|
+
};
|
|
17307
|
+
}
|
|
17308
|
+
function describeWindowLaunch(plan, platform = process.platform) {
|
|
17210
17309
|
const behaviour = plan.show === "never" ? "window created and never shown" : plan.show === "inactive" ? "window shown without activating the app" : "window shown and focused";
|
|
17211
|
-
const assumption = plan.assumed ? ` (assumed: ${plan.assumed})` : "";
|
|
17212
|
-
|
|
17310
|
+
const assumption = plan.assumed ? ` (mode assumed: ${plan.assumed})` : "";
|
|
17311
|
+
const extras = [
|
|
17312
|
+
plan.hideDock && platform === "darwin" ? "no Dock tile" : null
|
|
17313
|
+
].filter((part) => part !== null);
|
|
17314
|
+
const suffix = extras.length === 0 ? "" : `, ${extras.join(", ")}`;
|
|
17315
|
+
return `window mode ${plan.mode}: ${plan.width}x${plan.height}, ${behaviour}, page throttling ${plan.backgroundThrottling ? "on" : "off"}${suffix}${assumption}`;
|
|
17213
17316
|
}
|
|
17214
17317
|
const BASE64_FILE_EXTENSIONS = ["csv", "tsv", "xls", "xlsx", "ods"];
|
|
17215
17318
|
const resolveUserPath = (filePath, cwd) => {
|
|
@@ -17220,10 +17323,68 @@ const resolveUserPath = (filePath, cwd) => {
|
|
|
17220
17323
|
return path$1.join(cwd.startsWith("~/") ? resolveUserPath(cwd) : cwd, filePath);
|
|
17221
17324
|
return filePath;
|
|
17222
17325
|
};
|
|
17326
|
+
const windowLaunch = resolveWindowLaunchPlan({
|
|
17327
|
+
env: launchEnv,
|
|
17328
|
+
argv: process.argv,
|
|
17329
|
+
/*
|
|
17330
|
+
* The facts that let a flagless launch be recognised as a run rather than as
|
|
17331
|
+
* the operator's app — see the `driven` block in window-mode.ts for why the
|
|
17332
|
+
* shape is sound and why it cannot touch the shipped app.
|
|
17333
|
+
*
|
|
17334
|
+
* `app.isPackaged` is read here, at module load, rather than inside the
|
|
17335
|
+
* resolver: it describes the LAUNCH (the installed `.app` is packaged, a
|
|
17336
|
+
* checkout and the npm CLI are not), and the resolver stays free of Electron
|
|
17337
|
+
* so its rules can be tested as arithmetic instead of by booting an app.
|
|
17338
|
+
*
|
|
17339
|
+
* `process.stdin`/`process.stdout` are read as the process was STARTED with
|
|
17340
|
+
* them, for the same reason `launchEnv` exists: a terminal attached later
|
|
17341
|
+
* cannot make this a person's launch.
|
|
17342
|
+
*/
|
|
17343
|
+
packaged: electron.app.isPackaged,
|
|
17344
|
+
stdinIsTTY: process.stdin.isTTY,
|
|
17345
|
+
stdoutIsTTY: process.stdout.isTTY,
|
|
17346
|
+
platform: process.platform
|
|
17347
|
+
});
|
|
17348
|
+
for (const problem of windowLaunch.problems) {
|
|
17349
|
+
logger.warn(`[window-mode] ${problem}`, LogFileType.BACKEND);
|
|
17350
|
+
console.log(`[window-mode] ${problem}`);
|
|
17351
|
+
}
|
|
17352
|
+
if (windowLaunch.mode !== "normal") {
|
|
17353
|
+
console.log(`[window-mode] ${describeWindowLaunch(windowLaunch)}`);
|
|
17354
|
+
}
|
|
17355
|
+
let headlessExitDeadline = null;
|
|
17356
|
+
function armHeadlessExitDeadline(why) {
|
|
17357
|
+
if (headlessExitDeadline) return;
|
|
17358
|
+
const deadline2 = setTimeout(() => {
|
|
17359
|
+
const line = `${why}; the quit did not finish within ${LAUNCHER_EXIT_DEADLINE_MS} ms, exiting`;
|
|
17360
|
+
logger.error(`[window-mode] ${line}`, LogFileType.BACKEND);
|
|
17361
|
+
console.log(`[window-mode] ${line}`);
|
|
17362
|
+
electron.app.exit(0);
|
|
17363
|
+
}, LAUNCHER_EXIT_DEADLINE_MS);
|
|
17364
|
+
deadline2.unref();
|
|
17365
|
+
headlessExitDeadline = deadline2;
|
|
17366
|
+
}
|
|
17367
|
+
function endHeadlessRun(why, detail) {
|
|
17368
|
+
logger.info(`[window-mode] ${why}; quitting`, LogFileType.BACKEND);
|
|
17369
|
+
console.log(`[window-mode] ${why}; quitting`);
|
|
17370
|
+
if (detail) {
|
|
17371
|
+
logger.debug(
|
|
17372
|
+
`[window-mode] launcher probe: ${detail}`,
|
|
17373
|
+
LogFileType.BACKEND
|
|
17374
|
+
);
|
|
17375
|
+
}
|
|
17376
|
+
armHeadlessExitDeadline(why);
|
|
17377
|
+
electron.app.quit();
|
|
17378
|
+
}
|
|
17379
|
+
let launcherWatch = null;
|
|
17223
17380
|
electron.app.setName("Local Operator");
|
|
17224
17381
|
const image = electron.nativeImage.createFromPath(icon);
|
|
17225
17382
|
if (process.platform === "darwin" && electron.app.dock) {
|
|
17226
|
-
|
|
17383
|
+
if (windowLaunch.hideDock) {
|
|
17384
|
+
electron.app.dock.hide();
|
|
17385
|
+
} else {
|
|
17386
|
+
electron.app.dock.setIcon(image);
|
|
17387
|
+
}
|
|
17227
17388
|
}
|
|
17228
17389
|
const posthogClient = new posthogNode.PostHog(backendConfig.VITE_PUBLIC_POSTHOG_KEY, {
|
|
17229
17390
|
host: backendConfig.VITE_PUBLIC_POSTHOG_HOST,
|
|
@@ -17545,17 +17706,6 @@ const reportBackendFailure = (message, fileType) => {
|
|
|
17545
17706
|
}
|
|
17546
17707
|
electron.dialog.showErrorBox("Backend Error", message);
|
|
17547
17708
|
};
|
|
17548
|
-
const windowLaunch = resolveWindowLaunchPlan({
|
|
17549
|
-
env: launchEnv,
|
|
17550
|
-
argv: process.argv
|
|
17551
|
-
});
|
|
17552
|
-
for (const problem of windowLaunch.problems) {
|
|
17553
|
-
logger.warn(`[window-mode] ${problem}`, LogFileType.BACKEND);
|
|
17554
|
-
console.log(`[window-mode] ${problem}`);
|
|
17555
|
-
}
|
|
17556
|
-
if (windowLaunch.mode !== "normal") {
|
|
17557
|
-
console.log(`[window-mode] ${describeWindowLaunch(windowLaunch)}`);
|
|
17558
|
-
}
|
|
17559
17709
|
const devDriverArming = resolveDevDriverArming({
|
|
17560
17710
|
env: launchEnv,
|
|
17561
17711
|
windowMode: windowLaunch.mode
|
|
@@ -17656,6 +17806,29 @@ electron.app.whenReady().then(async () => {
|
|
|
17656
17806
|
electron.app.exit(0);
|
|
17657
17807
|
return;
|
|
17658
17808
|
}
|
|
17809
|
+
const launcher = resolveLauncherWatchPlan({
|
|
17810
|
+
mode: windowLaunch.mode,
|
|
17811
|
+
launcherPid: process.ppid,
|
|
17812
|
+
env: launchEnv
|
|
17813
|
+
});
|
|
17814
|
+
logger.info(`[window-mode] ${launcher.reason}`, LogFileType.BACKEND);
|
|
17815
|
+
if (windowLaunch.mode !== "normal") {
|
|
17816
|
+
console.log(`[window-mode] ${launcher.reason}`);
|
|
17817
|
+
}
|
|
17818
|
+
if (launcher.watch && launcher.launcherPid !== null) {
|
|
17819
|
+
launcherWatch = startLauncherWatch({
|
|
17820
|
+
launcherPid: launcher.launcherPid,
|
|
17821
|
+
intervalMs: LAUNCHER_POLL_INTERVAL_MS,
|
|
17822
|
+
isAlive: isProcessAlive,
|
|
17823
|
+
parentPid: () => process.ppid,
|
|
17824
|
+
onLauncherGone: ({ message, detail }) => endHeadlessRun(message, detail)
|
|
17825
|
+
});
|
|
17826
|
+
}
|
|
17827
|
+
if (windowLaunch.mode === "headless") {
|
|
17828
|
+
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
17829
|
+
process.on(signal, () => endHeadlessRun(`received ${signal}`));
|
|
17830
|
+
}
|
|
17831
|
+
}
|
|
17659
17832
|
electron.app.on("browser-window-created", (_, window) => {
|
|
17660
17833
|
utils.optimizer.watchWindowShortcuts(window);
|
|
17661
17834
|
});
|
|
@@ -18255,6 +18428,15 @@ electron.app.whenReady().then(async () => {
|
|
|
18255
18428
|
electron.app.quit();
|
|
18256
18429
|
});
|
|
18257
18430
|
electron.app.on("window-all-closed", () => {
|
|
18431
|
+
if (windowLaunch.mode === "headless") {
|
|
18432
|
+
logger.info(
|
|
18433
|
+
"[window-mode] all windows closed in a headless run; quitting",
|
|
18434
|
+
LogFileType.BACKEND
|
|
18435
|
+
);
|
|
18436
|
+
console.log("[window-mode] all windows closed in a headless run; quitting");
|
|
18437
|
+
electron.app.quit();
|
|
18438
|
+
return;
|
|
18439
|
+
}
|
|
18258
18440
|
if (process.platform === "darwin") {
|
|
18259
18441
|
if (activeUpdateService?.quitForInFlightInstall("last window closed")) {
|
|
18260
18442
|
electron.app.quit();
|
|
@@ -18319,6 +18501,10 @@ const holdQuitForSessionCookieSnapshot = createSessionCookieQuitHold({
|
|
|
18319
18501
|
electron.app.on("before-quit", async (event) => {
|
|
18320
18502
|
if (await holdQuitForSessionCookieSnapshot(event)) return;
|
|
18321
18503
|
logger.info("App is about to quit", LogFileType.BACKEND);
|
|
18504
|
+
if (windowLaunch.mode === "headless") {
|
|
18505
|
+
launcherWatch?.stop();
|
|
18506
|
+
armHeadlessExitDeadline("the app is quitting");
|
|
18507
|
+
}
|
|
18322
18508
|
electron.globalShortcut.unregisterAll();
|
|
18323
18509
|
activeUpdateService?.quitForInFlightInstall("app quit");
|
|
18324
18510
|
void stopBrowserHost();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-operator-ui",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.2",
|
|
4
4
|
"productName": "Local Operator",
|
|
5
5
|
"description": "User interface for AI agent assistants that run Python code safely on your device through an intuitive chat interface. Supports local and cloud LLM models with built-in security features.",
|
|
6
6
|
"main": "./out/main/index.js",
|
|
@@ -64,6 +64,8 @@
|
|
|
64
64
|
"postinstall": "node ./bin/postinstall.js",
|
|
65
65
|
"lint": "pnpm biome check src bin scripts/linux-sandbox.test.mjs",
|
|
66
66
|
"lint:fix": "pnpm biome check --write src bin scripts/linux-sandbox.test.mjs",
|
|
67
|
+
"//lint:scripts": "`pnpm lint` names the paths it checks by hand, so the whole of `scripts/` - the proof harnesses, evidence rigs and release gates - sat outside the lint and formatter contracts, and a formatter error rode into a merged pull request green. This gate runs the same `biome check` over the `scripts/` files a change touches against a base ref (`origin/main` locally, the merge commit's first parent in CI), so a violation cannot ride in with the diff that introduces it, while the tree's 106 pre-existing offenders are burnt down file by file rather than turned red at once. Widening `pnpm lint` to name `scripts/` outright is the follow-up once that backlog is gone; scripts/check-scripts-lint.mjs holds the measurements and the full rationale.",
|
|
68
|
+
"lint:scripts": "node scripts/check-scripts-lint.mjs",
|
|
67
69
|
"format": "pnpm biome format src",
|
|
68
70
|
"format:fix": "pnpm biome format --write src",
|
|
69
71
|
"prepack": "pnpm run build:npm",
|
|
@@ -71,7 +73,8 @@
|
|
|
71
73
|
"check-types:renderer": "tsc --noEmit -p tsconfig.app.json",
|
|
72
74
|
"check-types": "pnpm run check-types:main && pnpm run check-types:renderer",
|
|
73
75
|
"test:session-cookies": "node scripts/session-cookie-electron.test.mjs",
|
|
74
|
-
|
|
76
|
+
|
|
77
|
+
"test:desktop": "node scripts/run-desktop-tests.mjs scripts/slash-token.test.mjs scripts/slash-rank.test.mjs scripts/slash-submit.test.mjs scripts/slash-contract.test.mjs scripts/slash-row-format.test.mjs scripts/owned-serve-lifecycle.test.mjs scripts/launcher-watch.test.mjs scripts/linux-sandbox.test.mjs scripts/capture-evidence.test.mjs scripts/desktop-contract.test.mjs scripts/transcript-reducer.test.mjs scripts/transcript-paging.test.mjs scripts/provider-state.test.mjs scripts/stale-agent.test.mjs scripts/desktop-renderer-transport.test.mjs scripts/desktop-proxy.test.mjs scripts/session-stream-token.test.mjs scripts/session-load-recovery.test.mjs scripts/provider-grid-error-copy.test.mjs scripts/settings-time-to-error.test.mjs scripts/backend-error-surfaces.test.mjs scripts/canonical-chat.test.mjs scripts/stale-caption.test.mjs scripts/desktop-watch-lease.test.mjs scripts/session-switch.test.mjs scripts/chat-search.test.mjs scripts/palette-search.test.mjs scripts/palette-shortcut.test.mjs scripts/palette-contract.test.mjs scripts/palette-panel-request.test.mjs scripts/run-detail-model.test.mjs scripts/subagent-pulse.test.mjs scripts/chat-title.test.mjs scripts/clear-search.test.mjs scripts/tool-row.test.mjs scripts/attachment-url.test.mjs scripts/desktop-notifier.test.mjs scripts/launch-session.test.mjs scripts/desktop-feed.test.mjs scripts/viewer-record.test.mjs scripts/desktop-stream.test.mjs scripts/python-bytecode-cache.test.mjs scripts/update-affirmation.test.mjs scripts/update-robustness.test.mjs scripts/prune-python-resource.test.mjs scripts/managed-python.test.mjs scripts/usage-view.test.mjs scripts/usage-container.test.mjs scripts/session-status.test.mjs scripts/composer-readings.test.mjs scripts/composer-tabs.test.mjs scripts/ask-options.test.mjs scripts/window-mode.test.mjs scripts/evidence-manifest.test.mjs scripts/canonical-notice.test.mjs scripts/evidence-run-guard.test.mjs scripts/desktop-test-concurrency.test.mjs scripts/run-desktop-tests.test.mjs scripts/mcp-foreign-config-origin.test.mjs scripts/mentioned-files.test.mjs scripts/picker-feedback.test.mjs scripts/panel-chart-motion.test.mjs scripts/panel-info-credentials.test.mjs scripts/warm-session.test.mjs scripts/submit-latency.test.mjs scripts/completion-view-ack.test.mjs scripts/echo-delivery.test.mjs scripts/check-runtime-deps.test.mjs scripts/new-chat-row.test.mjs scripts/new-chat-row-evidence.test.mjs scripts/reconnect-page-gap.test.mjs scripts/browser-host.test.mjs scripts/check-vendored.test.mjs scripts/chat-session-status.test.mjs scripts/draft-selection.test.mjs scripts/toast-lifetime.test.mjs scripts/storybook-query-fixture.test.mjs scripts/mcp-list-query.test.mjs scripts/mcp-target-resolution.test.mjs scripts/mcp-auth-surface.test.mjs scripts/mcp-failure-routing.test.mjs scripts/daemon-discovery.test.mjs scripts/daemon-health-state.test.mjs scripts/daemon-observation.test.mjs scripts/session-cookies.test.mjs scripts/move-session.test.mjs scripts/frontend-replace.test.mjs scripts/browser-chrome.test.mjs scripts/dev-driver-gate.test.mjs scripts/logger-log-dir.test.mjs scripts/draft-splash.test.mjs scripts/suggestion-stack.test.mjs scripts/suggestion-stack-react.test.mjs scripts/notifications-off.test.mjs scripts/notification-launch.test.mjs scripts/notification-spawn-sites.test.mjs scripts/chrome-keychain.test.mjs scripts/interrupt-control.test.mjs",
|
|
75
78
|
"check-themes": "node scripts/generate-theme-css.mjs --check && node scripts/contrast-contract.mjs",
|
|
76
79
|
"check-vendored": "node scripts/check-vendored.mjs",
|
|
77
80
|
"check-evidence": "node scripts/check-evidence.mjs",
|