@rynx-ai/daemon 0.1.0 → 0.1.9
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/browser-cli-args.d.ts +20 -0
- package/dist/browser-cli-args.js +100 -0
- package/dist/browser-runner-session-context.d.ts +24 -0
- package/dist/browser-runner-session-context.js +113 -0
- package/dist/cdp-keyboard-input.d.ts +6 -0
- package/dist/cdp-keyboard-input.js +209 -0
- package/dist/channel-store.d.ts +0 -6
- package/dist/channel-store.js +2 -18
- package/dist/chrome-for-testing-release-resolver.d.ts +34 -0
- package/dist/chrome-for-testing-release-resolver.js +214 -0
- package/dist/chrome-for-testing-store.d.ts +85 -0
- package/dist/chrome-for-testing-store.js +836 -0
- package/dist/cli.js +467 -30
- package/dist/control-client.d.ts +113 -0
- package/dist/control-client.js +792 -0
- package/dist/control-deps.js +37 -6
- package/dist/control-endpoint.d.ts +14 -0
- package/dist/control-endpoint.js +124 -0
- package/dist/control-link-store.d.ts +16 -0
- package/dist/control-link-store.js +53 -0
- package/dist/daemon-owner.d.ts +14 -0
- package/dist/daemon-owner.js +104 -0
- package/dist/daemon-server.d.ts +12 -2
- package/dist/daemon-server.js +559 -28
- package/dist/db.js +140 -10
- package/dist/desktop-browser-host-client.d.ts +34 -0
- package/dist/desktop-browser-host-client.js +413 -0
- package/dist/direct-runtime-authenticator.d.ts +13 -0
- package/dist/direct-runtime-authenticator.js +127 -0
- package/dist/direct-runtime-config.d.ts +20 -0
- package/dist/direct-runtime-config.js +101 -0
- package/dist/embedded-browser-profile-cleanup.d.ts +58 -0
- package/dist/embedded-browser-profile-cleanup.js +338 -0
- package/dist/headless-browser-host.d.ts +14 -0
- package/dist/headless-browser-host.js +2156 -0
- package/dist/index-daemon.js +28 -5
- package/dist/installation-id.d.ts +33 -0
- package/dist/installation-id.js +692 -0
- package/dist/migrations/cleanup-legacy-sessions.d.ts +0 -3
- package/dist/migrations/cleanup-legacy-sessions.js +10 -28
- package/dist/plugin-cli.d.ts +30 -0
- package/dist/plugin-cli.js +270 -0
- package/dist/plugin-host-rpc.d.ts +85 -0
- package/dist/plugin-host-rpc.js +878 -0
- package/dist/plugin-install-restart.d.ts +13 -0
- package/dist/plugin-install-restart.js +24 -0
- package/dist/plugin-installer.d.ts +36 -15
- package/dist/plugin-installer.js +420 -155
- package/dist/plugin-package.d.ts +38 -0
- package/dist/plugin-package.js +553 -0
- package/dist/plugin-runtime-control.d.ts +2 -0
- package/dist/plugin-runtime-control.js +37 -0
- package/dist/plugin-store.d.ts +37 -8
- package/dist/plugin-store.js +80 -15
- package/dist/plugin-supervisor.d.ts +160 -0
- package/dist/plugin-supervisor.js +1143 -0
- package/dist/pm2.d.ts +3 -1
- package/dist/pm2.js +55 -13
- package/dist/provider-cli-service.d.ts +46 -0
- package/dist/provider-cli-service.js +510 -0
- package/dist/registry.d.ts +6 -19
- package/dist/registry.js +22 -87
- package/dist/remote-runtime-access-store.d.ts +61 -0
- package/dist/remote-runtime-access-store.js +386 -0
- package/dist/remote-runtime-admin.d.ts +32 -0
- package/dist/remote-runtime-admin.js +107 -0
- package/dist/remote-runtime-connection-manager.d.ts +92 -0
- package/dist/remote-runtime-connection-manager.js +1409 -0
- package/dist/remote-runtime-credential-store.d.ts +45 -0
- package/dist/remote-runtime-credential-store.js +639 -0
- package/dist/remote-runtime-target-control.d.ts +47 -0
- package/dist/remote-runtime-target-control.js +433 -0
- package/dist/remote-runtime-target-store.d.ts +53 -0
- package/dist/remote-runtime-target-store.js +307 -0
- package/dist/runtime-share-addresses.d.ts +21 -0
- package/dist/runtime-share-addresses.js +50 -0
- package/dist/session-browser-capability-store.d.ts +13 -0
- package/dist/session-browser-capability-store.js +58 -0
- package/dist/session-emulator-binding-store.d.ts +10 -0
- package/dist/session-emulator-binding-store.js +61 -0
- package/dist/session-launch-operations.d.ts +41 -0
- package/dist/session-launch-operations.js +124 -0
- package/dist/session-meta-store.d.ts +3 -2
- package/dist/session-meta-store.js +57 -7
- package/dist/setup.js +9 -4
- package/dist/skills-catalog.js +8 -4
- package/dist/update.d.ts +11 -2
- package/dist/update.js +0 -27
- package/package.json +18 -10
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type BrowserCliSubcommand = "install" | "update" | "version" | "clean" | "open" | "status" | "pages" | "endpoint" | "close";
|
|
2
|
+
export interface BrowserCliArgs {
|
|
3
|
+
subcommand: BrowserCliSubcommand;
|
|
4
|
+
json: boolean;
|
|
5
|
+
ensure: boolean;
|
|
6
|
+
channel?: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
runtime?: string;
|
|
9
|
+
session?: string;
|
|
10
|
+
url?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface BrowserCliTarget {
|
|
13
|
+
runtimeSelector: string;
|
|
14
|
+
sessionId: string;
|
|
15
|
+
credential?: RuntimeBrowserBootstrapCredential;
|
|
16
|
+
}
|
|
17
|
+
export declare function parseBrowserCliArgs(args: readonly string[]): BrowserCliArgs;
|
|
18
|
+
/** Resolve authority without ever turning a managed Session into an operator. */
|
|
19
|
+
export declare function resolveBrowserCliTarget(args: BrowserCliArgs, managedCredential: RuntimeBrowserBootstrapCredential | undefined): BrowserCliTarget;
|
|
20
|
+
import type { RuntimeBrowserBootstrapCredential } from "@rynx-ai/protocol/runtime-browser-bootstrap";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const SPECS = {
|
|
2
|
+
install: { values: ["--channel", "--version"], flags: ["--json"] },
|
|
3
|
+
update: { values: ["--channel"], flags: ["--json"] },
|
|
4
|
+
version: { values: [], flags: ["--json"] },
|
|
5
|
+
clean: { values: [], flags: ["--json"] },
|
|
6
|
+
open: {
|
|
7
|
+
values: ["--url", "--session", "--runtime"],
|
|
8
|
+
flags: ["--json"],
|
|
9
|
+
positionalUrl: true,
|
|
10
|
+
},
|
|
11
|
+
status: { values: ["--session", "--runtime"], flags: ["--json"] },
|
|
12
|
+
pages: { values: ["--session", "--runtime"], flags: ["--json"] },
|
|
13
|
+
endpoint: { values: ["--session"], flags: ["--ensure", "--json"] },
|
|
14
|
+
close: { values: ["--session", "--runtime"], flags: ["--json"] },
|
|
15
|
+
};
|
|
16
|
+
export function parseBrowserCliArgs(args) {
|
|
17
|
+
const subcommand = args[0];
|
|
18
|
+
if (!isBrowserSubcommand(subcommand)) {
|
|
19
|
+
throw new Error("browser: expected install, update, version, clean, open, status, pages, endpoint, or close");
|
|
20
|
+
}
|
|
21
|
+
const spec = SPECS[subcommand];
|
|
22
|
+
const values = new Map();
|
|
23
|
+
const flags = new Set();
|
|
24
|
+
const positionals = [];
|
|
25
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
26
|
+
const arg = args[index];
|
|
27
|
+
if (arg.startsWith("--")) {
|
|
28
|
+
if (spec.flags.includes(arg)) {
|
|
29
|
+
if (flags.has(arg))
|
|
30
|
+
throw new Error(`browser ${subcommand}: duplicate option ${arg}`);
|
|
31
|
+
flags.add(arg);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (spec.values.includes(arg)) {
|
|
35
|
+
if (values.has(arg))
|
|
36
|
+
throw new Error(`browser ${subcommand}: duplicate option ${arg}`);
|
|
37
|
+
const value = args[index + 1];
|
|
38
|
+
if (!value || value.startsWith("--")) {
|
|
39
|
+
throw new Error(`browser ${subcommand}: ${arg} requires a value`);
|
|
40
|
+
}
|
|
41
|
+
values.set(arg, value);
|
|
42
|
+
index += 1;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
throw new Error(`browser ${subcommand}: unknown option ${arg}`);
|
|
46
|
+
}
|
|
47
|
+
positionals.push(arg);
|
|
48
|
+
}
|
|
49
|
+
if (!spec.positionalUrl && positionals.length > 0) {
|
|
50
|
+
throw new Error(`browser ${subcommand}: unexpected argument ${positionals[0]}`);
|
|
51
|
+
}
|
|
52
|
+
if (positionals.length > 1) {
|
|
53
|
+
throw new Error(`browser ${subcommand}: unexpected extra argument ${positionals[1]}`);
|
|
54
|
+
}
|
|
55
|
+
if (positionals.length === 1 && values.has("--url")) {
|
|
56
|
+
throw new Error("browser open: pass the URL once, either positionally or with --url");
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
subcommand,
|
|
60
|
+
json: flags.has("--json"),
|
|
61
|
+
ensure: flags.has("--ensure"),
|
|
62
|
+
...(values.has("--channel") ? { channel: values.get("--channel") } : {}),
|
|
63
|
+
...(values.has("--version") ? { version: values.get("--version") } : {}),
|
|
64
|
+
...(values.has("--runtime") ? { runtime: values.get("--runtime") } : {}),
|
|
65
|
+
...(values.has("--session") ? { session: values.get("--session") } : {}),
|
|
66
|
+
...(values.has("--url") || positionals[0]
|
|
67
|
+
? { url: values.get("--url") ?? positionals[0] }
|
|
68
|
+
: {}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Resolve authority without ever turning a managed Session into an operator. */
|
|
72
|
+
export function resolveBrowserCliTarget(args, managedCredential) {
|
|
73
|
+
const runtimeSelector = args.runtime ?? "local";
|
|
74
|
+
if (runtimeSelector !== "local") {
|
|
75
|
+
if (managedCredential) {
|
|
76
|
+
throw new Error("browser: a managed Session cannot select another Runtime");
|
|
77
|
+
}
|
|
78
|
+
if (!args.session) {
|
|
79
|
+
throw new Error("browser: --session is required when selecting a remote Runtime");
|
|
80
|
+
}
|
|
81
|
+
return { runtimeSelector, sessionId: args.session };
|
|
82
|
+
}
|
|
83
|
+
if (managedCredential) {
|
|
84
|
+
if (args.session && args.session !== managedCredential.sessionId) {
|
|
85
|
+
throw new Error("browser: a managed Session cannot control another Session with --session");
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
runtimeSelector: "local",
|
|
89
|
+
sessionId: managedCredential.sessionId,
|
|
90
|
+
credential: managedCredential,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (!args.session) {
|
|
94
|
+
throw new Error("browser: run inside a managed Rynx Session or pass --session for operator access");
|
|
95
|
+
}
|
|
96
|
+
return { runtimeSelector: "local", sessionId: args.session };
|
|
97
|
+
}
|
|
98
|
+
function isBrowserSubcommand(value) {
|
|
99
|
+
return value !== undefined && Object.hasOwn(SPECS, value);
|
|
100
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SessionBrowserCapabilityStore } from "./session-browser-capability-store.js";
|
|
2
|
+
export interface BrowserRunnerSessionContextProviderOptions {
|
|
3
|
+
rootDir: string;
|
|
4
|
+
capabilities: SessionBrowserCapabilityStore;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Supplies managed runners with an atomically replaceable Session credential.
|
|
8
|
+
* A file indirection is necessary because Claude clear/fork keeps the same
|
|
9
|
+
* process tree while changing the owning Rynx Session.
|
|
10
|
+
*/
|
|
11
|
+
export declare class BrowserRunnerSessionContextProvider {
|
|
12
|
+
private readonly options;
|
|
13
|
+
private readonly rootDir;
|
|
14
|
+
private readonly contexts;
|
|
15
|
+
private closed;
|
|
16
|
+
constructor(options: BrowserRunnerSessionContextProviderOptions);
|
|
17
|
+
open(sessionId: string): {
|
|
18
|
+
childEnv: Readonly<Record<string, string>>;
|
|
19
|
+
rotate(newSessionId: string): void;
|
|
20
|
+
close(): void;
|
|
21
|
+
};
|
|
22
|
+
closeAll(): void;
|
|
23
|
+
private closeContext;
|
|
24
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { chmodSync, closeSync, fsyncSync, lstatSync, mkdirSync, openSync, readdirSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { RUNTIME_BROWSER_CONTEXT_FILE_ENV, parseRuntimeBrowserBootstrapCredential, } from "@rynx-ai/protocol/runtime-browser-bootstrap";
|
|
5
|
+
/**
|
|
6
|
+
* Supplies managed runners with an atomically replaceable Session credential.
|
|
7
|
+
* A file indirection is necessary because Claude clear/fork keeps the same
|
|
8
|
+
* process tree while changing the owning Rynx Session.
|
|
9
|
+
*/
|
|
10
|
+
export class BrowserRunnerSessionContextProvider {
|
|
11
|
+
options;
|
|
12
|
+
rootDir;
|
|
13
|
+
contexts = new Set();
|
|
14
|
+
closed = false;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.rootDir = resolve(options.rootDir);
|
|
18
|
+
ensureProtectedDirectory(this.rootDir);
|
|
19
|
+
removeStaleOwnedContexts(this.rootDir);
|
|
20
|
+
}
|
|
21
|
+
open(sessionId) {
|
|
22
|
+
if (this.closed)
|
|
23
|
+
throw new Error("Browser runner Session context provider is closed");
|
|
24
|
+
const owned = {
|
|
25
|
+
file: join(this.rootDir, `context-${randomBytes(18).toString("base64url")}.json`),
|
|
26
|
+
closed: false,
|
|
27
|
+
};
|
|
28
|
+
writeCredential(owned.file, sessionId, this.options.capabilities.issue(sessionId));
|
|
29
|
+
this.contexts.add(owned);
|
|
30
|
+
return {
|
|
31
|
+
childEnv: Object.freeze({ [RUNTIME_BROWSER_CONTEXT_FILE_ENV]: owned.file }),
|
|
32
|
+
rotate: (newSessionId) => {
|
|
33
|
+
if (owned.closed || this.closed) {
|
|
34
|
+
throw new Error("Browser runner Session context is closed");
|
|
35
|
+
}
|
|
36
|
+
writeCredential(owned.file, newSessionId, this.options.capabilities.issue(newSessionId));
|
|
37
|
+
},
|
|
38
|
+
close: () => this.closeContext(owned),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
closeAll() {
|
|
42
|
+
if (this.closed)
|
|
43
|
+
return;
|
|
44
|
+
this.closed = true;
|
|
45
|
+
for (const context of [...this.contexts])
|
|
46
|
+
this.closeContext(context);
|
|
47
|
+
}
|
|
48
|
+
closeContext(context) {
|
|
49
|
+
if (context.closed)
|
|
50
|
+
return;
|
|
51
|
+
context.closed = true;
|
|
52
|
+
this.contexts.delete(context);
|
|
53
|
+
removeOwnedRegularFile(context.file);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function ensureProtectedDirectory(path) {
|
|
57
|
+
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
58
|
+
const stat = lstatSync(path);
|
|
59
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
60
|
+
throw new Error("Browser runner Session context root must be a direct directory");
|
|
61
|
+
}
|
|
62
|
+
chmodSync(path, 0o700);
|
|
63
|
+
}
|
|
64
|
+
const OWNED_CONTEXT_NAME = /^context-[A-Za-z0-9_-]{24}\.json$/;
|
|
65
|
+
const OWNED_CONTEXT_TEMP_NAME = /^\.context-write-[A-Za-z0-9_-]{24}\.tmp$/;
|
|
66
|
+
/** A fresh provider has no live contexts, so exact-name protected files from
|
|
67
|
+
* the previous daemon incarnation are stale. Foreign names, links, unusual
|
|
68
|
+
* permissions, and directories are never treated as provider-owned. */
|
|
69
|
+
function removeStaleOwnedContexts(rootDir) {
|
|
70
|
+
for (const entry of readdirSync(rootDir, { withFileTypes: true })) {
|
|
71
|
+
if (!entry.isFile() || entry.isSymbolicLink())
|
|
72
|
+
continue;
|
|
73
|
+
if (!OWNED_CONTEXT_NAME.test(entry.name) && !OWNED_CONTEXT_TEMP_NAME.test(entry.name))
|
|
74
|
+
continue;
|
|
75
|
+
const file = join(rootDir, entry.name);
|
|
76
|
+
const stat = lstatSync(file);
|
|
77
|
+
if (!stat.isFile() || stat.isSymbolicLink() || (stat.mode & 0o777) !== 0o600)
|
|
78
|
+
continue;
|
|
79
|
+
rmSync(file, { force: true });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function writeCredential(file, sessionId, capability) {
|
|
83
|
+
const credential = parseRuntimeBrowserBootstrapCredential({ sessionId, capability });
|
|
84
|
+
const temporary = join(dirname(file), `.context-write-${randomBytes(18).toString("base64url")}.tmp`);
|
|
85
|
+
let descriptor;
|
|
86
|
+
try {
|
|
87
|
+
descriptor = openSync(temporary, "wx", 0o600);
|
|
88
|
+
writeFileSync(descriptor, `${JSON.stringify(credential)}\n`, "utf8");
|
|
89
|
+
fsyncSync(descriptor);
|
|
90
|
+
closeSync(descriptor);
|
|
91
|
+
descriptor = undefined;
|
|
92
|
+
renameSync(temporary, file);
|
|
93
|
+
chmodSync(file, 0o600);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (descriptor !== undefined)
|
|
97
|
+
closeSync(descriptor);
|
|
98
|
+
rmSync(temporary, { force: true });
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function removeOwnedRegularFile(file) {
|
|
103
|
+
try {
|
|
104
|
+
const stat = lstatSync(file);
|
|
105
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
106
|
+
return;
|
|
107
|
+
rmSync(file, { force: true });
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (error.code !== "ENOENT")
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RuntimeBrowserSurfaceInputEvent } from "@rynx-ai/protocol/runtime-browser-surface";
|
|
2
|
+
type SurfaceKeyboardEvent = Extract<RuntimeBrowserSurfaceInputEvent, {
|
|
3
|
+
type: "key.down" | "key.up";
|
|
4
|
+
}>;
|
|
5
|
+
export declare function cdpKeyboardEventParams(event: SurfaceKeyboardEvent, targetPlatform: string): Record<string, unknown>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
export function cdpKeyboardEventParams(event, targetPlatform) {
|
|
2
|
+
const windowsVirtualKeyCode = windowsVirtualKeyCodeFor(event.key, event.code);
|
|
3
|
+
const location = keyLocationFor(event.code);
|
|
4
|
+
const commands = event.type === "key.down"
|
|
5
|
+
? editingCommandsFor(event, targetPlatform)
|
|
6
|
+
: [];
|
|
7
|
+
return {
|
|
8
|
+
type: event.type === "key.down" ? "rawKeyDown" : "keyUp",
|
|
9
|
+
key: event.key,
|
|
10
|
+
code: event.code,
|
|
11
|
+
autoRepeat: event.repeat,
|
|
12
|
+
modifiers: modifierMask(event.modifiers),
|
|
13
|
+
...(windowsVirtualKeyCode === undefined ? {} : { windowsVirtualKeyCode }),
|
|
14
|
+
...(location === 0 ? {} : { location }),
|
|
15
|
+
...(location === 3 ? { isKeypad: true } : {}),
|
|
16
|
+
...(commands.length === 0 ? {} : { commands }),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const MAC_EDITING_COMMANDS = {
|
|
20
|
+
Backspace: "deleteBackward",
|
|
21
|
+
Delete: "deleteForward",
|
|
22
|
+
Enter: "insertNewline",
|
|
23
|
+
NumpadEnter: "insertNewline",
|
|
24
|
+
ArrowUp: "moveUp",
|
|
25
|
+
ArrowDown: "moveDown",
|
|
26
|
+
ArrowLeft: "moveLeft",
|
|
27
|
+
ArrowRight: "moveRight",
|
|
28
|
+
"Shift+ArrowUp": "moveUpAndModifySelection",
|
|
29
|
+
"Shift+ArrowDown": "moveDownAndModifySelection",
|
|
30
|
+
"Shift+ArrowLeft": "moveLeftAndModifySelection",
|
|
31
|
+
"Shift+ArrowRight": "moveRightAndModifySelection",
|
|
32
|
+
"Meta+KeyA": "selectAll",
|
|
33
|
+
"Meta+KeyZ": "undo",
|
|
34
|
+
"Shift+Meta+KeyZ": "redo",
|
|
35
|
+
};
|
|
36
|
+
const NON_MAC_EDITING_COMMANDS = {
|
|
37
|
+
"Control+KeyA": "selectAll",
|
|
38
|
+
};
|
|
39
|
+
function editingCommandsFor(event, targetPlatform) {
|
|
40
|
+
const commandKey = /^[A-Za-z]$/.test(event.key)
|
|
41
|
+
? `Key${event.key.toUpperCase()}`
|
|
42
|
+
: event.code;
|
|
43
|
+
const shortcut = [
|
|
44
|
+
event.modifiers.includes("shift") ? "Shift" : undefined,
|
|
45
|
+
event.modifiers.includes("control") ? "Control" : undefined,
|
|
46
|
+
event.modifiers.includes("alt") ? "Alt" : undefined,
|
|
47
|
+
event.modifiers.includes("meta") ? "Meta" : undefined,
|
|
48
|
+
commandKey,
|
|
49
|
+
].filter((part) => part !== undefined).join("+");
|
|
50
|
+
const command = targetPlatform === "darwin"
|
|
51
|
+
? MAC_EDITING_COMMANDS[shortcut]
|
|
52
|
+
: NON_MAC_EDITING_COMMANDS[shortcut];
|
|
53
|
+
return command === undefined ? [] : [command];
|
|
54
|
+
}
|
|
55
|
+
function modifierMask(modifiers) {
|
|
56
|
+
let mask = 0;
|
|
57
|
+
if (modifiers.includes("alt"))
|
|
58
|
+
mask |= 1;
|
|
59
|
+
if (modifiers.includes("control"))
|
|
60
|
+
mask |= 2;
|
|
61
|
+
if (modifiers.includes("meta"))
|
|
62
|
+
mask |= 4;
|
|
63
|
+
if (modifiers.includes("shift"))
|
|
64
|
+
mask |= 8;
|
|
65
|
+
return mask;
|
|
66
|
+
}
|
|
67
|
+
const WINDOWS_VIRTUAL_KEY_CODE_BY_KEY = {
|
|
68
|
+
Backspace: 0x08,
|
|
69
|
+
Tab: 0x09,
|
|
70
|
+
Clear: 0x0c,
|
|
71
|
+
Enter: 0x0d,
|
|
72
|
+
Shift: 0x10,
|
|
73
|
+
Control: 0x11,
|
|
74
|
+
Alt: 0x12,
|
|
75
|
+
Pause: 0x13,
|
|
76
|
+
CapsLock: 0x14,
|
|
77
|
+
Escape: 0x1b,
|
|
78
|
+
" ": 0x20,
|
|
79
|
+
PageUp: 0x21,
|
|
80
|
+
PageDown: 0x22,
|
|
81
|
+
End: 0x23,
|
|
82
|
+
Home: 0x24,
|
|
83
|
+
ArrowLeft: 0x25,
|
|
84
|
+
ArrowUp: 0x26,
|
|
85
|
+
ArrowRight: 0x27,
|
|
86
|
+
ArrowDown: 0x28,
|
|
87
|
+
PrintScreen: 0x2c,
|
|
88
|
+
Insert: 0x2d,
|
|
89
|
+
Delete: 0x2e,
|
|
90
|
+
Meta: 0x5b,
|
|
91
|
+
ContextMenu: 0x5d,
|
|
92
|
+
NumLock: 0x90,
|
|
93
|
+
ScrollLock: 0x91,
|
|
94
|
+
";": 0xba,
|
|
95
|
+
":": 0xba,
|
|
96
|
+
"=": 0xbb,
|
|
97
|
+
"+": 0xbb,
|
|
98
|
+
",": 0xbc,
|
|
99
|
+
"<": 0xbc,
|
|
100
|
+
"-": 0xbd,
|
|
101
|
+
_: 0xbd,
|
|
102
|
+
".": 0xbe,
|
|
103
|
+
">": 0xbe,
|
|
104
|
+
"/": 0xbf,
|
|
105
|
+
"?": 0xbf,
|
|
106
|
+
"`": 0xc0,
|
|
107
|
+
"~": 0xc0,
|
|
108
|
+
"[": 0xdb,
|
|
109
|
+
"{": 0xdb,
|
|
110
|
+
"\\": 0xdc,
|
|
111
|
+
"|": 0xdc,
|
|
112
|
+
"]": 0xdd,
|
|
113
|
+
"}": 0xdd,
|
|
114
|
+
"'": 0xde,
|
|
115
|
+
'"': 0xde,
|
|
116
|
+
};
|
|
117
|
+
const WINDOWS_VIRTUAL_KEY_CODE_BY_CODE = {
|
|
118
|
+
Backspace: 0x08,
|
|
119
|
+
Tab: 0x09,
|
|
120
|
+
Clear: 0x0c,
|
|
121
|
+
Enter: 0x0d,
|
|
122
|
+
NumpadEnter: 0x0d,
|
|
123
|
+
ShiftLeft: 0x10,
|
|
124
|
+
ShiftRight: 0x10,
|
|
125
|
+
ControlLeft: 0x11,
|
|
126
|
+
ControlRight: 0x11,
|
|
127
|
+
AltLeft: 0x12,
|
|
128
|
+
AltRight: 0x12,
|
|
129
|
+
Pause: 0x13,
|
|
130
|
+
CapsLock: 0x14,
|
|
131
|
+
Escape: 0x1b,
|
|
132
|
+
Space: 0x20,
|
|
133
|
+
PageUp: 0x21,
|
|
134
|
+
PageDown: 0x22,
|
|
135
|
+
End: 0x23,
|
|
136
|
+
Home: 0x24,
|
|
137
|
+
ArrowLeft: 0x25,
|
|
138
|
+
ArrowUp: 0x26,
|
|
139
|
+
ArrowRight: 0x27,
|
|
140
|
+
ArrowDown: 0x28,
|
|
141
|
+
PrintScreen: 0x2c,
|
|
142
|
+
Insert: 0x2d,
|
|
143
|
+
Delete: 0x2e,
|
|
144
|
+
MetaLeft: 0x5b,
|
|
145
|
+
MetaRight: 0x5c,
|
|
146
|
+
ContextMenu: 0x5d,
|
|
147
|
+
NumpadMultiply: 0x6a,
|
|
148
|
+
NumpadAdd: 0x6b,
|
|
149
|
+
NumpadComma: 0x6c,
|
|
150
|
+
NumpadSubtract: 0x6d,
|
|
151
|
+
NumpadDecimal: 0x6e,
|
|
152
|
+
NumpadDivide: 0x6f,
|
|
153
|
+
NumLock: 0x90,
|
|
154
|
+
ScrollLock: 0x91,
|
|
155
|
+
Semicolon: 0xba,
|
|
156
|
+
Equal: 0xbb,
|
|
157
|
+
Comma: 0xbc,
|
|
158
|
+
Minus: 0xbd,
|
|
159
|
+
Period: 0xbe,
|
|
160
|
+
Slash: 0xbf,
|
|
161
|
+
Backquote: 0xc0,
|
|
162
|
+
BracketLeft: 0xdb,
|
|
163
|
+
Backslash: 0xdc,
|
|
164
|
+
BracketRight: 0xdd,
|
|
165
|
+
Quote: 0xde,
|
|
166
|
+
IntlBackslash: 0xe2,
|
|
167
|
+
};
|
|
168
|
+
function windowsVirtualKeyCodeFor(key, code) {
|
|
169
|
+
if (code === "MetaLeft" || code === "MetaRight" || code === "IntlBackslash") {
|
|
170
|
+
return WINDOWS_VIRTUAL_KEY_CODE_BY_CODE[code];
|
|
171
|
+
}
|
|
172
|
+
if (code.startsWith("Numpad")) {
|
|
173
|
+
const numpadDigit = /^Numpad([0-9])$/.exec(code)?.[1];
|
|
174
|
+
if (numpadDigit && /^[0-9]$/.test(key))
|
|
175
|
+
return 0x60 + Number(numpadDigit);
|
|
176
|
+
const namedNumpadKey = key.length > 1 ? WINDOWS_VIRTUAL_KEY_CODE_BY_KEY[key] : undefined;
|
|
177
|
+
if (namedNumpadKey !== undefined)
|
|
178
|
+
return namedNumpadKey;
|
|
179
|
+
const numpadCode = WINDOWS_VIRTUAL_KEY_CODE_BY_CODE[code];
|
|
180
|
+
if (numpadCode !== undefined)
|
|
181
|
+
return numpadCode;
|
|
182
|
+
}
|
|
183
|
+
const functionKey = /^F([1-9]|1[0-9]|2[0-4])$/.exec(code)?.[1];
|
|
184
|
+
if (functionKey)
|
|
185
|
+
return 0x6f + Number(functionKey);
|
|
186
|
+
if (/^[A-Za-z]$/.test(key))
|
|
187
|
+
return key.toUpperCase().charCodeAt(0);
|
|
188
|
+
if (/^[0-9]$/.test(key))
|
|
189
|
+
return key.charCodeAt(0);
|
|
190
|
+
const logical = WINDOWS_VIRTUAL_KEY_CODE_BY_KEY[key];
|
|
191
|
+
if (logical !== undefined)
|
|
192
|
+
return logical;
|
|
193
|
+
const physicalDigit = /^Digit([0-9])$/.exec(code)?.[1];
|
|
194
|
+
if (physicalDigit)
|
|
195
|
+
return physicalDigit.charCodeAt(0);
|
|
196
|
+
const physical = WINDOWS_VIRTUAL_KEY_CODE_BY_CODE[code];
|
|
197
|
+
if (physical !== undefined)
|
|
198
|
+
return physical;
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
function keyLocationFor(code) {
|
|
202
|
+
if (code.startsWith("Numpad"))
|
|
203
|
+
return 3;
|
|
204
|
+
if (["ShiftLeft", "ControlLeft", "AltLeft", "MetaLeft"].includes(code))
|
|
205
|
+
return 1;
|
|
206
|
+
if (["ShiftRight", "ControlRight", "AltRight", "MetaRight"].includes(code))
|
|
207
|
+
return 2;
|
|
208
|
+
return 0;
|
|
209
|
+
}
|
package/dist/channel-store.d.ts
CHANGED
|
@@ -20,9 +20,3 @@ export declare function renameChannel(id: string, name: string): void;
|
|
|
20
20
|
export declare function setChannelOptions(id: string, options: Record<string, unknown>): void;
|
|
21
21
|
/** Remove a channel; its bound instance (if any) cascades away (FK ON DELETE CASCADE). */
|
|
22
22
|
export declare function removeChannel(id: string): void;
|
|
23
|
-
/**
|
|
24
|
-
* Bridge each channel's stored `options` into `process.env`, so a channel reads
|
|
25
|
-
* its credentials from the environment as usual. Existing env (including `.env`)
|
|
26
|
-
* wins — only undefined keys are filled.
|
|
27
|
-
*/
|
|
28
|
-
export declare function applyChannelEnv(): void;
|
package/dist/channel-store.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* (`options`), configured and authorized on its own. No agent binding here;
|
|
4
4
|
* that lives on an {@link import("./instance-store.js")} instance.
|
|
5
5
|
*
|
|
6
|
-
* Credentials stay in each channel's `options`
|
|
7
|
-
*
|
|
6
|
+
* Credentials stay in each channel's `options` and are sent only to that
|
|
7
|
+
* plugin runner instance; they are never copied into the daemon environment.
|
|
8
8
|
*/
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
10
|
import { db } from "./db.js";
|
|
@@ -63,19 +63,3 @@ export function setChannelOptions(id, options) {
|
|
|
63
63
|
export function removeChannel(id) {
|
|
64
64
|
db().prepare("DELETE FROM channels WHERE id = ?").run(id);
|
|
65
65
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Bridge each channel's stored `options` into `process.env`, so a channel reads
|
|
68
|
-
* its credentials from the environment as usual. Existing env (including `.env`)
|
|
69
|
-
* wins — only undefined keys are filled.
|
|
70
|
-
*/
|
|
71
|
-
export function applyChannelEnv() {
|
|
72
|
-
for (const channel of listChannels()) {
|
|
73
|
-
if (!channel.options)
|
|
74
|
-
continue;
|
|
75
|
-
for (const [key, value] of Object.entries(channel.options)) {
|
|
76
|
-
if (typeof value === "string" && process.env[key] === undefined) {
|
|
77
|
-
process.env[key] = value;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves an explicit Chrome for Testing channel or full version through the
|
|
3
|
+
* public GoogleChromeLabs JSON API.
|
|
4
|
+
*
|
|
5
|
+
* The upstream manifests intentionally contain download URLs but no archive
|
|
6
|
+
* digest. A resolved release therefore does not claim a SHA-256. The artifact
|
|
7
|
+
* store computes and records the archive digest when it installs these bytes.
|
|
8
|
+
*/
|
|
9
|
+
import { type ResolvedChromeForTestingBuildMetadata } from "./chrome-for-testing-store.js";
|
|
10
|
+
export type ChromeForTestingReleaseChannel = "Stable" | "Beta" | "Dev" | "Canary";
|
|
11
|
+
export type ChromeForTestingReleaseSelector = {
|
|
12
|
+
kind: "channel";
|
|
13
|
+
channel: ChromeForTestingReleaseChannel;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "version";
|
|
16
|
+
version: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Officially resolved release metadata. There is deliberately no `sha256`:
|
|
20
|
+
* Chrome for Testing's JSON API does not publish one.
|
|
21
|
+
*/
|
|
22
|
+
export interface ResolvedChromeForTestingRelease extends ResolvedChromeForTestingBuildMetadata {
|
|
23
|
+
manifestUrl: string;
|
|
24
|
+
}
|
|
25
|
+
export type ChromeForTestingMetadataFetcher = (url: string, init: RequestInit) => Promise<Response>;
|
|
26
|
+
export interface ResolveChromeForTestingReleaseOptions {
|
|
27
|
+
/** Injectable only so platform behavior can be tested on any build host. */
|
|
28
|
+
platform?: string;
|
|
29
|
+
/** Injectable only so platform behavior can be tested on any build host. */
|
|
30
|
+
arch?: string;
|
|
31
|
+
fetchMetadata?: ChromeForTestingMetadataFetcher;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
export declare function resolveChromeForTestingRelease(selector: ChromeForTestingReleaseSelector, options?: ResolveChromeForTestingReleaseOptions): Promise<ResolvedChromeForTestingRelease>;
|