@rynx-ai/daemon 0.1.9 → 0.1.10-beta.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/dist/app-browser-host-supervisor.d.ts +97 -0
- package/dist/app-browser-host-supervisor.js +529 -0
- package/dist/browser-artifact-management.d.ts +20 -0
- package/dist/browser-artifact-management.js +61 -0
- package/dist/chrome-for-testing-store.js +1 -1
- package/dist/chrome-inspection-gateway.d.ts +58 -0
- package/dist/chrome-inspection-gateway.js +806 -0
- package/dist/chrome-inspection-manager.d.ts +94 -0
- package/dist/chrome-inspection-manager.js +573 -0
- package/dist/cli.js +13 -2
- package/dist/control-client.d.ts +9 -0
- package/dist/control-client.js +63 -0
- package/dist/daemon-build-status.d.ts +8 -0
- package/dist/daemon-build-status.js +18 -0
- package/dist/daemon-server.d.ts +19 -2
- package/dist/daemon-server.js +615 -59
- package/dist/db.js +56 -0
- package/dist/desktop-browser-host-client.d.ts +2 -1
- package/dist/desktop-browser-host-client.js +3 -1
- package/dist/direct-runtime-authenticator.js +11 -6
- package/dist/headless-browser-host.js +18 -1
- package/dist/index-daemon.js +28 -1
- package/dist/maintenance-management.d.ts +11 -0
- package/dist/maintenance-management.js +13 -0
- package/dist/plugin-installer.d.ts +35 -0
- package/dist/plugin-installer.js +195 -94
- package/dist/plugin-management-service.d.ts +58 -0
- package/dist/plugin-management-service.js +240 -0
- package/dist/plugin-package.d.ts +26 -3
- package/dist/plugin-package.js +235 -56
- package/dist/pm2.js +37 -5
- package/dist/remote-runtime-access-store.d.ts +1 -0
- package/dist/remote-runtime-access-store.js +7 -0
- package/dist/remote-runtime-admin.d.ts +3 -0
- package/dist/remote-runtime-admin.js +3 -0
- package/dist/remote-runtime-connection-manager.d.ts +12 -1
- package/dist/remote-runtime-connection-manager.js +170 -4
- package/dist/remote-runtime-target-control.d.ts +5 -1
- package/dist/remote-runtime-target-control.js +62 -7
- package/dist/remote-runtime-target-store.d.ts +4 -1
- package/dist/remote-runtime-target-store.js +21 -2
- package/dist/session-log-store.js +29 -0
- package/dist/session-meta-store.js +3 -1
- package/dist/session-pending-message-store.d.ts +2 -0
- package/dist/session-pending-message-store.js +41 -0
- package/dist/session-resource-store.d.ts +32 -0
- package/dist/session-resource-store.js +700 -0
- package/dist/setup.d.ts +16 -0
- package/dist/setup.js +136 -2
- package/package.json +14 -9
package/dist/cli.js
CHANGED
|
@@ -52,8 +52,8 @@ Lifecycle:
|
|
|
52
52
|
|
|
53
53
|
Plugins (static manifest + isolated runner):
|
|
54
54
|
plugin manage list
|
|
55
|
-
plugin manage install <npm|path|git|url> [
|
|
56
|
-
plugin manage update <id> [
|
|
55
|
+
plugin manage install <npm|path|git|url> [...] [--expect-digest <sha256-...>]
|
|
56
|
+
plugin manage update <id> [...] [--expect-digest <sha256-...>]
|
|
57
57
|
plugin manage uninstall <id>
|
|
58
58
|
plugin manage enable|disable <id>
|
|
59
59
|
manage arbitrary installed plugins
|
|
@@ -147,8 +147,10 @@ async function pluginManageCommand(sub, arg) {
|
|
|
147
147
|
const force = process.argv.includes("--force");
|
|
148
148
|
const grantAll = process.argv.includes("--grant-all");
|
|
149
149
|
const allowScripts = process.argv.includes("--allow-scripts");
|
|
150
|
+
const expectedDigest = digestOption(process.argv);
|
|
150
151
|
const result = await installPlugin(arg, (line) => console.log(line), {
|
|
151
152
|
allowScripts,
|
|
153
|
+
...(expectedDigest ? { expectedDigest } : {}),
|
|
152
154
|
onConflict: async (existing) => {
|
|
153
155
|
if (force)
|
|
154
156
|
return true;
|
|
@@ -197,9 +199,11 @@ async function pluginManageCommand(sub, arg) {
|
|
|
197
199
|
if (!isRunnablePluginRecord(existing)) {
|
|
198
200
|
fail(`plugin "${arg}" uses the removed legacy format; reinstall it from an explicit source`);
|
|
199
201
|
}
|
|
202
|
+
const expectedDigest = digestOption(process.argv);
|
|
200
203
|
const result = await installPlugin(existing.spec, (line) => console.log(line), {
|
|
201
204
|
expectedId: existing.name,
|
|
202
205
|
allowScripts: process.argv.includes("--allow-scripts"),
|
|
206
|
+
...(expectedDigest ? { expectedDigest } : {}),
|
|
203
207
|
onConflict: () => true,
|
|
204
208
|
approveCapabilities: (manifest, current) => approvePluginCapabilities(manifest, current?.grants ?? [], process.argv.includes("--grant-all")),
|
|
205
209
|
});
|
|
@@ -641,6 +645,13 @@ function optionValue(args, option) {
|
|
|
641
645
|
fail(`${option}: missing value`);
|
|
642
646
|
return value;
|
|
643
647
|
}
|
|
648
|
+
function digestOption(args) {
|
|
649
|
+
const expected = optionValue(args, "--expect-digest");
|
|
650
|
+
const legacyAlias = optionValue(args, "--expected-digest");
|
|
651
|
+
if (expected && legacyAlias)
|
|
652
|
+
fail("pass only one digest expectation option");
|
|
653
|
+
return expected ?? legacyAlias;
|
|
654
|
+
}
|
|
644
655
|
async function readStdinBounded(maxBytes) {
|
|
645
656
|
const chunks = [];
|
|
646
657
|
let bytes = 0;
|
package/dist/control-client.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type DaemonStatus } from "@rynx-ai/protocol/remote-runtime";
|
|
2
|
+
import { type DaemonChromeInspectionConfigureInput, type DaemonChromeInspectionStatus, type DaemonShutdownIfIdleResult } from "@rynx-ai/protocol/control";
|
|
2
3
|
import { type RemoteRuntimeRpcMethod, type RemoteRuntimeRpcParams, type RemoteRuntimeRpcResultFor } from "@rynx-ai/protocol/remote-runtime-rpc";
|
|
3
4
|
import { type PairingOffer } from "@rynx-ai/protocol/direct-runtime";
|
|
4
5
|
import { type RuntimeBrowserBootstrapCredential, type RuntimeBrowserEndpointDescriptor } from "@rynx-ai/protocol/runtime-browser-bootstrap";
|
|
@@ -61,6 +62,14 @@ export declare function ensureResidentDaemon(): Promise<ResidentDaemon>;
|
|
|
61
62
|
export declare function getResidentDaemonIdentity(): Promise<ResidentDaemonIdentity>;
|
|
62
63
|
/** Read the transport-independent status of the resident daemon process. */
|
|
63
64
|
export declare function getResidentDaemonRuntimeStatus(): Promise<DaemonStatus>;
|
|
65
|
+
/**
|
|
66
|
+
* Ask the resident daemon to perform one final activity check and stop only
|
|
67
|
+
* when idle. Busy is a normal result so the caller can ask the user to resolve
|
|
68
|
+
* the listed work and explicitly retry.
|
|
69
|
+
*/
|
|
70
|
+
export declare function shutdownResidentDaemonIfIdle(): Promise<DaemonShutdownIfIdleResult>;
|
|
71
|
+
export declare function getResidentChromeInspectionStatus(): Promise<DaemonChromeInspectionStatus>;
|
|
72
|
+
export declare function configureResidentChromeInspection(input: DaemonChromeInspectionConfigureInput): Promise<DaemonChromeInspectionStatus>;
|
|
64
73
|
/** Create one pairing offer on the resident daemon for a selected reachable route. */
|
|
65
74
|
export declare function createResidentRemoteRuntimePairingOffer(input?: {
|
|
66
75
|
clientLabel?: string;
|
package/dist/control-client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { open } from "node:fs/promises";
|
|
2
2
|
import { isAbsolute } from "node:path";
|
|
3
3
|
import { isDaemonCoreCompatible, parseDaemonStatus, } from "@rynx-ai/protocol/remote-runtime";
|
|
4
|
+
import { DAEMON_CHROME_INSPECTION_PATH, DAEMON_SHUTDOWN_IF_IDLE_PATH, parseDaemonChromeInspectionConfigureInput, parseDaemonChromeInspectionStatus, parseDaemonShutdownIfIdleResult, } from "@rynx-ai/protocol/control";
|
|
4
5
|
import { parseRemoteRuntimeRpcRequest, parseRemoteRuntimeRpcResponseForMethod, REMOTE_RUNTIME_RPC_MAX_FRAME_BYTES, REMOTE_RUNTIME_RPC_METHOD_METADATA, } from "@rynx-ai/protocol/remote-runtime-rpc";
|
|
5
6
|
import { parsePairingOffer, } from "@rynx-ai/protocol/direct-runtime";
|
|
6
7
|
import { RUNTIME_BROWSER_BOOTSTRAP_PATH, RUNTIME_BROWSER_CAPABILITY_ENV, RUNTIME_BROWSER_CAPABILITY_HEADER, RUNTIME_BROWSER_CONTEXT_FILE_ENV, RUNTIME_BROWSER_MANAGEMENT_PATH_PREFIX, RUNTIME_BROWSER_RPC_PATH, RUNTIME_BROWSER_SESSION_ID_ENV, RUNTIME_BROWSER_SESSION_ID_HEADER, parseRuntimeBrowserBootstrapCredential, parseRuntimeBrowserEndpointDescriptor, } from "@rynx-ai/protocol/runtime-browser-bootstrap";
|
|
@@ -9,6 +10,7 @@ import { ensureDaemonControlEndpoint } from "./control-endpoint.js";
|
|
|
9
10
|
export { connectResidentDesktopBrowserHost, } from "./desktop-browser-host-client.js";
|
|
10
11
|
const IDENTITY_TIMEOUT_MS = 5_000;
|
|
11
12
|
const RUNTIME_STATUS_TIMEOUT_MS = 5_000;
|
|
13
|
+
const DAEMON_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
12
14
|
const PLUGIN_CLI_TIMEOUT_MS = 120_000;
|
|
13
15
|
const PLUGIN_CLI_BODY_MAX_BYTES = 512 * 1024;
|
|
14
16
|
const PLUGIN_CLI_STDIN_MAX_BYTES = 256 * 1024;
|
|
@@ -17,6 +19,9 @@ const PLUGIN_CLI_ARGS_TOTAL_MAX_BYTES = 32 * 1024;
|
|
|
17
19
|
const PLUGIN_CLI_ARGS_MAX_COUNT = 64;
|
|
18
20
|
const IDENTITY_RESPONSE_MAX_BYTES = 16 * 1024;
|
|
19
21
|
const RUNTIME_STATUS_RESPONSE_MAX_BYTES = 64 * 1024;
|
|
22
|
+
const DAEMON_SHUTDOWN_RESPONSE_MAX_BYTES = 4 * 1024;
|
|
23
|
+
const CHROME_INSPECTION_TIMEOUT_MS = 30_000;
|
|
24
|
+
const CHROME_INSPECTION_RESPONSE_MAX_BYTES = 256 * 1024;
|
|
20
25
|
const REMOTE_RUNTIME_ADMIN_RESPONSE_MAX_BYTES = 64 * 1024;
|
|
21
26
|
const RUNTIME_TARGET_RESPONSE_MAX_BYTES = 128 * 1024;
|
|
22
27
|
const RUNTIME_TARGET_REQUEST_MAX_BYTES = 64 * 1024;
|
|
@@ -97,6 +102,45 @@ export async function getResidentDaemonRuntimeStatus() {
|
|
|
97
102
|
}
|
|
98
103
|
return status;
|
|
99
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Ask the resident daemon to perform one final activity check and stop only
|
|
107
|
+
* when idle. Busy is a normal result so the caller can ask the user to resolve
|
|
108
|
+
* the listed work and explicitly retry.
|
|
109
|
+
*/
|
|
110
|
+
export async function shutdownResidentDaemonIfIdle() {
|
|
111
|
+
const endpoint = await ensureDaemonControlEndpoint();
|
|
112
|
+
assertLoopbackOrigin(endpoint.origin);
|
|
113
|
+
const response = await fetch(`${endpoint.origin}${DAEMON_SHUTDOWN_IF_IDLE_PATH}`, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: managementHeaders(endpoint.managementToken),
|
|
116
|
+
signal: AbortSignal.timeout(DAEMON_SHUTDOWN_TIMEOUT_MS),
|
|
117
|
+
});
|
|
118
|
+
const body = await readBoundedResponse(response, DAEMON_SHUTDOWN_RESPONSE_MAX_BYTES);
|
|
119
|
+
if (response.status !== 202 && response.status !== 409) {
|
|
120
|
+
throw daemonRequestError("daemon shutdown-if-idle request", response.status, body);
|
|
121
|
+
}
|
|
122
|
+
let result;
|
|
123
|
+
try {
|
|
124
|
+
result = parseDaemonShutdownIfIdleResult(JSON.parse(body));
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw new Error(`daemon returned an invalid shutdown-if-idle response: ${messageOf(error)}`);
|
|
128
|
+
}
|
|
129
|
+
if (response.status === 202 && result.outcome !== "accepted" ||
|
|
130
|
+
response.status === 409 && result.outcome !== "busy") {
|
|
131
|
+
throw new Error("daemon returned a shutdown-if-idle outcome inconsistent with HTTP status");
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
export async function getResidentChromeInspectionStatus() {
|
|
136
|
+
const result = await chromeInspectionRequest("GET");
|
|
137
|
+
return parseDaemonChromeInspectionStatus(result);
|
|
138
|
+
}
|
|
139
|
+
export async function configureResidentChromeInspection(input) {
|
|
140
|
+
const normalized = parseDaemonChromeInspectionConfigureInput(input);
|
|
141
|
+
const result = await chromeInspectionRequest("PUT", normalized);
|
|
142
|
+
return parseDaemonChromeInspectionStatus(result);
|
|
143
|
+
}
|
|
100
144
|
/** Create one pairing offer on the resident daemon for a selected reachable route. */
|
|
101
145
|
export async function createResidentRemoteRuntimePairingOffer(input = {}) {
|
|
102
146
|
const endpoint = await ensureDaemonControlEndpoint();
|
|
@@ -545,6 +589,25 @@ export async function invokeResidentPluginCommand(pluginId, args, options = {})
|
|
|
545
589
|
function managementHeaders(token) {
|
|
546
590
|
return { "x-rynx-management-token": token };
|
|
547
591
|
}
|
|
592
|
+
async function chromeInspectionRequest(method, bodyValue) {
|
|
593
|
+
const endpoint = await ensureDaemonControlEndpoint();
|
|
594
|
+
assertLoopbackOrigin(endpoint.origin);
|
|
595
|
+
const body = bodyValue === undefined ? undefined : JSON.stringify(bodyValue);
|
|
596
|
+
const response = await fetch(`${endpoint.origin}${DAEMON_CHROME_INSPECTION_PATH}`, {
|
|
597
|
+
method,
|
|
598
|
+
headers: {
|
|
599
|
+
...managementHeaders(endpoint.managementToken),
|
|
600
|
+
...(body === undefined ? {} : { "content-type": "application/json" }),
|
|
601
|
+
},
|
|
602
|
+
...(body === undefined ? {} : { body }),
|
|
603
|
+
signal: AbortSignal.timeout(CHROME_INSPECTION_TIMEOUT_MS),
|
|
604
|
+
});
|
|
605
|
+
const responseBody = await readBoundedResponse(response, CHROME_INSPECTION_RESPONSE_MAX_BYTES);
|
|
606
|
+
if (!response.ok) {
|
|
607
|
+
throw daemonRequestError("Chrome inspection request", response.status, responseBody);
|
|
608
|
+
}
|
|
609
|
+
return parseJsonObject(responseBody, "daemon returned an invalid Chrome inspection response");
|
|
610
|
+
}
|
|
548
611
|
function assertLoopbackOrigin(origin) {
|
|
549
612
|
const hostname = new URL(origin).hostname;
|
|
550
613
|
if (hostname !== "127.0.0.1" && hostname !== "[::1]" && hostname !== "::1") {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface DaemonBuildStatus {
|
|
2
|
+
restartRequired: boolean;
|
|
3
|
+
}
|
|
4
|
+
/** Detect a same-installation rebuild that happened after this daemon
|
|
5
|
+
* incarnation started. Package path/version changes are handled by the PM2
|
|
6
|
+
* lifecycle wrapper; this is the development and in-place-update fallback. */
|
|
7
|
+
export declare function daemonEntryRestartRequired(entryPath: string, daemonStartedAtMs: number): boolean;
|
|
8
|
+
export declare function createDaemonBuildStatus(entryPath: string, daemonStartedAtMs: number): () => DaemonBuildStatus;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
const FILE_TIMESTAMP_TOLERANCE_MS = 1_000;
|
|
3
|
+
/** Detect a same-installation rebuild that happened after this daemon
|
|
4
|
+
* incarnation started. Package path/version changes are handled by the PM2
|
|
5
|
+
* lifecycle wrapper; this is the development and in-place-update fallback. */
|
|
6
|
+
export function daemonEntryRestartRequired(entryPath, daemonStartedAtMs) {
|
|
7
|
+
try {
|
|
8
|
+
return statSync(entryPath).mtimeMs > daemonStartedAtMs + FILE_TIMESTAMP_TOLERANCE_MS;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function createDaemonBuildStatus(entryPath, daemonStartedAtMs) {
|
|
15
|
+
return () => ({
|
|
16
|
+
restartRequired: daemonEntryRestartRequired(entryPath, daemonStartedAtMs),
|
|
17
|
+
});
|
|
18
|
+
}
|
package/dist/daemon-server.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { type AppConfig } from "@rynx-ai/core";
|
|
2
|
-
import {
|
|
2
|
+
import { type DaemonActivitySnapshot } from "@rynx-ai/protocol/control";
|
|
3
|
+
import { SessionBrowserService, startServer, startDirectRuntimeServer, type DirectRuntimeBrowserInspectHost, type SessionBrowserHost } from "@rynx-ai/server";
|
|
4
|
+
import { AppBrowserHostSupervisor, type AppBrowserHostSupervisorOptions } from "./app-browser-host-supervisor.js";
|
|
5
|
+
import { ChromeInspectionManager, type ChromeInspectionManagerOptions } from "./chrome-inspection-manager.js";
|
|
3
6
|
import { type DaemonOwnerLease } from "./daemon-owner.js";
|
|
4
7
|
import { type DirectRuntimeConfig } from "./direct-runtime-config.js";
|
|
8
|
+
export declare function daemonActivityBlocksShutdown(activity: DaemonActivitySnapshot): boolean;
|
|
5
9
|
export interface StartRynxDaemonServerOptions {
|
|
6
10
|
config?: AppConfig;
|
|
7
11
|
warn?: (message: string) => void;
|
|
@@ -13,5 +17,18 @@ export interface StartRynxDaemonServerOptions {
|
|
|
13
17
|
loadDirectConfig?: () => Promise<DirectRuntimeConfig>;
|
|
14
18
|
/** Test seam for listener lifecycle and bind failures. */
|
|
15
19
|
startDirectServer?: typeof startDirectRuntimeServer;
|
|
20
|
+
/** Test seam for resident Chrome inspection settings and gateway lifecycle. */
|
|
21
|
+
createChromeInspectionManager?: (options: ChromeInspectionManagerOptions) => Promise<Pick<ChromeInspectionManager, "status" | "configure" | "shutdown">>;
|
|
22
|
+
/** Test seam for the App-only embedded Chromium process supervisor. */
|
|
23
|
+
createBrowserHostSupervisor?: (options: AppBrowserHostSupervisorOptions) => Pick<AppBrowserHostSupervisor, "ensureConnected" | "restart" | "stop">;
|
|
16
24
|
}
|
|
17
|
-
export declare function startRynxDaemonServer({ config, warn, writeEndpoint, acquireOwner, loadDirectConfig, startDirectServer, }?: StartRynxDaemonServerOptions): Promise<Awaited<ReturnType<typeof startServer>>>;
|
|
25
|
+
export declare function startRynxDaemonServer({ config, warn, writeEndpoint, acquireOwner, loadDirectConfig, startDirectServer, createChromeInspectionManager, createBrowserHostSupervisor, }?: StartRynxDaemonServerOptions): Promise<Awaited<ReturnType<typeof startServer>>>;
|
|
26
|
+
export declare function createSupervisedAppSessionBrowserHost(desktop: SessionBrowserHost, supervisor: Pick<AppBrowserHostSupervisor, "ensureConnected" | "restart">): SessionBrowserHost;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the authenticated semantic target inside its owning Runtime and
|
|
29
|
+
* attach directly to that Page's loopback CDP WebSocket. No endpoint is ever
|
|
30
|
+
* projected into the Direct protocol.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createDirectRuntimeBrowserInspectHost(browsers: Pick<SessionBrowserService, "getInspectionPageEndpoint">, options?: {
|
|
33
|
+
openTimeoutMs?: number;
|
|
34
|
+
}): DirectRuntimeBrowserInspectHost;
|