@rynx-ai/daemon 0.1.11-beta.2 → 0.1.11-beta.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/bundled-plugins/plugins/lark/dist/runtime.js +6 -7
- package/bundled-plugins/plugins/lark/dist/settings.js +6 -4
- package/bundled-plugins/plugins/lark/package.json +1 -1
- package/dist/browser-artifact-management.d.ts +4 -1
- package/dist/browser-artifact-management.js +18 -10
- package/dist/browser-artifacts.d.ts +1 -0
- package/dist/browser-artifacts.js +1 -0
- package/dist/chrome-for-testing-runtime.d.ts +27 -0
- package/dist/chrome-for-testing-runtime.js +193 -0
- package/dist/chrome-for-testing-store.d.ts +2 -0
- package/dist/chrome-for-testing-store.js +9 -0
- package/dist/daemon-server.d.ts +4 -1
- package/dist/daemon-server.js +137 -32
- package/dist/db.js +2 -0
- package/dist/entry-path.d.ts +3 -0
- package/dist/entry-path.js +12 -0
- package/dist/index-daemon.js +3 -87
- package/dist/lifecycle.d.ts +12 -0
- package/dist/lifecycle.js +16 -0
- package/dist/maintenance-lease.d.ts +39 -0
- package/dist/maintenance-lease.js +250 -0
- package/dist/plugin-host-rpc.d.ts +19 -1
- package/dist/plugin-host-rpc.js +163 -23
- package/dist/plugin-installer.js +59 -0
- package/dist/plugin-supervisor.d.ts +5 -0
- package/dist/plugin-supervisor.js +218 -29
- package/dist/pm2.d.ts +11 -2
- package/dist/pm2.js +116 -19
- package/dist/process.d.ts +12 -0
- package/dist/process.js +91 -0
- package/dist/session-launch-operations.d.ts +10 -1
- package/dist/session-launch-operations.js +15 -6
- package/dist/setup-service.d.ts +20 -0
- package/dist/setup-service.js +38 -0
- package/package.json +33 -20
- package/dist/browser-cli-args.d.ts +0 -20
- package/dist/browser-cli-args.js +0 -100
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -550
- package/dist/control-client.d.ts +0 -122
- package/dist/control-client.js +0 -855
- package/dist/control-endpoint.d.ts +0 -14
- package/dist/control-endpoint.js +0 -124
- package/dist/desktop-browser-host-client.d.ts +0 -35
- package/dist/desktop-browser-host-client.js +0 -415
- package/dist/setup.d.ts +0 -20
- package/dist/setup.js +0 -386
- package/dist/update.d.ts +0 -25
- package/dist/update.js +0 -133
|
@@ -126032,9 +126032,6 @@ function isAgentRuntimeId(value) {
|
|
|
126032
126032
|
return value === "codex" || value === "traex" || value === "claude";
|
|
126033
126033
|
}
|
|
126034
126034
|
function normalizeRuntimeId(value) {
|
|
126035
|
-
if (value === "traecli") {
|
|
126036
|
-
return "traex";
|
|
126037
|
-
}
|
|
126038
126035
|
return isAgentRuntimeId(value) ? value : void 0;
|
|
126039
126036
|
}
|
|
126040
126037
|
var RUNTIME_PROFILES = {
|
|
@@ -126100,7 +126097,7 @@ var envSchema = external_exports.object({
|
|
|
126100
126097
|
* `/runtime` card. `codex` keeps the legacy behaviour; `traex` targets the
|
|
126101
126098
|
* ByteDance Codex fork. Per-conversation overrides are stored separately.
|
|
126102
126099
|
*/
|
|
126103
|
-
|
|
126100
|
+
DEFAULT_RUNTIME: external_exports.enum(["codex", "traex", "claude"]).default("codex"),
|
|
126104
126101
|
/**
|
|
126105
126102
|
* Default declarative agent (a spec under `~/.rynx/agents/<name>.json`) for
|
|
126106
126103
|
* conversations that haven't selected one via `/agent`. Empty ⇒ no default;
|
|
@@ -126114,10 +126111,12 @@ var envSchema = external_exports.object({
|
|
|
126114
126111
|
* to the latest balanced Sonnet; per-conversation `/model` overrides still apply.
|
|
126115
126112
|
*/
|
|
126116
126113
|
CLAUDE_MODEL: external_exports.string().optional().transform((value) => value?.trim() || "claude-sonnet-4-6"),
|
|
126117
|
-
/**
|
|
126114
|
+
/** Codex-lineage approval default (legacy `CODEX_APPROVAL_POLICY` is honoured);
|
|
126115
|
+
* also helps derive Claude's unset permission posture. */
|
|
126118
126116
|
AGENT_APPROVAL_POLICY: external_exports.enum(["never", "on-failure", "on-request", "untrusted"]).default("never"),
|
|
126119
126117
|
CODEX_MODEL: external_exports.string().optional().transform((value) => value?.trim() || "gpt-5.5"),
|
|
126120
|
-
/**
|
|
126118
|
+
/** Codex-lineage sandbox default (legacy `CODEX_SANDBOX` is honoured); paired
|
|
126119
|
+
* with approval to derive Claude's unset permission posture. */
|
|
126121
126120
|
AGENT_SANDBOX: external_exports.enum(["read-only", "workspace-write", "danger-full-access"]).default("danger-full-access"),
|
|
126122
126121
|
/** Shared by both runtimes (codex + traex). Legacy `CODEX_FULL_AUTO` still honoured. */
|
|
126123
126122
|
AGENT_FULL_AUTO: external_exports.union([external_exports.boolean(), external_exports.string()]).optional().transform((value) => {
|
|
@@ -126352,7 +126351,7 @@ var CHROME_INSPECTION_ERROR_MAX_BYTES = 4 * 1024;
|
|
|
126352
126351
|
import path from "node:path";
|
|
126353
126352
|
var SESSION_EXECUTION_SNAPSHOT_VERSION = 1;
|
|
126354
126353
|
var nonEmptyString = external_exports.string().trim().min(1);
|
|
126355
|
-
var absolutePath =
|
|
126354
|
+
var absolutePath = external_exports.string().min(1).refine((value) => Boolean(value.trim()) && path.isAbsolute(value) && path.normalize(value) === value, "path must be normalized and absolute");
|
|
126356
126355
|
var sessionWorkspaceSnapshotSchema = external_exports.object({
|
|
126357
126356
|
cwd: absolutePath,
|
|
126358
126357
|
allowedDirs: external_exports.array(absolutePath).min(1)
|
|
@@ -126033,7 +126033,7 @@ var envSchema = external_exports.object({
|
|
|
126033
126033
|
* `/runtime` card. `codex` keeps the legacy behaviour; `traex` targets the
|
|
126034
126034
|
* ByteDance Codex fork. Per-conversation overrides are stored separately.
|
|
126035
126035
|
*/
|
|
126036
|
-
|
|
126036
|
+
DEFAULT_RUNTIME: external_exports.enum(["codex", "traex", "claude"]).default("codex"),
|
|
126037
126037
|
/**
|
|
126038
126038
|
* Default declarative agent (a spec under `~/.rynx/agents/<name>.json`) for
|
|
126039
126039
|
* conversations that haven't selected one via `/agent`. Empty ⇒ no default;
|
|
@@ -126047,10 +126047,12 @@ var envSchema = external_exports.object({
|
|
|
126047
126047
|
* to the latest balanced Sonnet; per-conversation `/model` overrides still apply.
|
|
126048
126048
|
*/
|
|
126049
126049
|
CLAUDE_MODEL: external_exports.string().optional().transform((value) => value?.trim() || "claude-sonnet-4-6"),
|
|
126050
|
-
/**
|
|
126050
|
+
/** Codex-lineage approval default (legacy `CODEX_APPROVAL_POLICY` is honoured);
|
|
126051
|
+
* also helps derive Claude's unset permission posture. */
|
|
126051
126052
|
AGENT_APPROVAL_POLICY: external_exports.enum(["never", "on-failure", "on-request", "untrusted"]).default("never"),
|
|
126052
126053
|
CODEX_MODEL: external_exports.string().optional().transform((value) => value?.trim() || "gpt-5.5"),
|
|
126053
|
-
/**
|
|
126054
|
+
/** Codex-lineage sandbox default (legacy `CODEX_SANDBOX` is honoured); paired
|
|
126055
|
+
* with approval to derive Claude's unset permission posture. */
|
|
126054
126056
|
AGENT_SANDBOX: external_exports.enum(["read-only", "workspace-write", "danger-full-access"]).default("danger-full-access"),
|
|
126055
126057
|
/** Shared by both runtimes (codex + traex). Legacy `CODEX_FULL_AUTO` still honoured. */
|
|
126056
126058
|
AGENT_FULL_AUTO: external_exports.union([external_exports.boolean(), external_exports.string()]).optional().transform((value) => {
|
|
@@ -126217,7 +126219,7 @@ var CHROME_INSPECTION_ERROR_MAX_BYTES = 4 * 1024;
|
|
|
126217
126219
|
import path from "node:path";
|
|
126218
126220
|
var SESSION_EXECUTION_SNAPSHOT_VERSION = 1;
|
|
126219
126221
|
var nonEmptyString = external_exports.string().trim().min(1);
|
|
126220
|
-
var absolutePath =
|
|
126222
|
+
var absolutePath = external_exports.string().min(1).refine((value) => Boolean(value.trim()) && path.isAbsolute(value) && path.normalize(value) === value, "path must be normalized and absolute");
|
|
126221
126223
|
var sessionWorkspaceSnapshotSchema = external_exports.object({
|
|
126222
126224
|
cwd: absolutePath,
|
|
126223
126225
|
allowedDirs: external_exports.array(absolutePath).min(1)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { DaemonBrowserArtifactCleanResult, DaemonBrowserArtifactInstallInput, DaemonBrowserArtifactInstallResult, DaemonBrowserArtifactUpdateInput, DaemonBrowserArtifactVersionResult } from "@rynx-ai/protocol/control";
|
|
2
2
|
import { type ChromeForTestingReleaseSelector, type ResolvedChromeForTestingRelease } from "./chrome-for-testing-release-resolver.js";
|
|
3
|
-
import { type ChromeForTestingArtifactStore } from "./chrome-for-testing-store.js";
|
|
3
|
+
import { type InstalledChromeForTestingBuild, type ChromeForTestingArtifactStore } from "./chrome-for-testing-store.js";
|
|
4
|
+
import { type EnsureChromeForTestingRuntimeOptions } from "./chrome-for-testing-runtime.js";
|
|
4
5
|
export interface BrowserArtifactManagementContext {
|
|
5
6
|
signal?: AbortSignal;
|
|
7
|
+
onProgress?: (message: string) => void;
|
|
6
8
|
}
|
|
7
9
|
export interface BrowserArtifactManagementService {
|
|
8
10
|
install(input: DaemonBrowserArtifactInstallInput, context?: BrowserArtifactManagementContext): Promise<DaemonBrowserArtifactInstallResult>;
|
|
@@ -15,6 +17,7 @@ export interface BrowserArtifactManagementServiceOptions {
|
|
|
15
17
|
signal?: AbortSignal;
|
|
16
18
|
}) => Promise<ResolvedChromeForTestingRelease>;
|
|
17
19
|
createStore?: () => ChromeForTestingArtifactStore;
|
|
20
|
+
ensureRuntime?: (build: InstalledChromeForTestingBuild, options?: EnsureChromeForTestingRuntimeOptions) => Promise<void>;
|
|
18
21
|
}
|
|
19
22
|
/** Daemon-owned Browser artifact operations exposed through local management. */
|
|
20
23
|
export declare function createBrowserArtifactManagementService(options?: BrowserArtifactManagementServiceOptions): BrowserArtifactManagementService;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveChromeForTestingRelease, } from "./chrome-for-testing-release-resolver.js";
|
|
2
2
|
import { createChromeForTestingArtifactStore, } from "./chrome-for-testing-store.js";
|
|
3
|
+
import { ensureChromeForTestingRuntime, } from "./chrome-for-testing-runtime.js";
|
|
3
4
|
const MAX_PROGRESS_MESSAGES = 64;
|
|
4
5
|
const MAX_PROGRESS_MESSAGE_BYTES = 2 * 1024;
|
|
5
6
|
const MAX_PROGRESS_TOTAL_BYTES = 32 * 1024;
|
|
@@ -7,6 +8,7 @@ const MAX_PROGRESS_TOTAL_BYTES = 32 * 1024;
|
|
|
7
8
|
export function createBrowserArtifactManagementService(options = {}) {
|
|
8
9
|
const resolveRelease = options.resolveRelease ?? resolveChromeForTestingRelease;
|
|
9
10
|
const createStore = options.createStore ?? createChromeForTestingArtifactStore;
|
|
11
|
+
const ensureRuntime = options.ensureRuntime ?? ensureChromeForTestingRuntime;
|
|
10
12
|
let store;
|
|
11
13
|
const artifactStore = () => store ??= createStore();
|
|
12
14
|
const install = async (input, context = {}) => {
|
|
@@ -19,18 +21,24 @@ export function createBrowserArtifactManagementService(options = {}) {
|
|
|
19
21
|
const resolved = await resolveRelease(selector, { signal: context.signal });
|
|
20
22
|
const messages = [];
|
|
21
23
|
let messageBytes = 0;
|
|
24
|
+
const onProgress = (message) => {
|
|
25
|
+
context.onProgress?.(message);
|
|
26
|
+
if (messages.length >= MAX_PROGRESS_MESSAGES)
|
|
27
|
+
return;
|
|
28
|
+
const bounded = boundedUtf8(message, MAX_PROGRESS_MESSAGE_BYTES);
|
|
29
|
+
const bytes = Buffer.byteLength(bounded, "utf8");
|
|
30
|
+
if (messageBytes + bytes > MAX_PROGRESS_TOTAL_BYTES)
|
|
31
|
+
return;
|
|
32
|
+
messageBytes += bytes;
|
|
33
|
+
messages.push(bounded);
|
|
34
|
+
};
|
|
22
35
|
const installed = await artifactStore().installResolved(resolved, {
|
|
23
36
|
signal: context.signal,
|
|
24
|
-
onProgress
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (messageBytes + bytes > MAX_PROGRESS_TOTAL_BYTES)
|
|
30
|
-
return;
|
|
31
|
-
messageBytes += bytes;
|
|
32
|
-
messages.push(bounded);
|
|
33
|
-
},
|
|
37
|
+
onProgress,
|
|
38
|
+
verifyBeforeActivate: (build) => ensureRuntime(build, {
|
|
39
|
+
signal: context.signal,
|
|
40
|
+
onProgress,
|
|
41
|
+
}),
|
|
34
42
|
});
|
|
35
43
|
return { resolved, installed, messages };
|
|
36
44
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createBrowserArtifactManagementService, type BrowserArtifactManagementContext, type BrowserArtifactManagementService, type BrowserArtifactManagementServiceOptions, } from "./browser-artifact-management.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createBrowserArtifactManagementService, } from "./browser-artifact-management.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { InstalledChromeForTestingBuild } from "./chrome-for-testing-store.js";
|
|
2
|
+
export interface BrowserRuntimeCommandResult {
|
|
3
|
+
exitCode: number | null;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
errorCode?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface BrowserRuntimeCommandOptions {
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
timeoutMs: number;
|
|
11
|
+
}
|
|
12
|
+
export type BrowserRuntimeCommandRunner = (command: string, args: readonly string[], options: BrowserRuntimeCommandOptions) => Promise<BrowserRuntimeCommandResult>;
|
|
13
|
+
export interface EnsureChromeForTestingRuntimeOptions {
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
onProgress?: (message: string) => void;
|
|
16
|
+
platform?: string;
|
|
17
|
+
getuid?: () => number | undefined;
|
|
18
|
+
findSystemExecutable?: (name: string) => Promise<string | undefined>;
|
|
19
|
+
runCommand?: BrowserRuntimeCommandRunner;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Verifies that a managed Chrome build can start. On Linux, known missing
|
|
23
|
+
* shared libraries are installed with a fixed package allowlist before a
|
|
24
|
+
* second probe; arbitrary output from ldd is never passed to a shell.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ensureChromeForTestingRuntime(build: InstalledChromeForTestingBuild, options?: EnsureChromeForTestingRuntimeOptions): Promise<void>;
|
|
27
|
+
export declare function parseMissingSharedLibraries(output: string): string[];
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { access } from "node:fs/promises";
|
|
4
|
+
const PROBE_TIMEOUT_MS = 10_000;
|
|
5
|
+
const INSTALL_TIMEOUT_MS = 5 * 60_000;
|
|
6
|
+
const MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
7
|
+
const SYSTEM_PACKAGES = {
|
|
8
|
+
apt: {
|
|
9
|
+
"libasound.so.2": "libasound2",
|
|
10
|
+
"libatk-1.0.so.0": "libatk1.0-0",
|
|
11
|
+
"libatk-bridge-2.0.so.0": "libatk-bridge2.0-0",
|
|
12
|
+
"libatspi.so.0": "libatspi2.0-0",
|
|
13
|
+
"libcairo.so.2": "libcairo2",
|
|
14
|
+
"libcups.so.2": "libcups2",
|
|
15
|
+
"libdbus-1.so.3": "libdbus-1-3",
|
|
16
|
+
"libdrm.so.2": "libdrm2",
|
|
17
|
+
"libexpat.so.1": "libexpat1",
|
|
18
|
+
"libfontconfig.so.1": "libfontconfig1",
|
|
19
|
+
"libfreetype.so.6": "libfreetype6",
|
|
20
|
+
"libgbm.so.1": "libgbm1",
|
|
21
|
+
"libglib-2.0.so.0": "libglib2.0-0",
|
|
22
|
+
"libgobject-2.0.so.0": "libglib2.0-0",
|
|
23
|
+
"libgtk-3.so.0": "libgtk-3-0",
|
|
24
|
+
"libnspr4.so": "libnspr4",
|
|
25
|
+
"libnss3.so": "libnss3",
|
|
26
|
+
"libpango-1.0.so.0": "libpango-1.0-0",
|
|
27
|
+
"libX11.so.6": "libx11-6",
|
|
28
|
+
"libxcb.so.1": "libxcb1",
|
|
29
|
+
"libXcomposite.so.1": "libxcomposite1",
|
|
30
|
+
"libXdamage.so.1": "libxdamage1",
|
|
31
|
+
"libXext.so.6": "libxext6",
|
|
32
|
+
"libXfixes.so.3": "libxfixes3",
|
|
33
|
+
"libxkbcommon.so.0": "libxkbcommon0",
|
|
34
|
+
"libXrandr.so.2": "libxrandr2",
|
|
35
|
+
},
|
|
36
|
+
rpm: {
|
|
37
|
+
"libasound.so.2": "alsa-lib",
|
|
38
|
+
"libatk-1.0.so.0": "atk",
|
|
39
|
+
"libatk-bridge-2.0.so.0": "at-spi2-atk",
|
|
40
|
+
"libatspi.so.0": "at-spi2-core",
|
|
41
|
+
"libcairo.so.2": "cairo",
|
|
42
|
+
"libcups.so.2": "cups-libs",
|
|
43
|
+
"libdbus-1.so.3": "dbus-libs",
|
|
44
|
+
"libdrm.so.2": "libdrm",
|
|
45
|
+
"libexpat.so.1": "expat",
|
|
46
|
+
"libfontconfig.so.1": "fontconfig",
|
|
47
|
+
"libfreetype.so.6": "freetype",
|
|
48
|
+
"libgbm.so.1": "mesa-libgbm",
|
|
49
|
+
"libglib-2.0.so.0": "glib2",
|
|
50
|
+
"libgobject-2.0.so.0": "glib2",
|
|
51
|
+
"libgtk-3.so.0": "gtk3",
|
|
52
|
+
"libnspr4.so": "nspr",
|
|
53
|
+
"libnss3.so": "nss",
|
|
54
|
+
"libpango-1.0.so.0": "pango",
|
|
55
|
+
"libX11.so.6": "libX11",
|
|
56
|
+
"libxcb.so.1": "libxcb",
|
|
57
|
+
"libXcomposite.so.1": "libXcomposite",
|
|
58
|
+
"libXdamage.so.1": "libXdamage",
|
|
59
|
+
"libXext.so.6": "libXext",
|
|
60
|
+
"libXfixes.so.3": "libXfixes",
|
|
61
|
+
"libxkbcommon.so.0": "libxkbcommon",
|
|
62
|
+
"libXrandr.so.2": "libXrandr",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Verifies that a managed Chrome build can start. On Linux, known missing
|
|
67
|
+
* shared libraries are installed with a fixed package allowlist before a
|
|
68
|
+
* second probe; arbitrary output from ldd is never passed to a shell.
|
|
69
|
+
*/
|
|
70
|
+
export async function ensureChromeForTestingRuntime(build, options = {}) {
|
|
71
|
+
const runCommand = options.runCommand ?? runBrowserRuntimeCommand;
|
|
72
|
+
const findSystemExecutable = options.findSystemExecutable ?? findSystemCommand;
|
|
73
|
+
const run = async (command, args, timeoutMs = PROBE_TIMEOUT_MS) => {
|
|
74
|
+
options.signal?.throwIfAborted();
|
|
75
|
+
const result = await runCommand(command, args, { signal: options.signal, timeoutMs });
|
|
76
|
+
options.signal?.throwIfAborted();
|
|
77
|
+
return result;
|
|
78
|
+
};
|
|
79
|
+
const initialProbe = await run(build.executablePath, ["--version"]);
|
|
80
|
+
if (initialProbe.exitCode === 0) {
|
|
81
|
+
options.onProgress?.(`verified Chrome for Testing ${build.version} can start`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if ((options.platform ?? process.platform) !== "linux") {
|
|
85
|
+
throw browserLaunchError(build, initialProbe);
|
|
86
|
+
}
|
|
87
|
+
const ldd = await findSystemExecutable("ldd");
|
|
88
|
+
if (!ldd) {
|
|
89
|
+
throw new Error(`${browserLaunchError(build, initialProbe).message}; ldd is unavailable`);
|
|
90
|
+
}
|
|
91
|
+
const dependencyCheck = await run(ldd, [build.executablePath]);
|
|
92
|
+
const missingLibraries = parseMissingSharedLibraries(`${dependencyCheck.stdout}\n${dependencyCheck.stderr}`);
|
|
93
|
+
if (missingLibraries.length === 0)
|
|
94
|
+
throw browserLaunchError(build, initialProbe);
|
|
95
|
+
const packageManager = await resolvePackageManager(findSystemExecutable);
|
|
96
|
+
if (!packageManager) {
|
|
97
|
+
throw new Error(`Chrome for Testing is missing ${missingLibraries.join(", ")}, ` +
|
|
98
|
+
"but no supported system package manager (apt-get, dnf, or yum) is available");
|
|
99
|
+
}
|
|
100
|
+
const packages = packagesForLibraries(packageManager.family, missingLibraries);
|
|
101
|
+
const installArgs = packageManager.family === "apt"
|
|
102
|
+
? ["install", "-y", "--no-install-recommends", ...packages]
|
|
103
|
+
: ["install", "-y", ...packages];
|
|
104
|
+
let command = packageManager.path;
|
|
105
|
+
let args = installArgs;
|
|
106
|
+
const getuid = options.getuid ?? (() => process.getuid?.());
|
|
107
|
+
if (getuid() !== 0) {
|
|
108
|
+
const sudo = await findSystemExecutable("sudo");
|
|
109
|
+
if (!sudo || (await run(sudo, ["-n", "true"])).exitCode !== 0) {
|
|
110
|
+
throw new Error(`Chrome for Testing is missing ${missingLibraries.join(", ")}. ` +
|
|
111
|
+
`Install these packages as an administrator and retry: ${packages.join(" ")}`);
|
|
112
|
+
}
|
|
113
|
+
command = sudo;
|
|
114
|
+
args = ["-n", packageManager.path, ...installArgs];
|
|
115
|
+
}
|
|
116
|
+
options.onProgress?.(`installing Chrome for Testing system dependencies: ${packages.join(" ")}`);
|
|
117
|
+
const installation = await run(command, args, INSTALL_TIMEOUT_MS);
|
|
118
|
+
if (installation.exitCode !== 0) {
|
|
119
|
+
throw new Error(`failed to install Chrome for Testing system dependencies (${packages.join(" ")}): ` +
|
|
120
|
+
commandFailure(installation));
|
|
121
|
+
}
|
|
122
|
+
const finalProbe = await run(build.executablePath, ["--version"]);
|
|
123
|
+
if (finalProbe.exitCode !== 0)
|
|
124
|
+
throw browserLaunchError(build, finalProbe);
|
|
125
|
+
options.onProgress?.(`verified Chrome for Testing ${build.version} can start`);
|
|
126
|
+
}
|
|
127
|
+
export function parseMissingSharedLibraries(output) {
|
|
128
|
+
const libraries = new Set();
|
|
129
|
+
for (const line of output.split(/\r?\n/u)) {
|
|
130
|
+
const match = /^\s*(\S+)\s+=>\s+not found\s*$/u.exec(line);
|
|
131
|
+
if (match?.[1])
|
|
132
|
+
libraries.add(match[1]);
|
|
133
|
+
}
|
|
134
|
+
return [...libraries].sort();
|
|
135
|
+
}
|
|
136
|
+
function packagesForLibraries(family, libraries) {
|
|
137
|
+
const mapping = SYSTEM_PACKAGES[family];
|
|
138
|
+
const unsupported = libraries.filter((library) => !mapping[library]);
|
|
139
|
+
if (unsupported.length > 0) {
|
|
140
|
+
throw new Error(`Chrome for Testing is missing unsupported system libraries: ${unsupported.join(", ")}`);
|
|
141
|
+
}
|
|
142
|
+
return [...new Set(libraries.map((library) => mapping[library]))].sort();
|
|
143
|
+
}
|
|
144
|
+
async function resolvePackageManager(findExecutable) {
|
|
145
|
+
const apt = await findExecutable("apt-get");
|
|
146
|
+
if (apt)
|
|
147
|
+
return { family: "apt", path: apt };
|
|
148
|
+
const dnf = await findExecutable("dnf");
|
|
149
|
+
if (dnf)
|
|
150
|
+
return { family: "rpm", path: dnf };
|
|
151
|
+
const yum = await findExecutable("yum");
|
|
152
|
+
return yum ? { family: "rpm", path: yum } : undefined;
|
|
153
|
+
}
|
|
154
|
+
async function findSystemCommand(name) {
|
|
155
|
+
for (const directory of ["/usr/bin", "/bin", "/usr/sbin", "/sbin"]) {
|
|
156
|
+
const candidate = `${directory}/${name}`;
|
|
157
|
+
try {
|
|
158
|
+
await access(candidate, constants.X_OK);
|
|
159
|
+
return candidate;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Try the next standard system directory.
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
function runBrowserRuntimeCommand(command, args, options) {
|
|
168
|
+
return new Promise((resolveResult) => {
|
|
169
|
+
execFile(command, [...args], {
|
|
170
|
+
encoding: "utf8",
|
|
171
|
+
env: { ...process.env, LC_ALL: "C" },
|
|
172
|
+
maxBuffer: MAX_OUTPUT_BYTES,
|
|
173
|
+
signal: options.signal,
|
|
174
|
+
timeout: options.timeoutMs,
|
|
175
|
+
}, (error, stdout, stderr) => {
|
|
176
|
+
resolveResult({
|
|
177
|
+
exitCode: error
|
|
178
|
+
? typeof error.code === "number" ? error.code : null
|
|
179
|
+
: 0,
|
|
180
|
+
stdout,
|
|
181
|
+
stderr,
|
|
182
|
+
...(error && typeof error.code === "string" ? { errorCode: error.code } : {}),
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function browserLaunchError(build, result) {
|
|
188
|
+
return new Error(`Chrome for Testing ${build.version} was installed but cannot start: ${commandFailure(result)}`);
|
|
189
|
+
}
|
|
190
|
+
function commandFailure(result) {
|
|
191
|
+
const detail = result.stderr.trim() || result.stdout.trim() || result.errorCode;
|
|
192
|
+
return detail ? detail.slice(0, 2_000) : `exit code ${result.exitCode ?? "unknown"}`;
|
|
193
|
+
}
|
|
@@ -58,6 +58,8 @@ export interface ChromeForTestingArtifactStoreOptions {
|
|
|
58
58
|
export interface InstallChromeForTestingOptions {
|
|
59
59
|
signal?: AbortSignal;
|
|
60
60
|
onProgress?: (message: string) => void;
|
|
61
|
+
/** Runs after the build is complete but before it becomes active. */
|
|
62
|
+
verifyBeforeActivate?: (build: InstalledChromeForTestingBuild) => Promise<void>;
|
|
61
63
|
}
|
|
62
64
|
export interface PruneChromeForTestingStagingOptions {
|
|
63
65
|
olderThanMs?: number;
|
|
@@ -86,6 +86,11 @@ export function createChromeForTestingArtifactStore(options = {}) {
|
|
|
86
86
|
});
|
|
87
87
|
};
|
|
88
88
|
const installDownloadedBuild = async (release, expectedSha256, installOptions) => {
|
|
89
|
+
const verifyBeforeActivate = async (build) => {
|
|
90
|
+
throwIfAborted(installOptions.signal);
|
|
91
|
+
await installOptions.verifyBeforeActivate?.(build);
|
|
92
|
+
throwIfAborted(installOptions.signal);
|
|
93
|
+
};
|
|
89
94
|
const paths = ensureStorePaths(rootDir, platform.hostPlatform);
|
|
90
95
|
if (expectedSha256 !== undefined) {
|
|
91
96
|
const finalPath = buildPath(paths.buildsRoot, release.version, expectedSha256);
|
|
@@ -94,6 +99,7 @@ export function createChromeForTestingArtifactStore(options = {}) {
|
|
|
94
99
|
sha256: expectedSha256,
|
|
95
100
|
});
|
|
96
101
|
if (existing) {
|
|
102
|
+
await verifyBeforeActivate(existing);
|
|
97
103
|
writeActiveManifest(paths.root, existing);
|
|
98
104
|
return existing;
|
|
99
105
|
}
|
|
@@ -105,6 +111,7 @@ export function createChromeForTestingArtifactStore(options = {}) {
|
|
|
105
111
|
if (active?.version === release.version &&
|
|
106
112
|
active.artifactPlatform === release.platform &&
|
|
107
113
|
active.sourceUrl === release.url) {
|
|
114
|
+
await verifyBeforeActivate(active);
|
|
108
115
|
return active;
|
|
109
116
|
}
|
|
110
117
|
}
|
|
@@ -134,6 +141,7 @@ export function createChromeForTestingArtifactStore(options = {}) {
|
|
|
134
141
|
const finalPath = buildPath(paths.buildsRoot, build.version, build.sha256);
|
|
135
142
|
const existing = readInstalledBuild(finalPath, paths.buildsRoot, platform, build);
|
|
136
143
|
if (existing) {
|
|
144
|
+
await verifyBeforeActivate(existing);
|
|
137
145
|
writeActiveManifest(paths.root, existing);
|
|
138
146
|
return existing;
|
|
139
147
|
}
|
|
@@ -164,6 +172,7 @@ export function createChromeForTestingArtifactStore(options = {}) {
|
|
|
164
172
|
throw error;
|
|
165
173
|
}
|
|
166
174
|
const installed = requireInstalledBuild(finalPath, paths.buildsRoot, platform, build);
|
|
175
|
+
await verifyBeforeActivate(installed);
|
|
167
176
|
writeActiveManifest(paths.root, installed);
|
|
168
177
|
installOptions.onProgress?.(`installed Chrome for Testing ${build.version} (${build.sha256})`);
|
|
169
178
|
return installed;
|
package/dist/daemon-server.d.ts
CHANGED
|
@@ -9,6 +9,9 @@ export declare function daemonActivityBlocksShutdown(activity: DaemonActivitySna
|
|
|
9
9
|
export interface StartRynxDaemonServerOptions {
|
|
10
10
|
config?: AppConfig;
|
|
11
11
|
warn?: (message: string) => void;
|
|
12
|
+
/** Process-owner callback after every daemon resource and owner lease has
|
|
13
|
+
* shut down successfully. Library callers normally leave this unset. */
|
|
14
|
+
onShutdownComplete?: () => void;
|
|
12
15
|
/** Test/deployment seam; failures roll back the already-listening server. */
|
|
13
16
|
writeEndpoint?: (config: AppConfig, managementToken: string) => Promise<void>;
|
|
14
17
|
/** Test seam for owner-lock failure injection. Production always uses the RYNX_HOME lease. */
|
|
@@ -25,7 +28,7 @@ export interface StartRynxDaemonServerOptions {
|
|
|
25
28
|
/** Test seam for the App-only embedded Chromium process supervisor. */
|
|
26
29
|
createBrowserHostSupervisor?: (options: AppBrowserHostSupervisorOptions) => Pick<AppBrowserHostSupervisor, "ensureConnected" | "restart" | "stop">;
|
|
27
30
|
}
|
|
28
|
-
export declare function startRynxDaemonServer({ config, warn, writeEndpoint, acquireOwner, preparePlugins, loadDirectConfig, startDirectServer, createChromeInspectionManager, createBrowserHostSupervisor, }?: StartRynxDaemonServerOptions): Promise<Awaited<ReturnType<typeof startServer>>>;
|
|
31
|
+
export declare function startRynxDaemonServer({ config, warn, onShutdownComplete, writeEndpoint, acquireOwner, preparePlugins, loadDirectConfig, startDirectServer, createChromeInspectionManager, createBrowserHostSupervisor, }?: StartRynxDaemonServerOptions): Promise<Awaited<ReturnType<typeof startServer>>>;
|
|
29
32
|
export declare function createSupervisedAppSessionBrowserHost(desktop: SessionBrowserHost, supervisor: Pick<AppBrowserHostSupervisor, "ensureConnected" | "restart">): SessionBrowserHost;
|
|
30
33
|
/**
|
|
31
34
|
* Resolve the authenticated semantic target inside its owning Runtime and
|