@vymalo/opencode-browser 0.12.0 → 0.14.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 (44) hide show
  1. package/dist/agent-client.d.ts +36 -36
  2. package/dist/agent-client.js +201 -189
  3. package/dist/agent-client.js.map +1 -1
  4. package/dist/broker.d.ts +85 -87
  5. package/dist/broker.js +614 -599
  6. package/dist/broker.js.map +1 -1
  7. package/dist/catalog.d.ts +76 -76
  8. package/dist/catalog.js +826 -596
  9. package/dist/catalog.js.map +1 -1
  10. package/dist/endpoint.d.ts +42 -42
  11. package/dist/endpoint.js +132 -131
  12. package/dist/endpoint.js.map +1 -1
  13. package/dist/index.js +1 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib.js +1 -0
  16. package/dist/lib.js.map +1 -1
  17. package/dist/logging.d.ts +6 -6
  18. package/dist/logging.js +60 -63
  19. package/dist/logging.js.map +1 -1
  20. package/dist/node-transport.d.ts +6 -6
  21. package/dist/node-transport.js +48 -44
  22. package/dist/node-transport.js.map +1 -1
  23. package/dist/opencode.d.ts +10 -10
  24. package/dist/opencode.js +143 -146
  25. package/dist/opencode.js.map +1 -1
  26. package/dist/protocol.d.ts +127 -127
  27. package/dist/protocol.js +148 -128
  28. package/dist/protocol.js.map +1 -1
  29. package/dist/schema.d.ts +32 -32
  30. package/dist/schema.js +39 -44
  31. package/dist/schema.js.map +1 -1
  32. package/dist/token-file.d.ts +4 -4
  33. package/dist/token-file.js +68 -60
  34. package/dist/token-file.js.map +1 -1
  35. package/dist/tools.d.ts +11 -11
  36. package/dist/tools.js +128 -123
  37. package/dist/tools.js.map +1 -1
  38. package/dist/transport.d.ts +17 -17
  39. package/dist/transport.js +3 -9
  40. package/dist/transport.js.map +1 -1
  41. package/dist/types.d.ts +61 -61
  42. package/dist/types.js +1 -0
  43. package/dist/types.js.map +1 -1
  44. package/package.json +3 -3
package/dist/logging.js CHANGED
@@ -1,73 +1,70 @@
1
1
  export const LOG_LEVEL_PRIORITY = {
2
- trace: 5,
3
- debug: 10,
4
- info: 20,
5
- warn: 30,
6
- error: 40
2
+ trace: 5,
3
+ debug: 10,
4
+ info: 20,
5
+ warn: 30,
6
+ error: 40
7
7
  };
8
8
  export const DEFAULT_LOG_LEVEL = "info";
9
9
  function redactFields(fields) {
10
- if (!fields) {
11
- return undefined;
12
- }
13
- const redacted = {};
14
- for (const [key, value] of Object.entries(fields)) {
15
- if (/token|secret|password|authorization/i.test(key)) {
16
- redacted[key] = "[redacted]";
17
- continue;
18
- }
19
- redacted[key] = value;
20
- }
21
- return redacted;
10
+ if (!fields) {
11
+ return undefined;
12
+ }
13
+ const redacted = {};
14
+ for (const [key, value] of Object.entries(fields)) {
15
+ if (/token|secret|password|authorization/i.test(key)) {
16
+ redacted[key] = "[redacted]";
17
+ continue;
18
+ }
19
+ redacted[key] = value;
20
+ }
21
+ return redacted;
22
22
  }
