@oharness/engine 0.7.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/agent/agent.d.ts +1 -0
  2. package/dist/agent/agent.js +5 -0
  3. package/dist/agent/agent.js.map +1 -1
  4. package/dist/agent/permissions.js +8 -0
  5. package/dist/agent/permissions.js.map +1 -1
  6. package/dist/auth/manager.d.ts +8 -1
  7. package/dist/auth/manager.js +26 -4
  8. package/dist/auth/manager.js.map +1 -1
  9. package/dist/auth/providers/official.js +3 -2
  10. package/dist/auth/providers/official.js.map +1 -1
  11. package/dist/auth/store.d.ts +14 -8
  12. package/dist/auth/store.js +87 -3
  13. package/dist/auth/store.js.map +1 -1
  14. package/dist/browser/cdp.d.ts +75 -0
  15. package/dist/browser/cdp.js +136 -0
  16. package/dist/browser/cdp.js.map +1 -0
  17. package/dist/browser/chrome.d.ts +19 -0
  18. package/dist/browser/chrome.js +306 -0
  19. package/dist/browser/chrome.js.map +1 -0
  20. package/dist/browser/extension.d.ts +35 -0
  21. package/dist/browser/extension.js +297 -0
  22. package/dist/browser/extension.js.map +1 -0
  23. package/dist/browser/host-manifest.d.ts +78 -0
  24. package/dist/browser/host-manifest.js +166 -0
  25. package/dist/browser/host-manifest.js.map +1 -0
  26. package/dist/browser/host.d.ts +27 -0
  27. package/dist/browser/host.js +311 -0
  28. package/dist/browser/host.js.map +1 -0
  29. package/dist/browser/index.d.ts +26 -0
  30. package/dist/browser/index.js +15 -0
  31. package/dist/browser/index.js.map +1 -0
  32. package/dist/browser/login-import.d.ts +98 -0
  33. package/dist/browser/login-import.js +244 -0
  34. package/dist/browser/login-import.js.map +1 -0
  35. package/dist/browser/plugin.d.ts +109 -0
  36. package/dist/browser/plugin.js +22 -0
  37. package/dist/browser/plugin.js.map +1 -0
  38. package/dist/browser/presence.d.ts +29 -0
  39. package/dist/browser/presence.js +54 -0
  40. package/dist/browser/presence.js.map +1 -0
  41. package/dist/browser/provider.d.ts +17 -0
  42. package/dist/browser/provider.js +13 -0
  43. package/dist/browser/provider.js.map +1 -0
  44. package/dist/browser/screencast.d.ts +42 -0
  45. package/dist/browser/screencast.js +99 -0
  46. package/dist/browser/screencast.js.map +1 -0
  47. package/dist/browser/session.d.ts +320 -0
  48. package/dist/browser/session.js +837 -0
  49. package/dist/browser/session.js.map +1 -0
  50. package/dist/browser/snapshot.d.ts +49 -0
  51. package/dist/browser/snapshot.js +145 -0
  52. package/dist/browser/snapshot.js.map +1 -0
  53. package/dist/browser/socket.d.ts +15 -0
  54. package/dist/browser/socket.js +32 -0
  55. package/dist/browser/socket.js.map +1 -0
  56. package/dist/browser/target.d.ts +112 -0
  57. package/dist/browser/target.js +20 -0
  58. package/dist/browser/target.js.map +1 -0
  59. package/dist/browser/tools.d.ts +3 -0
  60. package/dist/browser/tools.js +537 -0
  61. package/dist/browser/tools.js.map +1 -0
  62. package/dist/config.d.ts +14 -0
  63. package/dist/config.js.map +1 -1
  64. package/dist/core/errors.d.ts +12 -0
  65. package/dist/core/errors.js +14 -1
  66. package/dist/core/errors.js.map +1 -1
  67. package/dist/harness.d.ts +49 -0
  68. package/dist/harness.js +100 -4
  69. package/dist/harness.js.map +1 -1
  70. package/dist/index.d.ts +1 -0
  71. package/dist/index.js +1 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/mcp/builtin.d.ts +65 -8
  74. package/dist/mcp/builtin.js +66 -35
  75. package/dist/mcp/builtin.js.map +1 -1
  76. package/dist/mcp/ottoport-updates.d.ts +90 -0
  77. package/dist/mcp/ottoport-updates.js +228 -0
  78. package/dist/mcp/ottoport-updates.js.map +1 -0
  79. package/dist/providers/piai.js +7 -2
  80. package/dist/providers/piai.js.map +1 -1
  81. package/dist/runtime-plugins/builtin/studio.d.ts +87 -16
  82. package/dist/runtime-plugins/builtin/studio.js +374 -62
  83. package/dist/runtime-plugins/builtin/studio.js.map +1 -1
  84. package/dist/runtime-plugins/host.js +8 -2
  85. package/dist/runtime-plugins/host.js.map +1 -1
  86. package/dist/tools/define.d.ts +1 -0
  87. package/dist/tools/define.js +3 -0
  88. package/dist/tools/define.js.map +1 -1
  89. package/dist/tools/types.d.ts +11 -0
  90. package/dist/tools/types.js.map +1 -1
  91. package/package.json +4 -8
