@vymalo/opencode-browser 0.7.1 → 0.7.3
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/README.md +2 -1
- package/dist/endpoint.d.ts +7 -0
- package/dist/endpoint.js +26 -11
- package/dist/endpoint.js.map +1 -1
- package/dist/lib.d.ts +2 -1
- package/dist/lib.js +2 -1
- package/dist/lib.js.map +1 -1
- package/dist/node-transport.d.ts +12 -0
- package/dist/node-transport.js +52 -0
- package/dist/node-transport.js.map +1 -0
- package/dist/opencode.d.ts +1 -1
- package/dist/opencode.js +24 -27
- package/dist/opencode.js.map +1 -1
- package/dist/transport.d.ts +2 -7
- package/dist/transport.js +2 -60
- package/dist/transport.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -12,7 +12,8 @@ can call and hosts a localhost WebSocket **bridge**. A companion **browser exten
|
|
|
12
12
|
(Chromium MV3 + Firefox) connects to the bridge and drives real tabs.
|
|
13
13
|
|
|
14
14
|
> Browser extensions can't host servers, so the plugin is the server and the extension dials
|
|
15
|
-
> out to it. The
|
|
15
|
+
> out to it. The bridge is served with the `ws` package, which runs under both runtimes OpenCode
|
|
16
|
+
> uses — Bun (CLI / `opencode web`) and Node (the desktop app) — so it works in either.
|
|
16
17
|
|
|
17
18
|
```mermaid
|
|
18
19
|
flowchart LR
|
package/dist/endpoint.d.ts
CHANGED
|
@@ -14,6 +14,13 @@ export interface EndpointOptions {
|
|
|
14
14
|
label?: string;
|
|
15
15
|
/** Delay before retrying election after a drop. */
|
|
16
16
|
reelectMs?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Fired whenever this endpoint becomes the host — on the initial election AND
|
|
19
|
+
* on any later failover re-election. Use it for host-only side effects like
|
|
20
|
+
* advertising the bridge token, so they recur on every host transition rather
|
|
21
|
+
* than only once at startup.
|
|
22
|
+
*/
|
|
23
|
+
onHost?: () => void;
|
|
17
24
|
}
|
|
18
25
|
export interface EndpointDeps {
|
|
19
26
|
logger: Logger;
|
package/dist/endpoint.js
CHANGED
|
@@ -34,26 +34,39 @@ export async function createEndpoint(opts, deps) {
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
mode = "electing";
|
|
37
|
-
// 1) Try to host (win the bind).
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
port: opts.port,
|
|
42
|
-
token: opts.token,
|
|
43
|
-
executor: opts.executor,
|
|
44
|
-
timeoutMs: opts.timeoutMs
|
|
45
|
-
}, { logger: deps.logger, transport });
|
|
37
|
+
// 1) Try to host (win the bind). Everything is inside the try so a transport
|
|
38
|
+
// that throws at construction (or a Broker that fails to start) degrades to
|
|
39
|
+
// guest instead of crashing the plugin load.
|
|
40
|
+
let candidate = null;
|
|
46
41
|
try {
|
|
42
|
+
const transport = deps.createServerTransport();
|
|
43
|
+
candidate = new Broker({
|
|
44
|
+
host: opts.host,
|
|
45
|
+
port: opts.port,
|
|
46
|
+
token: opts.token,
|
|
47
|
+
executor: opts.executor,
|
|
48
|
+
timeoutMs: opts.timeoutMs
|
|
49
|
+
}, { logger: deps.logger, transport });
|
|
47
50
|
await candidate.start();
|
|
48
51
|
broker = candidate;
|
|
49
52
|
current = candidate.createLocalAgent();
|
|
50
53
|
mode = "host";
|
|
51
54
|
deps.logger.info("browser_endpoint_mode", { mode: "host" });
|
|
55
|
+
// Isolate the side-effect callback: a throw here must NOT reach the outer
|
|
56
|
+
// catch, which would stop the freshly-started broker and degrade to guest.
|
|
57
|
+
try {
|
|
58
|
+
opts.onHost?.();
|
|
59
|
+
}
|
|
60
|
+
catch (onHostErr) {
|
|
61
|
+
deps.logger.error("browser_endpoint_onhost_error", {
|
|
62
|
+
message: onHostErr instanceof Error ? onHostErr.message : String(onHostErr)
|
|
63
|
+
});
|
|
64
|
+
}
|
|
52
65
|
return;
|
|
53
66
|
}
|
|
54
67
|
catch (err) {
|
|
55
68
|
try {
|
|
56
|
-
candidate
|
|
69
|
+
candidate?.stop();
|
|
57
70
|
}
|
|
58
71
|
catch {
|
|
59
72
|
/* never bound */
|
|
@@ -79,7 +92,9 @@ export async function createEndpoint(opts, deps) {
|
|
|
79
92
|
agentClient = client;
|
|
80
93
|
current = client;
|
|
81
94
|
mode = "guest";
|
|
82
|
-
|
|
95
|
+
// Guest election is routine and happens once per plugin load (every
|
|
96
|
+
// session) — keep it at debug so it doesn't dominate the log stream.
|
|
97
|
+
deps.logger.debug("browser_endpoint_mode", { mode: "guest" });
|
|
83
98
|
}
|
|
84
99
|
catch {
|
|
85
100
|
scheduleReelect();
|
package/dist/endpoint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint.js","sourceRoot":"","sources":["../src/endpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAsB,MAAM,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAwB,WAAW,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"endpoint.js","sourceRoot":"","sources":["../src/endpoint.ts"],"names":[],"mappings":"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"}
|
package/dist/lib.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export { type BrowserPluginFactoryOptions, createBrowserPlugin, OpencodeBrowserP
|
|
|
2
2
|
export { type AgentEndpoint, Broker, BrokerError, type BrokerDeps, type BrokerOptions } from "./broker.js";
|
|
3
3
|
export { AgentClient, AgentClientError, type AgentClientDeps, type AgentClientOptions, type AgentSocket, type AgentSocketFactory, type AgentSocketHandlers } from "./agent-client.js";
|
|
4
4
|
export { createEndpoint, type Endpoint, type EndpointDeps, type EndpointMode, type EndpointOptions } from "./endpoint.js";
|
|
5
|
-
export { type BridgeTransport, type ClientConnection,
|
|
5
|
+
export { type BridgeTransport, type ClientConnection, isAddrInUse, type TransportHandlers } from "./transport.js";
|
|
6
|
+
export { createNodeAgentSocket, createNodeTransport } from "./node-transport.js";
|
|
6
7
|
export { type BridgeFile, readBridgeFile, resolveSharedToken, type TokenSource, writeBridgeFile } from "./token-file.js";
|
|
7
8
|
export { createBrowserTools, type SaveScreenshot, type SendFn, type ToolDeps } from "./tools.js";
|
|
8
9
|
export { BROWSER_TOOLS, DEFAULT_GROUPS, type NeutralResult, TOOL_GROUPS, type ToolGroup, type ToolSpec } from "./catalog.js";
|
package/dist/lib.js
CHANGED
|
@@ -2,7 +2,8 @@ export { createBrowserPlugin, OpencodeBrowserPlugin } from "./opencode.js";
|
|
|
2
2
|
export { Broker, BrokerError } from "./broker.js";
|
|
3
3
|
export { AgentClient, AgentClientError } from "./agent-client.js";
|
|
4
4
|
export { createEndpoint } from "./endpoint.js";
|
|
5
|
-
export {
|
|
5
|
+
export { isAddrInUse } from "./transport.js";
|
|
6
|
+
export { createNodeAgentSocket, createNodeTransport } from "./node-transport.js";
|
|
6
7
|
export { readBridgeFile, resolveSharedToken, writeBridgeFile } from "./token-file.js";
|
|
7
8
|
export { createBrowserTools } from "./tools.js";
|
|
8
9
|
export { BROWSER_TOOLS, DEFAULT_GROUPS, TOOL_GROUPS } from "./catalog.js";
|
package/dist/lib.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAEL,MAAM,EACN,WAAW,EAGZ,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,gBAAgB,EAMjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EAKf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAGL,
|
|
1
|
+
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAEL,MAAM,EACN,WAAW,EAGZ,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,gBAAgB,EAMjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EAKf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAGL,WAAW,EAEZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,OAAO,EAEL,cAAc,EACd,kBAAkB,EAElB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,kBAAkB,EAAmD,MAAM,YAAY,CAAC;AAEjG,OAAO,EACL,aAAa,EACb,cAAc,EAEd,WAAW,EAGZ,MAAM,cAAc,CAAC;AAEtB,OAAO,EAA+C,YAAY,EAAE,MAAM,aAAa,CAAC;AAExF,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EAIrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,eAAe,EAKf,WAAW,EACX,WAAW,EAIX,UAAU,EACV,MAAM,EACN,gBAAgB,EAGjB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AgentSocket, AgentSocketHandlers } from "./agent-client.js";
|
|
2
|
+
import type { BridgeTransport } from "./transport.js";
|
|
3
|
+
/**
|
|
4
|
+
* `BridgeTransport` backed by the Node `ws` package. This is the bridge's single
|
|
5
|
+
* host transport: `ws` runs under both Node *and* Bun, so it covers every
|
|
6
|
+
* runtime OpenCode loads the plugin in — the Bun CLI/web *and* the Node desktop
|
|
7
|
+
* app — without a Bun-specific code path. `listen` resolves once bound and
|
|
8
|
+
* rejects on `EADDRINUSE`, which drives the endpoint's host-vs-guest election.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createNodeTransport(): BridgeTransport;
|
|
11
|
+
/** Agent-socket factory backed by the Node `ws` client (guest mode). */
|
|
12
|
+
export declare function createNodeAgentSocket(url: string, handlers: AgentSocketHandlers): AgentSocket;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
2
|
+
/**
|
|
3
|
+
* `BridgeTransport` backed by the Node `ws` package. This is the bridge's single
|
|
4
|
+
* host transport: `ws` runs under both Node *and* Bun, so it covers every
|
|
5
|
+
* runtime OpenCode loads the plugin in — the Bun CLI/web *and* the Node desktop
|
|
6
|
+
* app — without a Bun-specific code path. `listen` resolves once bound and
|
|
7
|
+
* rejects on `EADDRINUSE`, which drives the endpoint's host-vs-guest election.
|
|
8
|
+
*/
|
|
9
|
+
export function createNodeTransport() {
|
|
10
|
+
let server = null;
|
|
11
|
+
return {
|
|
12
|
+
listen({ host, port }, handlers) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const wss = new WebSocketServer({ host, port });
|
|
15
|
+
server = wss;
|
|
16
|
+
wss.once("listening", () => resolve());
|
|
17
|
+
wss.once("error", (err) => reject(err));
|
|
18
|
+
wss.on("connection", (ws) => {
|
|
19
|
+
const conn = {
|
|
20
|
+
send: (data) => ws.send(data),
|
|
21
|
+
close: () => ws.close()
|
|
22
|
+
};
|
|
23
|
+
handlers.onOpen(conn);
|
|
24
|
+
ws.on("message", (data) => handlers.onMessage(conn, data.toString()));
|
|
25
|
+
ws.on("close", () => handlers.onClose(conn));
|
|
26
|
+
ws.on("error", () => {
|
|
27
|
+
/* close follows */
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
stop() {
|
|
33
|
+
server?.close();
|
|
34
|
+
server = null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Agent-socket factory backed by the Node `ws` client (guest mode). */
|
|
39
|
+
export function createNodeAgentSocket(url, handlers) {
|
|
40
|
+
const ws = new WebSocket(url);
|
|
41
|
+
ws.on("open", () => handlers.onOpen());
|
|
42
|
+
ws.on("message", (data) => handlers.onMessage(data.toString()));
|
|
43
|
+
ws.on("close", () => handlers.onClose());
|
|
44
|
+
ws.on("error", () => {
|
|
45
|
+
/* close follows */
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
send: (data) => ws.send(data),
|
|
49
|
+
close: () => ws.close()
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=node-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-transport.js","sourceRoot":"","sources":["../src/node-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAKhD;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB;IACjC,IAAI,MAAM,GAA2B,IAAI,CAAC;IAE1C,OAAO;QACL,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ;YAC7B,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC3C,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChD,MAAM,GAAG,GAAG,CAAC;gBACb,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;gBACvC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBACxC,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAa,EAAE,EAAE;oBACrC,MAAM,IAAI,GAAqB;wBAC7B,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;wBAC7B,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE;qBACxB,CAAC;oBACF,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBACtB,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;oBACtE,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;oBAC7C,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;wBAClB,mBAAmB;oBACrB,CAAC,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QACD,IAAI;YACF,MAAM,EAAE,KAAK,EAAE,CAAC;YAChB,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAE,QAA6B;IAC9E,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC;IAC9B,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAChE,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAClB,mBAAmB;IACrB,CAAC,CAAC,CAAC;IACH,OAAO;QACL,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;QAC7B,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE;KACxB,CAAC;AACJ,CAAC"}
|
package/dist/opencode.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Plugin } from "@opencode-ai/plugin";
|
|
|
2
2
|
import type { AgentSocketFactory } from "./agent-client.js";
|
|
3
3
|
import { type Logger } from "./logging.js";
|
|
4
4
|
import { type SaveScreenshot } from "./tools.js";
|
|
5
|
-
import {
|
|
5
|
+
import type { BridgeTransport } from "./transport.js";
|
|
6
6
|
export interface BrowserPluginFactoryOptions {
|
|
7
7
|
/** Inject a logger (defaults to the OpenCode-piped logger). */
|
|
8
8
|
logger?: Logger;
|
package/dist/opencode.js
CHANGED
|
@@ -3,21 +3,8 @@ import { DEFAULT_GROUPS, TOOL_GROUPS } from "./catalog.js";
|
|
|
3
3
|
import { createEndpoint } from "./endpoint.js";
|
|
4
4
|
import { createJsonConsoleLogger, DEFAULT_LOG_LEVEL, fromOpenCodeLogLevel, LOG_LEVEL_PRIORITY } from "./logging.js";
|
|
5
5
|
import { resolveSharedToken, writeBridgeFile } from "./token-file.js";
|
|
6
|
+
import { createNodeAgentSocket, createNodeTransport } from "./node-transport.js";
|
|
6
7
|
import { createBrowserTools } from "./tools.js";
|
|
7
|
-
import { createBunTransport } from "./transport.js";
|
|
8
|
-
/** Agent-socket factory using the runtime's global WebSocket (Bun provides one). */
|
|
9
|
-
const bunAgentSocket = (url, handlers) => {
|
|
10
|
-
const Ctor = globalThis.WebSocket;
|
|
11
|
-
if (!Ctor) {
|
|
12
|
-
throw new Error("global WebSocket is not available in this runtime");
|
|
13
|
-
}
|
|
14
|
-
const ws = new Ctor(url);
|
|
15
|
-
ws.addEventListener("open", () => handlers.onOpen());
|
|
16
|
-
ws.addEventListener("message", (event) => handlers.onMessage(typeof event.data === "string" ? event.data : String(event.data)));
|
|
17
|
-
ws.addEventListener("close", () => handlers.onClose());
|
|
18
|
-
ws.addEventListener("error", () => { });
|
|
19
|
-
return { send: (data) => ws.send(data), close: () => ws.close() };
|
|
20
|
-
};
|
|
21
8
|
function resolveGroups(raw) {
|
|
22
9
|
if (!Array.isArray(raw)) {
|
|
23
10
|
return [...DEFAULT_GROUPS];
|
|
@@ -40,11 +27,19 @@ const DEFAULTS = {
|
|
|
40
27
|
*/
|
|
41
28
|
function createOpenCodeLogger(client, getMinLevel) {
|
|
42
29
|
const fallback = createJsonConsoleLogger("debug");
|
|
30
|
+
// OpenCode already captures plugin logs via client.app.log (and filters them
|
|
31
|
+
// by its own log level). Mirroring every event to stdout on top of that is
|
|
32
|
+
// what floods the terminal (e.g. `opencode web` re-loads the plugin per
|
|
33
|
+
// session). So only mirror warn/error to the JSON console by default; set
|
|
34
|
+
// VYMALO_PLUGIN_CONSOLE_LOG=1 to restore full console output for debugging.
|
|
35
|
+
const consoleAll = /^(1|true|yes|on)$/i.test(process.env.VYMALO_PLUGIN_CONSOLE_LOG ?? "");
|
|
43
36
|
const write = (level, event, fields) => {
|
|
44
37
|
if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[getMinLevel()]) {
|
|
45
38
|
return;
|
|
46
39
|
}
|
|
47
|
-
|
|
40
|
+
if (consoleAll || level === "warn" || level === "error") {
|
|
41
|
+
fallback[level](event, fields);
|
|
42
|
+
}
|
|
48
43
|
void client.app
|
|
49
44
|
.log({ body: { service: PLUGIN_SERVICE_NAME, level, message: event, extra: fields } })
|
|
50
45
|
.catch(() => {
|
|
@@ -98,6 +93,16 @@ export function createBrowserPlugin(factoryOptions = {}) {
|
|
|
98
93
|
// Share the token across adapters via the per-user state file (explicit wins).
|
|
99
94
|
const { token, source } = resolveSharedToken(options.port, tokenProvided ? options.token : undefined, generateToken);
|
|
100
95
|
writeBridgeFile(options.port, token);
|
|
96
|
+
// Only the host advertises the token (guests reuse the same one from the
|
|
97
|
+
// shared file — reprinting it per session is just noise, and re-emits the
|
|
98
|
+
// secret). Fired via onHost so a host reached by failover re-election still
|
|
99
|
+
// advertises it, not only the initial host. The `paste_into_extension` field
|
|
100
|
+
// name dodges the logger's redaction filter so the value stays copyable.
|
|
101
|
+
const advertiseToken = () => {
|
|
102
|
+
if (source !== "explicit") {
|
|
103
|
+
logger.info("browser_bridge_token", { paste_into_extension: token, source, mode: "host" });
|
|
104
|
+
}
|
|
105
|
+
};
|
|
101
106
|
// Auto-elect: host the broker (win the bind) or join an existing one as a guest.
|
|
102
107
|
const endpoint = await createEndpoint({
|
|
103
108
|
host: options.host,
|
|
@@ -105,11 +110,12 @@ export function createBrowserPlugin(factoryOptions = {}) {
|
|
|
105
110
|
token,
|
|
106
111
|
executor: executorProvided ? options.executor : undefined,
|
|
107
112
|
timeoutMs: options.timeoutMs,
|
|
108
|
-
label: "opencode-plugin"
|
|
113
|
+
label: "opencode-plugin",
|
|
114
|
+
onHost: advertiseToken
|
|
109
115
|
}, {
|
|
110
116
|
logger,
|
|
111
|
-
createServerTransport: factoryOptions.createServerTransport ??
|
|
112
|
-
createAgentSocket: factoryOptions.createAgentSocket ??
|
|
117
|
+
createServerTransport: factoryOptions.createServerTransport ?? createNodeTransport,
|
|
118
|
+
createAgentSocket: factoryOptions.createAgentSocket ?? createNodeAgentSocket
|
|
113
119
|
});
|
|
114
120
|
// When OpenCode exits, hand the browser back automatically — the user
|
|
115
121
|
// shouldn't have to click Disconnect.
|
|
@@ -121,15 +127,6 @@ export function createBrowserPlugin(factoryOptions = {}) {
|
|
|
121
127
|
/* best-effort */
|
|
122
128
|
}
|
|
123
129
|
});
|
|
124
|
-
if (source !== "explicit") {
|
|
125
|
-
// Make the token visible so it can be pasted into the extension. The
|
|
126
|
-
// `paste_into_extension` field name dodges the logger's redaction filter.
|
|
127
|
-
logger.info("browser_bridge_token", {
|
|
128
|
-
paste_into_extension: token,
|
|
129
|
-
source,
|
|
130
|
-
mode: endpoint.mode()
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
130
|
const tools = createBrowserTools({
|
|
134
131
|
send: endpoint.send,
|
|
135
132
|
options,
|
package/dist/opencode.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EAEpB,kBAAkB,EAGnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"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"}
|
package/dist/transport.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The transport seam for the bridge/broker. The real implementation
|
|
3
|
-
* `
|
|
2
|
+
* The transport seam for the bridge/broker. The real implementation is the Node
|
|
3
|
+
* `ws` variant in `node-transport.ts` (which runs under both Node and Bun);
|
|
4
4
|
* tests inject a fake. `listen` is **async** and resolves only once the port is
|
|
5
5
|
* actually bound (and rejects on `EADDRINUSE`) — that's what the broker election
|
|
6
6
|
* relies on to decide host-vs-guest.
|
|
@@ -25,8 +25,3 @@ export interface BridgeTransport {
|
|
|
25
25
|
}
|
|
26
26
|
/** True for an "address already in use" style bind error (→ become a guest). */
|
|
27
27
|
export declare function isAddrInUse(err: unknown): boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Real transport backed by `Bun.serve` — the production path inside OpenCode
|
|
30
|
-
* (which runs on Bun). Throws if invoked outside Bun.
|
|
31
|
-
*/
|
|
32
|
-
export declare function createBunTransport(): BridgeTransport;
|
package/dist/transport.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The transport seam for the bridge/broker. The real implementation
|
|
3
|
-
* `
|
|
2
|
+
* The transport seam for the bridge/broker. The real implementation is the Node
|
|
3
|
+
* `ws` variant in `node-transport.ts` (which runs under both Node and Bun);
|
|
4
4
|
* tests inject a fake. `listen` is **async** and resolves only once the port is
|
|
5
5
|
* actually bound (and rejects on `EADDRINUSE`) — that's what the broker election
|
|
6
6
|
* relies on to decide host-vs-guest.
|
|
@@ -10,62 +10,4 @@ export function isAddrInUse(err) {
|
|
|
10
10
|
const msg = err instanceof Error ? err.message : String(err);
|
|
11
11
|
return /eaddrinuse|address already in use|in use/i.test(msg);
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Real transport backed by `Bun.serve` — the production path inside OpenCode
|
|
15
|
-
* (which runs on Bun). Throws if invoked outside Bun.
|
|
16
|
-
*/
|
|
17
|
-
export function createBunTransport() {
|
|
18
|
-
const bun = globalThis.Bun;
|
|
19
|
-
if (!bun) {
|
|
20
|
-
throw new Error("the opencode-browser bridge requires the Bun runtime (Bun.serve not found)");
|
|
21
|
-
}
|
|
22
|
-
let server = null;
|
|
23
|
-
const conns = new WeakMap();
|
|
24
|
-
return {
|
|
25
|
-
listen({ host, port }, handlers) {
|
|
26
|
-
return new Promise((resolve, reject) => {
|
|
27
|
-
try {
|
|
28
|
-
server = bun.serve({
|
|
29
|
-
hostname: host,
|
|
30
|
-
port,
|
|
31
|
-
fetch(req, srv) {
|
|
32
|
-
if (srv.upgrade(req)) {
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
return new Response("opencode-browser bridge: websocket only", { status: 426 });
|
|
36
|
-
},
|
|
37
|
-
websocket: {
|
|
38
|
-
open(ws) {
|
|
39
|
-
const conn = { send: (d) => ws.send(d), close: () => ws.close() };
|
|
40
|
-
conns.set(ws, conn);
|
|
41
|
-
handlers.onOpen(conn);
|
|
42
|
-
},
|
|
43
|
-
message(ws, message) {
|
|
44
|
-
const conn = conns.get(ws);
|
|
45
|
-
if (conn) {
|
|
46
|
-
handlers.onMessage(conn, typeof message === "string" ? message : Buffer.from(message).toString());
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
close(ws) {
|
|
50
|
-
const conn = conns.get(ws);
|
|
51
|
-
conns.delete(ws);
|
|
52
|
-
if (conn) {
|
|
53
|
-
handlers.onClose(conn);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
resolve(); // Bun.serve throws synchronously on EADDRINUSE; reaching here = bound.
|
|
59
|
-
}
|
|
60
|
-
catch (err) {
|
|
61
|
-
reject(err);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
},
|
|
65
|
-
stop() {
|
|
66
|
-
server?.stop(true);
|
|
67
|
-
server = null;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
13
|
//# sourceMappingURL=transport.js.map
|
package/dist/transport.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoBH,gFAAgF;AAChF,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO,2CAA2C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/D,CAAC
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoBH,gFAAgF;AAChF,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO,2CAA2C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vymalo/opencode-browser",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "OpenCode plugin that gives the model hands in a real browser: it registers browser_* tools (open, click, type, scroll, screenshot, snapshot, …) and hosts a localhost WebSocket bridge that a companion Chromium/Firefox extension connects to. Tabs are organized into named groups so each task stays isolated and inspectable.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vymalo contributors",
|
|
@@ -49,9 +49,11 @@
|
|
|
49
49
|
"access": "public"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@opencode-ai/plugin": "1.15.10"
|
|
52
|
+
"@opencode-ai/plugin": "1.15.10",
|
|
53
|
+
"ws": "^8.21.0"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
56
|
+
"@types/ws": "^8.18.0",
|
|
55
57
|
"vite": "^8.0.14",
|
|
56
58
|
"vitest": "^4.1.7"
|
|
57
59
|
},
|