23
23
  export function createJsonConsoleLogger(minLevel = DEFAULT_LOG_LEVEL) {
24
- const minPriority = LOG_LEVEL_PRIORITY[minLevel];
25
- const write = (level, event, fields) => {
26
- if (LOG_LEVEL_PRIORITY[level] < minPriority) {
27
- return;
28
- }
29
- const payload = {
30
- ts: new Date().toISOString(),
31
- level,
32
- event,
33
- ...(redactFields(fields) ?? {})
34
- };
35
- const line = JSON.stringify(payload);
36
- if (level === "error") {
37
- console.error(line);
38
- return;
39
- }
40
- if (level === "warn") {
41
- console.warn(line);
42
- return;
43
- }
44
- console.log(line);
45
- };
46
- return {
47
- trace: (event, fields) => write("trace", event, fields),
48
- debug: (event, fields) => write("debug", event, fields),
49
- info: (event, fields) => write("info", event, fields),
50
- warn: (event, fields) => write("warn", event, fields),
51
- error: (event, fields) => write("error", event, fields)
52
- };
24
+ const minPriority = LOG_LEVEL_PRIORITY[minLevel];
25
+ const write = (level, event, fields) => {
26
+ if (LOG_LEVEL_PRIORITY[level] < minPriority) {
27
+ return;
28
+ }
29
+ const payload = {
30
+ ts: new Date().toISOString(),
31
+ level,
32
+ event,
33
+ ...redactFields(fields) ?? {}
34
+ };
35
+ const line = JSON.stringify(payload);
36
+ if (level === "error") {
37
+ console.error(line);
38
+ return;
39
+ }
40
+ if (level === "warn") {
41
+ console.warn(line);
42
+ return;
43
+ }
44
+ console.log(line);
45
+ };
46
+ return {
47
+ trace: (event, fields) => write("trace", event, fields),
48
+ debug: (event, fields) => write("debug", event, fields),
49
+ info: (event, fields) => write("info", event, fields),
50
+ warn: (event, fields) => write("warn", event, fields),
51
+ error: (event, fields) => write("error", event, fields)
52
+ };
53
53
  }
54
54
  export function fromOpenCodeLogLevel(value) {
55
- if (typeof value !== "string") {
56
- return undefined;
57
- }
58
- switch (value.toUpperCase()) {
59
- case "DEBUG":
60
- // Host DEBUG unlocks our most-verbose internal tier (trace) — the browser
61
- // bridge is the package users debug most, so DEBUG opts into full tracing.
62
- return "trace";
63
- case "INFO":
64
- return "info";
65
- case "WARN":
66
- return "warn";
67
- case "ERROR":
68
- return "error";
69
- default:
70
- return undefined;
71
- }
55
+ if (typeof value !== "string") {
56
+ return undefined;
57
+ }
58
+ switch (value.toUpperCase()) {
59
+ case "DEBUG":
60
+ // Host DEBUG unlocks our most-verbose internal tier (trace) — the browser
61
+ // bridge is the package users debug most, so DEBUG opts into full tracing.
62
+ return "trace";
63
+ case "INFO": return "info";
64
+ case "WARN": return "warn";
65
+ case "ERROR": return "error";
66
+ default: return undefined;
67
+ }
72
68
  }
69
+
73
70
  //# sourceMappingURL=logging.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"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"}
1
+ {"mappings":"AAIA,OAAO,MAAM,qBAA+C;CAC1D,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;AACT;AAEA,OAAO,MAAM,oBAA8B;AAc3C,SAAS,aAAa,QAA2C;CAC/D,IAAI,CAAC,QAAQ;EACX,OAAO;CACT;CACA,MAAM,WAAsB,CAAC;CAC7B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;EACjD,IAAI,uCAAuC,KAAK,GAAG,GAAG;GACpD,SAAS,OAAO;GAChB;EACF;EACA,SAAS,OAAO;CAClB;CACA,OAAO;AACT;AAEA,OAAO,SAAS,wBAAwB,WAAqB,mBAA2B;CACtF,MAAM,cAAc,mBAAmB;CAEvC,MAAM,SAAS,OAAiB,OAAe,WAA6B;EAC1E,IAAI,mBAAmB,SAAS,aAAa;GAC3C;EACF;EACA,MAAM,UAAU;GACd,IAAI,IAAI,KAAK,CAAC,CAAC,YAAY;GAC3B;GACA;GACA,GAAI,aAAa,MAAM,KAAK,CAAC;EAC/B;EACA,MAAM,OAAO,KAAK,UAAU,OAAO;EACnC,IAAI,UAAU,SAAS;GACrB,QAAQ,MAAM,IAAI;GAClB;EACF;EACA,IAAI,UAAU,QAAQ;GACpB,QAAQ,KAAK,IAAI;GACjB;EACF;EACA,QAAQ,IAAI,IAAI;CAClB;CAEA,OAAO;EACL,QAAQ,OAAO,WAAW,MAAM,SAAS,OAAO,MAAM;EACtD,QAAQ,OAAO,WAAW,MAAM,SAAS,OAAO,MAAM;EACtD,OAAO,OAAO,WAAW,MAAM,QAAQ,OAAO,MAAM;EACpD,OAAO,OAAO,WAAW,MAAM,QAAQ,OAAO,MAAM;EACpD,QAAQ,OAAO,WAAW,MAAM,SAAS,OAAO,MAAM;CACxD;AACF;AAEA,OAAO,SAAS,qBAAqB,OAAsC;CACzE,IAAI,OAAO,UAAU,UAAU;EAC7B,OAAO;CACT;CACA,QAAQ,MAAM,YAAY,GAA1B;EACE,KAAK;;;EAGH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,SACE,OAAO;CACX;AACF","names":[],"sources":["../src/logging.ts"],"version":3,"file":"logging.js","sourceRoot":""}
@@ -1,12 +1,12 @@
1
1
  import type { AgentSocket, AgentSocketHandlers } from "./agent-client.js";