@@ -0,0 +1,297 @@
1
+ import { connect } from "node:net";
2
+ import { BrowserError } from "../core/errors.js";
3
+ import { CdpConnection } from "./cdp.js";
4
+ import { ScreencastHub } from "./screencast.js";
5
+ import { extensionSocketPath } from "./socket.js";
6
+ /** Open the socket the native messaging host listens on. */
7
+ export function connectExtensionChannel(path = extensionSocketPath()) {
8
+ return new Promise((resolve, reject) => {
9
+ const socket = connect(path);
10
+ const messageHandlers = new Set();
11
+ const closeHandlers = new Set();
12
+ let buffer = "";
13
+ socket.setEncoding("utf8");
14
+ socket.on("data", (chunk) => {
15
+ buffer += chunk;
16
+ let newline;
17
+ while ((newline = buffer.indexOf("\n")) !== -1) {
18
+ const line = buffer.slice(0, newline).trim();
19
+ buffer = buffer.slice(newline + 1);
20
+ if (line === "")
21
+ continue;
22
+ let parsed;
23
+ try {
24
+ parsed = JSON.parse(line);
25
+ }
26
+ catch {
27
+ continue;
28
+ }
29
+ for (const handler of messageHandlers)
30
+ handler(parsed);
31
+ }
32
+ });
33
+ socket.once("error", (err) => {
34
+ reject(new BrowserError(`no browser extension is connected. Add the OHarness extension to Chrome, Edge or Brave and keep that browser open; outside the desktop app, also run \`oharness-browser-host install\` once. (${err.message})`, err));
35
+ });
36
+ socket.on("close", () => {
37
+ for (const handler of closeHandlers)
38
+ handler("the extension disconnected");
39
+ });
40
+ socket.once("connect", () => {
41
+ socket.removeAllListeners("error");
42
+ socket.on("error", () => {
43
+ for (const handler of closeHandlers)
44
+ handler("the extension connection failed");
45
+ });
46
+ resolve({
47
+ send: (message) => socket.write(`${JSON.stringify(message)}\n`),
48
+ onMessage: (handler) => void messageHandlers.add(handler),
49
+ onClose: (handler) => void closeHandlers.add(handler),
50
+ close: () => socket.end(),
51
+ });
52
+ });
53
+ });
54
+ }
55
+ export async function extensionTarget(channel, context = {}) {
56
+ return new ExtensionTarget(channel ?? (await connectExtensionChannel()), context);
57
+ }
58
+ class ExtensionTarget {
59
+ kind = "extension";
60
+ channel;
61
+ cdp;
62
+ attached = new Map();
63
+ /** Tabs this conversation opened, as against the person's own it was pointed at. */
64
+ openedTabs = new Set();
65
+ control = new Map();
66
+ nextControlId = 1;
67
+ context;
68
+ constructor(channel, context = {}) {
69
+ this.channel = channel;
70
+ this.context = context;
71
+ this.cdp = new CdpConnection((message) => channel.send({ t: "cdp", ...message }));
72
+ channel.onMessage((message) => {
73
+ if (typeof message !== "object" || message === null)
74
+ return;
75
+ const envelope = message;
76
+ if (envelope["t"] === "cdp") {
77
+ // The person cancelled the debugging banner, or the tab went away. The
78
+ // page is no longer ours; the next use of it attaches again.
79
+ if (envelope["method"] === "Inspector.detached" && envelope["sessionId"] !== undefined) {
80
+ this.attached.delete(String(envelope["sessionId"]));
81
+ }
82
+ const { t: _ignored, ...rest } = envelope;
83
+ this.cdp.deliver(rest);
84
+ return;
85
+ }
86
+ if (envelope["t"] === "ctl") {
87
+ const reply = envelope;
88
+ const pending = this.control.get(reply.id);
89
+ if (!pending)
90
+ return;
91
+ this.control.delete(reply.id);
92
+ if (reply.error)
93
+ pending.reject(new BrowserError(reply.error));
94
+ else
95
+ pending.resolve(reply.result);
96
+ }
97
+ });
98
+ channel.onClose((reason) => {
99
+ this.cdp.fail(reason ?? "the extension disconnected");
100
+ for (const pending of this.control.values()) {
101
+ pending.reject(new BrowserError(reason ?? "the extension disconnected"));
102
+ }
103
+ this.control.clear();
104
+ this.attached.clear();
105
+ });
106
+ }
107
+ /**
108
+ * Ask the extension something CDP has no browser-level answer for.
109
+ *
110
+ * `chrome.debugger` is scoped to a tab, so listing and opening tabs go through
111
+ * `chrome.tabs` on the extension side and come back here as control replies.
112
+ */
113
+ ask(op, params = {}) {
114
+ const id = this.nextControlId++;
115
+ return new Promise((resolve, reject) => {
116
+ this.control.set(id, { resolve, reject });
117
+ const timer = setTimeout(() => {
118
+ if (!this.control.delete(id))
119
+ return;
120
+ reject(new BrowserError(`the extension did not answer ${op}`));
121
+ }, 15_000);
122
+ timer.unref?.();
123
+ try {
124
+ this.channel.send({ t: "ctl", id, op, ...params });
125
+ }
126
+ catch (err) {
127
+ this.control.delete(id);
128
+ reject(new BrowserError(`could not reach the extension`, err));
129
+ }
130
+ });
131
+ }
132
+ /**
133
+ * The tabs this conversation is driving — never the rest of the window.
134
+ *
135
+ * Every other target answers with all of its pages, because all of them are
136
+ * the agent's. Here the rest are the person's, and several conversations may
137
+ * be using the browser at once: attaching everything would put a banner on
138
+ * every tab and have each conversation start on whichever tab came first.
139
+ * The others are listed by `pageInfos` and attached when one is chosen.
140
+ */
141
+ async pages() {
142
+ const open = new Set((await this.tabRecords()).map((entry) => String(entry.tabId)));
143
+ return [...this.attached.values()].filter((page) => open.has(page.id));
144
+ }
145
+ async tabRecords() {
146
+ const result = await this.ask("tabs");
147
+ return Array.isArray(result) ? result : [];
148
+ }
149
+ async pageInfos() {
150
+ return (await this.tabRecords()).flatMap((entry) => {
151
+ if (typeof entry.tabId !== "number" && typeof entry.tabId !== "string")
152
+ return [];
153
+ const title = typeof entry.title === "string" ? entry.title.trim() : "";
154
+ return [{
155
+ id: String(entry.tabId),
156
+ url: typeof entry.url === "string" ? entry.url : "",
157
+ ...(title ? { title } : {}),
158
+ }];
159
+ });
160
+ }
161
+ async pageById(id) {
162
+ const known = (await this.pageInfos()).some((page) => page.id === id);
163
+ return known ? this.attach(id) : undefined;
164
+ }
165
+ async newPage(url = "about:blank") {
166
+ // Grouped per conversation, so the person can see which tabs belong to
167
+ // which piece of work, and several conversations never share a tab.
168
+ const title = this.context.label?.()?.trim();
169
+ const result = (await this.ask("newTab", {
170
+ url,
171
+ group: {
172
+ key: this.context.sessionId ?? "oharness",
173
+ ...(title ? { title } : {}),
174
+ // So the side panel, finding the person in this group, can open the
175
+ // conversation in its project.
176
+ ...(this.context.cwd ? { cwd: this.context.cwd } : {}),
177
+ },
178
+ }));
179
+ const tabId = result?.tabId;
180
+ if (typeof tabId !== "number" && typeof tabId !== "string") {
181
+ throw new BrowserError("the extension opened no tab");
182
+ }
183
+ this.openedTabs.add(String(tabId));
184
+ return this.attach(String(tabId));
185
+ }
186
+ opened(id) {
187
+ return this.openedTabs.has(id);
188
+ }
189
+ /** Let go of one tab — the banner goes, the tab stays where the person had it. */
190
+ async release(page) {
191
+ this.attached.delete(page.id);
192
+ await this.ask("detach", { tabId: page.id });
193
+ }
194
+ async activate(page) {
195
+ await this.ask("activate", { tabId: page.id });
196
+ }
197
+ /** The browser the extension runs in. An extension older than `info` cannot say. */
198
+ async describe() {
199
+ const result = (await this.ask("info"));
200
+ const name = typeof result?.name === "string" && result.name.trim() ? result.name.trim() : "Chrome";
201
+ return { name, ...(typeof result?.version === "string" ? { version: result.version } : {}) };
202
+ }
203
+ async attach(tabId) {
204
+ const existing = this.attached.get(tabId);
205
+ if (existing)
206
+ return existing;
207
+ await this.ask("attach", { tabId });
208
+ const page = new ExtensionPage(tabId, this.cdp, (op, params) => this.ask(op, params), () => {
209
+ this.attached.delete(tabId);
210
+ this.openedTabs.delete(tabId);
211
+ });
212
+ this.attached.set(tabId, page);
213
+ await page.prepare();
214
+ return page;
215
+ }
216
+ async close() {
217
+ // Detach rather than close: these are the person's own tabs, and shutting
218
+ // the harness down must not take their browsing with it.
219
+ for (const tabId of this.attached.keys()) {
220
+ await this.ask("detach", { tabId }).catch(() => { });
221
+ }
222
+ this.attached.clear();
223
+ this.channel.close();
224
+ }
225
+ }
226
+ class ExtensionPage {
227
+ id;
228
+ cdp;
229
+ ask;
230
+ forget;
231
+ screencast;
232
+ constructor(id, cdp, ask, forget) {
233
+ this.id = id;
234
+ this.cdp = cdp;
235
+ this.ask = ask;
236
+ this.forget = forget;
237
+ this.screencast = new ScreencastHub(this);
238
+ }
239
+ async prepare() {
240
+ for (const domain of ["Page.enable", "Runtime.enable", "DOM.enable", "Log.enable"]) {
241
+ await this.send(domain).catch(() => { });
242
+ }
243
+ }
244
+ send(method, params = {}) {
245
+ return this.cdp.send(method, params, this.id);
246
+ }
247
+ on(event, handler) {
248
+ return this.cdp.on(event, handler, this.id);
249
+ }
250
+ /** Frames, shared with every other watcher of this tab. */
251
+ frames(opts = {}) {
252
+ return this.screencast.frames(opts);
253
+ }
254
+ async input(event) {
255
+ if (event.type === "text") {
256
+ await this.send("Input.insertText", { text: event.text });
257
+ return;
258
+ }
259
+ if (event.type === "key") {
260
+ const type = event.action === "char" ? "char" : event.action === "down" ? "keyDown" : "keyUp";
261
+ await this.send("Input.dispatchKeyEvent", {
262
+ type,
263
+ ...(event.key ? { key: event.key } : {}),
264
+ ...(event.code ? { code: event.code } : {}),
265
+ ...(event.text ? { text: event.text } : {}),
266
+ modifiers: event.modifiers ?? 0,
267
+ });
268
+ return;
269
+ }
270
+ if (event.action === "wheel") {
271
+ await this.send("Input.dispatchMouseEvent", {
272
+ type: "mouseWheel",
273
+ x: event.x,
274
+ y: event.y,
275
+ deltaX: event.deltaX ?? 0,
276
+ deltaY: event.deltaY ?? 0,
277
+ modifiers: event.modifiers ?? 0,
278
+ });
279
+ return;
280
+ }
281
+ await this.send("Input.dispatchMouseEvent", {
282
+ type: event.action === "move" ? "mouseMoved" : event.action === "down" ? "mousePressed" : "mouseReleased",
283
+ x: event.x,
284
+ y: event.y,
285
+ button: event.button ?? "left",
286
+ clickCount: event.clickCount ?? (event.action === "move" ? 0 : 1),
287
+ modifiers: event.modifiers ?? 0,
288
+ });
289
+ }
290
+ /** Explicit tab close. Target shutdown still only detaches the person's other tabs. */
291
+ async close() {
292
+ this.forget();
293
+ this.screencast.close();
294
+ await this.ask("closeTab", { tabId: this.id });
295
+ }
296
+ }
297
+ //# sourceMappingURL=extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.js","sourceRoot":"","sources":["../../src/browser/extension.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Registering the native messaging host.
3
+ *
4
+ * Chrome will only start a host it has a manifest for, and will only let the
5
+ * named extensions talk to it. The manifest is a small JSON file in a
6
+ * per-browser directory — except on Windows, where a registry value points at
7
+ * the file.
8
+ *
9
+ * What the manifest names is a launcher this writes, never the host script
10
+ * itself. Chrome starts hosts with almost no environment: from the Dock on a
11
+ * Mac, `PATH` has no Homebrew in it, so a script beginning `#!/usr/bin/env node`
12
+ * fails with nothing but "native host has exited". Nor can a script inside a
13
+ * packaged app's archive be run at all. The launcher names the runtime by its
14
+ * absolute path — the Node running the install, or the desktop app's own
15
+ * executable told to behave as Node — and is the one file here that has to be
16
+ * executable.
17
+ */
18
+ export declare const HOST_NAME = "ai.oharness.browser";
19
+ /**
20
+ * The extension's id, pinned by the `key` in its manifest.
21
+ *
22
+ * Without a key an unpacked extension's id is derived from the directory it
23
+ * was loaded from, so it differs per machine and every install needed the
24
+ * person to copy it across by hand. The key makes it the same everywhere, so
25
+ * registering needs no argument and the desktop app can do it unasked.
26
+ */
27
+ export declare const EXTENSION_ID = "ccflolhofpnacaokjapodnphjjgiddmi";
28
+ /**
29
+ * Where somebody gets the extension, for the settings page to send them.
30
+ *
31
+ * PLACEHOLDER until the store listing exists. When it does, this becomes the
32
+ * listing's URL and EXTENSION_ID above becomes the listing's id.
33
+ */
34
+ export declare const EXTENSION_INSTALL_URL = "https://oharness.ai/extension";
35
+ interface BrowserSlot {
36
+ id: string;
37
+ label: string;
38
+ dir: string;
39
+ }
40
+ /** Every Chromium the manifest can be installed for, on this platform. */
41
+ export declare function nativeHostTargets(platform?: NodeJS.Platform, home?: string): BrowserSlot[];
42
+ /** Where each Chromium on Windows looks for a host, under HKEY_CURRENT_USER. */
43
+ export declare function windowsRegistryKeys(): string[];
44
+ export interface NativeHostManifest {
45
+ name: string;
46
+ description: string;
47
+ path: string;
48
+ type: "stdio";
49
+ allowed_origins: string[];
50
+ }
51
+ export declare function nativeHostManifest(executable: string, extensionIds: string[]): NativeHostManifest;
52
+ export interface InstallResult {
53
+ /** The launcher, then every manifest written. */
54
+ written: string[];
55
+ /** Set only when something could not be finished here, with what is left to run. */
56
+ manual?: string;
57
+ }
58
+ export interface InstallOptions {
59
+ /** An absolute path to Node, or to an Electron app that runs scripts as Node. */
60
+ runtime: string;
61
+ /** The host entry script the runtime is given. */
62
+ script: string;
63
+ /** The runtime is Electron: tell it to be Node rather than open a window. */
64
+ runAsNode?: boolean;
65
+ /** Extensions allowed to start the host. Defaults to the published one. */
66
+ extensionIds?: string[];
67
+ platform?: NodeJS.Platform;
68
+ home?: string;
69
+ /** Where the launcher and the Windows manifest go. Defaults to the user directory. */
70
+ dir?: string;
71
+ /** Restrict to these browser ids; default is every one this platform knows. */
72
+ browsers?: string[];
73
+ /** Writes one registry default value. Replaceable so tests never touch a registry. */
74
+ setRegistry?: (key: string, value: string) => Promise<void>;
75
+ }
76
+ export declare function hostLauncher(opts: Pick<InstallOptions, "runtime" | "script" | "runAsNode">, platform: NodeJS.Platform): string;
77
+ export declare function installNativeHost(opts: InstallOptions): Promise<InstallResult>;
78
+ export {};
@@ -0,0 +1,166 @@
1
+ import { execFile } from "node:child_process";
2
+ import { chmod, mkdir, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import path from "node:path";
5
+ import { BrowserError } from "../core/errors.js";
6
+ import { userDir } from "../paths.js";
7
+ /**
8
+ * Registering the native messaging host.
9
+ *
10
+ * Chrome will only start a host it has a manifest for, and will only let the
11
+ * named extensions talk to it. The manifest is a small JSON file in a
12
+ * per-browser directory — except on Windows, where a registry value points at
13
+ * the file.
14
+ *
15
+ * What the manifest names is a launcher this writes, never the host script
16
+ * itself. Chrome starts hosts with almost no environment: from the Dock on a
17
+ * Mac, `PATH` has no Homebrew in it, so a script beginning `#!/usr/bin/env node`
18
+ * fails with nothing but "native host has exited". Nor can a script inside a
19
+ * packaged app's archive be run at all. The launcher names the runtime by its
20
+ * absolute path — the Node running the install, or the desktop app's own
21
+ * executable told to behave as Node — and is the one file here that has to be
22
+ * executable.
23
+ */
24
+ export const HOST_NAME = "ai.oharness.browser";
25
+ /**
26
+ * The extension's id, pinned by the `key` in its manifest.
27
+ *
28
+ * Without a key an unpacked extension's id is derived from the directory it
29
+ * was loaded from, so it differs per machine and every install needed the
30
+ * person to copy it across by hand. The key makes it the same everywhere, so
31
+ * registering needs no argument and the desktop app can do it unasked.
32
+ */
33
+ export const EXTENSION_ID = "ccflolhofpnacaokjapodnphjjgiddmi";
34
+ /**
35
+ * Where somebody gets the extension, for the settings page to send them.
36
+ *
37
+ * PLACEHOLDER until the store listing exists. When it does, this becomes the
38
+ * listing's URL and EXTENSION_ID above becomes the listing's id.
39
+ */
40
+ export const EXTENSION_INSTALL_URL = "https://oharness.ai/extension";
41
+ /** Every Chromium the manifest can be installed for, on this platform. */
42
+ export function nativeHostTargets(platform = process.platform, home = homedir()) {
43
+ if (platform === "darwin") {
44
+ const support = path.join(home, "Library", "Application Support");
45
+ return [
46
+ { id: "chrome", label: "Google Chrome", dir: path.join(support, "Google", "Chrome", "NativeMessagingHosts") },
47
+ { id: "chrome-beta", label: "Chrome Beta", dir: path.join(support, "Google", "Chrome Beta", "NativeMessagingHosts") },
48
+ { id: "chromium", label: "Chromium", dir: path.join(support, "Chromium", "NativeMessagingHosts") },
49
+ { id: "edge", label: "Microsoft Edge", dir: path.join(support, "Microsoft Edge", "NativeMessagingHosts") },
50
+ { id: "brave", label: "Brave", dir: path.join(support, "BraveSoftware", "Brave-Browser", "NativeMessagingHosts") },
51
+ ];
52
+ }
53
+ if (platform === "linux") {
54
+ const config = process.env["XDG_CONFIG_HOME"] || path.join(home, ".config");
55
+ return [
56
+ { id: "chrome", label: "Google Chrome", dir: path.join(config, "google-chrome", "NativeMessagingHosts") },
57
+ { id: "chromium", label: "Chromium", dir: path.join(config, "chromium", "NativeMessagingHosts") },
58
+ { id: "edge", label: "Microsoft Edge", dir: path.join(config, "microsoft-edge", "NativeMessagingHosts") },
59
+ { id: "brave", label: "Brave", dir: path.join(config, "BraveSoftware", "brave-browser", "NativeMessagingHosts") },
60
+ ];
61
+ }
62
+ // Windows reads a registry value rather than a directory; see windowsRegistryKeys.
63
+ return [];
64
+ }
65
+ /** Where each Chromium on Windows looks for a host, under HKEY_CURRENT_USER. */
66
+ export function windowsRegistryKeys() {
67
+ return [
68
+ "Google\\Chrome",
69
+ "Chromium",
70
+ "Microsoft\\Edge",
71
+ "BraveSoftware\\Brave-Browser",
72
+ ].map((vendor) => `HKCU\\Software\\${vendor}\\NativeMessagingHosts\\${HOST_NAME}`);
73
+ }
74
+ export function nativeHostManifest(executable, extensionIds) {
75
+ if (extensionIds.length === 0) {
76
+ throw new BrowserError("an extension id is required: Chrome will not let an unlisted extension start a host");
77
+ }
78
+ for (const id of extensionIds) {
79
+ // Chrome ids are 32 characters of a-p. A wrong one fails silently at
80
+ // connect time, which is a miserable thing to debug, so it is caught here.
81
+ if (!/^[a-p]{32}$/.test(id)) {
82
+ throw new BrowserError(`"${id}" is not a Chrome extension id (32 letters a-p, shown on chrome://extensions)`);
83
+ }
84
+ }
85
+ return {
86
+ name: HOST_NAME,
87
+ description: "OHarness browser bridge",
88
+ path: executable,
89
+ type: "stdio",
90
+ allowed_origins: extensionIds.map((id) => `chrome-extension://${id}/`),
91
+ };
92
+ }
93
+ /** A path inside single quotes, for sh. */
94
+ function shellQuote(value) {
95
+ return `'${value.replace(/'/g, "'\\''")}'`;
96
+ }
97
+ export function hostLauncher(opts, platform) {
98
+ if (platform === "win32") {
99
+ // Nothing may reach stdout but the protocol, so the batch file echoes nothing.
100
+ return [
101
+ "@echo off",
102
+ ...(opts.runAsNode ? ["set ELECTRON_RUN_AS_NODE=1"] : []),
103
+ `"${opts.runtime}" "${opts.script}" %*`,
104
+ "",
105
+ ].join("\r\n");
106
+ }
107
+ return [
108
+ "#!/bin/sh",
109
+ "# Written by OHarness. Chrome runs this to reach the harness; see host-manifest.ts.",
110
+ `${opts.runAsNode ? "ELECTRON_RUN_AS_NODE=1 " : ""}exec ${shellQuote(opts.runtime)} ${shellQuote(opts.script)} "$@"`,
111
+ "",
112
+ ].join("\n");
113
+ }
114
+ function regAdd(key, value) {
115
+ return new Promise((resolve, reject) => {
116
+ execFile("reg", ["add", key, "/ve", "/t", "REG_SZ", "/d", value, "/f"], { windowsHide: true }, (err) => err ? reject(err) : resolve());
117
+ });
118
+ }
119
+ export async function installNativeHost(opts) {
120
+ const platform = opts.platform ?? process.platform;
121
+ const dir = opts.dir ?? (opts.home ? path.join(opts.home, ".oharness") : userDir());
122
+ const manifestFor = (launcher) => `${JSON.stringify(nativeHostManifest(launcher, opts.extensionIds ?? [EXTENSION_ID]), null, 2)}\n`;
123
+ const launcher = path.join(dir, platform === "win32" ? "browser-host.bat" : "browser-host");
124
+ await mkdir(dir, { recursive: true });
125
+ await writeFile(launcher, hostLauncher(opts, platform), "utf8");
126
+ if (platform !== "win32")
127
+ await chmod(launcher, 0o755);
128
+ const body = manifestFor(launcher);
129
+ if (platform === "win32") {
130
+ const file = path.join(dir, `${HOST_NAME}.json`);
131
+ await writeFile(file, body, "utf8");
132
+ const setRegistry = opts.setRegistry ?? regAdd;
133
+ const failed = [];
134
+ for (const key of windowsRegistryKeys()) {
135
+ await setRegistry(key, file).catch(() => failed.push(key));
136
+ }
137
+ if (failed.length === windowsRegistryKeys().length) {
138
+ return {
139
+ written: [launcher, file],
140
+ manual: `The registry could not be written. Run:\n reg add "${failed[0]}" /ve /t REG_SZ /d "${file}" /f`,
141
+ };
142
+ }
143
+ return { written: [launcher, file] };
144
+ }
145
+ const slots = nativeHostTargets(platform, opts.home).filter((slot) => !opts.browsers || opts.browsers.includes(slot.id));
146
+ const written = [launcher];
147
+ for (const slot of slots) {
148
+ // Written for every browser this platform knows, whether or not it is
149
+ // installed: the directory is created either way, and a manifest for a
150
+ // browser somebody installs next week costs a few hundred bytes now.
151
+ const file = path.join(slot.dir, `${HOST_NAME}.json`);
152
+ try {
153
+ await mkdir(slot.dir, { recursive: true });
154
+ await writeFile(file, body, "utf8");
155
+ written.push(file);
156
+ }
157
+ catch {
158
+ /* a browser whose support directory cannot be written is simply skipped */
159
+ }
160
+ }
161
+ if (written.length === 1) {
162
+ throw new BrowserError("could not write the host manifest for any browser");
163
+ }
164
+ return { written };
165
+ }
166
+ //# sourceMappingURL=host-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-manifest.js","sourceRoot":"","sources":["../../src/browser/host-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export interface HostOptions {
2
+ /** Chrome's messages in. Required: the engine owns no process streams. */
3
+ input: NodeJS.ReadableStream;
4
+ /** Chrome's messages out — the protocol itself, never a log. */
5
+ output: NodeJS.WritableStream;
6
+ socketPath?: string;
7
+ /** Told what went wrong. Dropped if the caller does not want to know. */
8
+ warn?: (message: string) => void;
9
+ }
10
+ export interface RunningHost {
11
+ readonly socketPath: string;
12
+ /**
13
+ * Settles once the host has shut down — Chrome closed its stdin, or `close`
14
+ * was called. The process running it should then exit explicitly: left to
15
+ * wind down on its own, Node closes every handle on the way out, and closing
16
+ * a unix socket server deletes its path even when a newer host owns it now.
17
+ */
18
+ readonly closed: Promise<void>;
19
+ close(): Promise<void>;
20
+ }
21
+ /** Split Chrome's length-prefixed stream into messages. */
22
+ export declare class NativeMessageFramer {
23
+ private buffer;
24
+ push(chunk: Buffer, onMessage: (message: unknown) => void, onError?: (reason: string) => void): void;
25
+ }
26
+ export declare function encodeNativeMessage(message: unknown): Buffer;
27
+ export declare function runBrowserHost(opts: HostOptions): Promise<RunningHost>;