2
2
  import type { BridgeTransport } from "./transport.js";
3
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
- */
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
10
  export declare function createNodeTransport(): BridgeTransport;
11
11
  /** Agent-socket factory backed by the Node `ws` client (guest mode). */
12
12
  export declare function createNodeAgentSocket(url: string, handlers: AgentSocketHandlers): AgentSocket;
@@ -1,52 +1,56 @@
1
1
  import { WebSocket, WebSocketServer } from "ws";
2
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
- */
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
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
- };
10
+ let server = null;
11
+ return {
12
+ listen({ host, port }, handlers) {
13
+ return new Promise((resolve, reject) => {
14
+ const wss = new WebSocketServer({
15
+ host,
16
+ port
17
+ });
18
+ server = wss;
19
+ wss.once("listening", () => resolve());
20
+ wss.once("error", (err) => reject(err));
21
+ wss.on("connection", (ws) => {
22
+ const conn = {
23
+ send: (data) => ws.send(data),
24
+ close: () => ws.close()
25
+ };
26
+ handlers.onOpen(conn);
27
+ ws.on("message", (data) => handlers.onMessage(conn, data.toString()));
28
+ ws.on("close", () => handlers.onClose(conn));
29
+ ws.on("error", () => {
30
+ /* close follows */
31
+ });
32
+ });
33
+ });
34
+ },
35
+ stop() {
36
+ server?.close();
37
+ server = null;
38
+ }
39
+ };
37
40
  }
38
41
  /** Agent-socket factory backed by the Node `ws` client (guest mode). */
39
42
  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
- };
43
+ const ws = new WebSocket(url);
44
+ ws.on("open", () => handlers.onOpen());
45
+ ws.on("message", (data) => handlers.onMessage(data.toString()));
46
+ ws.on("close", () => handlers.onClose());
47
+ ws.on("error", () => {
48
+ /* close follows */
49
+ });
50
+ return {
51
+ send: (data) => ws.send(data),
52
+ close: () => ws.close()
53
+ };
51
54
  }
55
+
52
56
  //# sourceMappingURL=node-transport.js.map
@@ -1 +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"}
1
+ {"mappings":"AAAA,SAAS,WAAW,uBAAuB;;;;;;;;AAY3C,OAAO,SAAS,sBAAuC;CACrD,IAAI,SAAiC;CAErC,OAAO;EACL,OAAO,EAAE,MAAM,QAAQ,UAAU;GAC/B,OAAO,IAAI,SAAe,SAAS,WAAW;IAC5C,MAAM,MAAM,IAAI,gBAAgB;KAAE;KAAM;IAAK,CAAC;IAC9C,SAAS;IACT,IAAI,KAAK,mBAAmB,QAAQ,CAAC;IACrC,IAAI,KAAK,UAAU,QAAQ,OAAO,GAAG,CAAC;IACtC,IAAI,GAAG,eAAe,OAAkB;KACtC,MAAM,OAAyB;MAC7B,OAAO,SAAS,GAAG,KAAK,IAAI;MAC5B,aAAa,GAAG,MAAM;KACxB;KACA,SAAS,OAAO,IAAI;KACpB,GAAG,GAAG,YAAY,SAAS,SAAS,UAAU,MAAM,KAAK,SAAS,CAAC,CAAC;KACpE,GAAG,GAAG,eAAe,SAAS,QAAQ,IAAI,CAAC;KAC3C,GAAG,GAAG,eAAe;;KAErB,CAAC;IACH,CAAC;GACH,CAAC;EACH;EACA,OAAO;GACL,QAAQ,MAAM;GACd,SAAS;EACX;CACF;AACF;;AAGA,OAAO,SAAS,sBAAsB,KAAa,UAA4C;CAC7F,MAAM,KAAK,IAAI,UAAU,GAAG;CAC5B,GAAG,GAAG,cAAc,SAAS,OAAO,CAAC;CACrC,GAAG,GAAG,YAAY,SAAS,SAAS,UAAU,KAAK,SAAS,CAAC,CAAC;CAC9D,GAAG,GAAG,eAAe,SAAS,QAAQ,CAAC;CACvC,GAAG,GAAG,eAAe;;CAErB,CAAC;CACD,OAAO;EACL,OAAO,SAAS,GAAG,KAAK,IAAI;EAC5B,aAAa,GAAG,MAAM;CACxB;AACF","names":[],"sources":["../src/node-transport.ts"],"version":3,"file":"node-transport.js","sourceRoot":""}
@@ -4,16 +4,16 @@ import { type Logger } from "./logging.js";
4
4
  import { type SaveScreenshot } from "./tools.js";
5
5
  import type { BridgeTransport } from "./transport.js";
6
6
  export interface BrowserPluginFactoryOptions {
7
- /** Inject a logger (defaults to the OpenCode-piped logger). */
8
- logger?: Logger;
9
- /** Inject the server transport factory for host mode (defaults to the Node `ws` server). */
10
- createServerTransport?: () => BridgeTransport;
11
- /** Inject the agent-socket factory for guest mode (defaults to the Node `ws` client). */
12
- createAgentSocket?: AgentSocketFactory;
13
- /** Inject the screenshot disk-writer (tests). */
14
- saveScreenshot?: SaveScreenshot;
15
- /** Inject token generation (tests). */
16
- generateToken?: () => string;
7
+ /** Inject a logger (defaults to the OpenCode-piped logger). */
8
+ logger?: Logger;
9
+ /** Inject the server transport factory for host mode (defaults to the Node `ws` server). */
10
+ createServerTransport?: () => BridgeTransport;
11
+ /** Inject the agent-socket factory for guest mode (defaults to the Node `ws` client). */
12
+ createAgentSocket?: AgentSocketFactory;
13
+ /** Inject the screenshot disk-writer (tests). */
14
+ saveScreenshot?: SaveScreenshot;
15
+ /** Inject token generation (tests). */
16
+ generateToken?: () => string;
17
17
  }
18
18
  export declare function createBrowserPlugin(factoryOptions?: BrowserPluginFactoryOptions): Plugin;
19
19
  export declare const OpencodeBrowserPlugin: Plugin;
package/dist/opencode.js CHANGED
@@ -6,163 +6,160 @@ import { readBridgeFile, resolveSharedToken, writeBridgeFile } from "./token-fil
6
6
  import { createNodeAgentSocket, createNodeTransport } from "./node-transport.js";
7
7
  import { createBrowserTools } from "./tools.js";
8
8
  function resolveGroups(raw) {
9
- if (!Array.isArray(raw)) {
10
- return [...DEFAULT_GROUPS];
11
- }
12
- const valid = raw.filter((g) => TOOL_GROUPS.includes(g));
13
- return valid.length > 0 ? valid : [...DEFAULT_GROUPS];
9
+ if (!Array.isArray(raw)) {
10
+ return [...DEFAULT_GROUPS];
11
+ }
12
+ const valid = raw.filter((g) => TOOL_GROUPS.includes(g));
13
+ return valid.length > 0 ? valid : [...DEFAULT_GROUPS];
14
14
  }
15
15
  const PLUGIN_SERVICE_NAME = "opencode-browser-plugin";
16
16
  const DEFAULTS = {
17
- host: "127.0.0.1",
18
- port: 4517,
19
- executor: "auto",
20
- timeoutMs: 30_000,
21
- screenshotDir: ".opencode/browser"
17
+ host: "127.0.0.1",
18
+ port: 4517,
19
+ executor: "auto",
20
+ timeoutMs: 3e4,
21
+ screenshotDir: ".opencode/browser"
22
22
  };
23
23
  /**
24
- * Pipe plugin logs through OpenCode's `client.app.log` so they show up in the
25
- * host's structured log stream, with the JSON console as a reliable fallback.
26
- * Mirrors the pattern used by `@vymalo/opencode-oauth2` / `-ratelimit`.
27
- */
24
+ * Pipe plugin logs through OpenCode's `client.app.log` so they show up in the
25
+ * host's structured log stream, with the JSON console as a reliable fallback.
26
+ * Mirrors the pattern used by `@vymalo/opencode-oauth2` / `-ratelimit`.
27
+ */
28
28
  function createOpenCodeLogger(client, getMinLevel) {
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 ?? "");
36
- const write = (level, event, fields) => {
37
- if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[getMinLevel()]) {
38
- return;
39
- }
40
- if (consoleAll || level === "warn" || level === "error") {
41
- fallback[level](event, fields);
42
- }
43
- // OpenCode's host log API has no `trace` tier — fold it into `debug` for the
44
- // host call (our own level filter above already gates trace by DEBUG).
45
- const hostLevel = level === "trace" ? "debug" : level;
46
- void client.app
47
- .log({
48
- body: { service: PLUGIN_SERVICE_NAME, level: hostLevel, message: event, extra: fields }
49
- })
50
- .catch(() => {
51
- /* best-effort */
52
- });
53
- };
54
- return {
55
- trace: (event, fields) => write("trace", event, fields),
56
- debug: (event, fields) => write("debug", event, fields),
57
- info: (event, fields) => write("info", event, fields),
58
- warn: (event, fields) => write("warn", event, fields),
59
- error: (event, fields) => write("error", event, fields)
60
- };
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 ?? "");
36
+ const write = (level, event, fields) => {
37
+ if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[getMinLevel()]) {
38
+ return;
39
+ }
40
+ if (consoleAll || level === "warn" || level === "error") {
41
+ fallback[level](event, fields);
42
+ }
43
+ // OpenCode's host log API has no `trace` tier — fold it into `debug` for the
44
+ // host call (our own level filter above already gates trace by DEBUG).
45
+ const hostLevel = level === "trace" ? "debug" : level;
46
+ void client.app.log({ body: {
47
+ service: PLUGIN_SERVICE_NAME,
48
+ level: hostLevel,
49
+ message: event,
50
+ extra: fields
51
+ } }).catch(() => {
52
+ /* best-effort */
53
+ });
54
+ };
55
+ return {
56
+ trace: (event, fields) => write("trace", event, fields),
57
+ debug: (event, fields) => write("debug", event, fields),
58
+ info: (event, fields) => write("info", event, fields),
59
+ warn: (event, fields) => write("warn", event, fields),
60
+ error: (event, fields) => write("error", event, fields)
61
+ };
61
62
  }
62
63
  function resolveOptions(raw, generateToken) {
63
- const opts = (raw ?? {});
64
- const token = typeof opts.token === "string" && opts.token.length > 0 ? opts.token : generateToken();
65
- return {
66
- enabled: opts.enabled !== false,
67
- host: opts.host ?? DEFAULTS.host,
68
- port: opts.port ?? DEFAULTS.port,
69
- token,
70
- executor: opts.executor ?? DEFAULTS.executor,
71
- groups: resolveGroups(opts.groups),
72
- timeoutMs: opts.timeoutMs ?? DEFAULTS.timeoutMs,
73
- screenshotDir: opts.screenshotDir ?? DEFAULTS.screenshotDir
74
- };
64
+ const opts = raw ?? {};
65
+ const token = typeof opts.token === "string" && opts.token.length > 0 ? opts.token : generateToken();
66
+ return {
67
+ enabled: opts.enabled !== false,
68
+ host: opts.host ?? DEFAULTS.host,
69
+ port: opts.port ?? DEFAULTS.port,
70
+ token,
71
+ executor: opts.executor ?? DEFAULTS.executor,
72
+ groups: resolveGroups(opts.groups),
73
+ timeoutMs: opts.timeoutMs ?? DEFAULTS.timeoutMs,
74
+ screenshotDir: opts.screenshotDir ?? DEFAULTS.screenshotDir
75
+ };
75
76
  }
76
77
  export function createBrowserPlugin(factoryOptions = {}) {
77
- return async ({ client }, pluginOptions) => {
78
- let currentLogLevel = DEFAULT_LOG_LEVEL;
79
- const logger = factoryOptions.logger ?? createOpenCodeLogger(client, () => currentLogLevel);
80
- const generateToken = factoryOptions.generateToken ?? (() => randomBytes(24).toString("hex"));
81
- const options = resolveOptions(pluginOptions, generateToken);
82
- const rawOptions = pluginOptions;
83
- // Empty string is NOT an explicit token (resolveOptions generates one for
84
- // it). Treating "" as provided would mark the shared token "explicit" and
85
- // skip the paste_into_extension log, leaving the extension unable to learn
86
- // the generated token.
87
- const tokenProvided = typeof rawOptions?.token === "string" && rawOptions.token.length > 0;
88
- // Only forward the executor preference when the operator set it explicitly;
89
- // otherwise the extension keeps its own dashboard choice.
90
- const executorProvided = typeof rawOptions?.executor === "string";
91
- if (!options.enabled) {
92
- logger.info("browser_plugin_disabled", {});
93
- return {
94
- config: async (config) => {
95
- currentLogLevel = fromOpenCodeLogLevel(config.logLevel) ?? DEFAULT_LOG_LEVEL;
96
- }
97
- };
98
- }
99
- // Share the token across adapters via the per-user state file (explicit wins).
100
- const { token, source } = resolveSharedToken(options.port, tokenProvided ? options.token : undefined, generateToken);
101
- // Persist + advertise the token, **host-only** (fired via onHost so a host
102
- // reached by failover re-election does it too). Writing only on the host —
103
- // not unconditionally at load like before — means a guest that resolved a
104
- // fresh token on a transient read miss can never clobber the live host's
105
- // token in the shared file. We write whenever the file doesn't already match
106
- // OUR (port, token): that keeps a port change in sync and lets an explicit
107
- // token overwrite a stale generated one — but a no-op when the file already
108
- // agrees, so a concurrent host doesn't thrash it. The `paste_into_extension`
109
- // field name dodges the logger's redaction filter so the value stays copyable.
110
- const advertiseToken = () => {
111
- const existing = readBridgeFile();
112
- if (!existing || existing.port !== options.port || existing.token !== token) {
113
- writeBridgeFile(options.port, token);
114
- }
115
- if (source !== "explicit") {
116
- logger.info("browser_bridge_token", {
117
- paste_into_extension: token,
118
- source,
119
- mode: "host"
120
- });
121
- }
122
- };
123
- // Auto-elect: host the broker (win the bind) or join an existing one as a guest.
124
- const endpoint = await createEndpoint({
125
- host: options.host,
126
- port: options.port,
127
- token,
128
- executor: executorProvided ? options.executor : undefined,
129
- timeoutMs: options.timeoutMs,
130
- label: "opencode-plugin",
131
- onHost: advertiseToken,
132
- // Lets a long-lived host pick up a rotated token from the shared file on
133
- // a failed handshake, instead of rejecting forever until it's restarted.
134
- // Disabled for an EXPLICIT token: the operator pinned it, so the file
135
- // must never override their configured secret.
136
- reloadToken: source === "explicit" ? undefined : () => readBridgeFile()?.token
137
- }, {
138
- logger,
139
- createServerTransport: factoryOptions.createServerTransport ?? createNodeTransport,
140
- createAgentSocket: factoryOptions.createAgentSocket ?? createNodeAgentSocket
141
- });
142
- // When OpenCode exits, hand the browser back automatically — the user
143
- // shouldn't have to click Disconnect.
144
- process.once("exit", () => {
145
- try {
146
- endpoint.shutdown();
147
- }
148
- catch {
149
- /* best-effort */
150
- }
151
- });
152
- const tools = createBrowserTools({
153
- send: endpoint.send,
154
- options,
155
- logger,
156
- saveScreenshot: factoryOptions.saveScreenshot
157
- });
158
- return {
159
- tool: tools,
160
- config: async (config) => {
161
- currentLogLevel = fromOpenCodeLogLevel(config.logLevel) ?? DEFAULT_LOG_LEVEL;
162
- }
163
- };
164
- };
78
+ return async ({ client }, pluginOptions) => {
79
+ let currentLogLevel = DEFAULT_LOG_LEVEL;
80
+ const logger = factoryOptions.logger ?? createOpenCodeLogger(client, () => currentLogLevel);
81
+ const generateToken = factoryOptions.generateToken ?? (() => randomBytes(24).toString("hex"));
82
+ const options = resolveOptions(pluginOptions, generateToken);
83
+ const rawOptions = pluginOptions;
84
+ // Empty string is NOT an explicit token (resolveOptions generates one for
85
+ // it). Treating "" as provided would mark the shared token "explicit" and
86
+ // skip the paste_into_extension log, leaving the extension unable to learn
87
+ // the generated token.
88
+ const tokenProvided = typeof rawOptions?.token === "string" && rawOptions.token.length > 0;
89
+ // Only forward the executor preference when the operator set it explicitly;
90
+ // otherwise the extension keeps its own dashboard choice.
91
+ const executorProvided = typeof rawOptions?.executor === "string";
92
+ if (!options.enabled) {
93
+ logger.info("browser_plugin_disabled", {});
94
+ return { config: async (config) => {
95
+ currentLogLevel = fromOpenCodeLogLevel(config.logLevel) ?? DEFAULT_LOG_LEVEL;
96
+ } };
97
+ }
98
+ // Share the token across adapters via the per-user state file (explicit wins).
99
+ const { token, source } = resolveSharedToken(options.port, tokenProvided ? options.token : undefined, generateToken);
100
+ // Persist + advertise the token, **host-only** (fired via onHost so a host
101
+ // reached by failover re-election does it too). Writing only on the host —
102
+ // not unconditionally at load like before — means a guest that resolved a
103
+ // fresh token on a transient read miss can never clobber the live host's
104
+ // token in the shared file. We write whenever the file doesn't already match
105
+ // OUR (port, token): that keeps a port change in sync and lets an explicit
106
+ // token overwrite a stale generated one — but a no-op when the file already
107
+ // agrees, so a concurrent host doesn't thrash it. The `paste_into_extension`
108
+ // field name dodges the logger's redaction filter so the value stays copyable.
109
+ const advertiseToken = () => {
110
+ const existing = readBridgeFile();
111
+ if (!existing || existing.port !== options.port || existing.token !== token) {
112
+ writeBridgeFile(options.port, token);
113
+ }
114
+ if (source !== "explicit") {
115
+ logger.info("browser_bridge_token", {
116
+ paste_into_extension: token,
117
+ source,
118
+ mode: "host"
119
+ });
120
+ }
121
+ };
122
+ // Auto-elect: host the broker (win the bind) or join an existing one as a guest.
123
+ const endpoint = await createEndpoint({
124
+ host: options.host,
125
+ port: options.port,
126
+ token,
127
+ executor: executorProvided ? options.executor : undefined,
128
+ timeoutMs: options.timeoutMs,
129
+ label: "opencode-plugin",
130
+ onHost: advertiseToken,
131
+ // Lets a long-lived host pick up a rotated token from the shared file on
132
+ // a failed handshake, instead of rejecting forever until it's restarted.
133
+ // Disabled for an EXPLICIT token: the operator pinned it, so the file
134
+ // must never override their configured secret.
135
+ reloadToken: source === "explicit" ? undefined : () => readBridgeFile()?.token
136
+ }, {
137
+ logger,
138
+ createServerTransport: factoryOptions.createServerTransport ?? createNodeTransport,
139
+ createAgentSocket: factoryOptions.createAgentSocket ?? createNodeAgentSocket
140
+ });
141
+ // When OpenCode exits, hand the browser back automatically — the user
142
+ // shouldn't have to click Disconnect.
143
+ process.once("exit", () => {
144
+ try {
145
+ endpoint.shutdown();
146
+ } catch {}
147
+ });
148
+ const tools = createBrowserTools({
149
+ send: endpoint.send,
150
+ options,
151
+ logger,
152
+ saveScreenshot: factoryOptions.saveScreenshot
153
+ });
154
+ return {
155
+ tool: tools,
156
+ config: async (config) => {
157
+ currentLogLevel = fromOpenCodeLogLevel(config.logLevel) ?? DEFAULT_LOG_LEVEL;
158
+ }
159
+ };
160
+ };
165
161
  }
166
162
  export const OpencodeBrowserPlugin = createBrowserPlugin();
167
163
  export default OpencodeBrowserPlugin;
164
+
168
165
  //# sourceMappingURL=opencode.js.map