@pasko70/pibo 1.13.0 → 1.15.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 (58) hide show
  1. package/dist/apps/chat/static-assets.js +3 -1
  2. package/dist/apps/chat/web-app.js +10 -0
  3. package/dist/apps/{context-files-ui/assets/dist-BFRDpzcv.js → chat-ui/assets/dist-BFwSuVfD.js} +1 -1
  4. package/dist/apps/chat-ui/assets/{dist-XmG4qG-A.js → dist-BYTi90un.js} +1 -1
  5. package/dist/apps/{context-files-ui/assets/dist-B_zXPNMb.js → chat-ui/assets/dist-Bi74WdBU.js} +1 -1
  6. package/dist/apps/chat-ui/assets/{dist-UYBI2F8E.js → dist-CYMM1_bS.js} +1 -1
  7. package/dist/apps/chat-ui/assets/{dist-DVPXynJE.js → dist-CuIwfDgF.js} +1 -1
  8. package/dist/apps/{context-files-ui/assets/dist-B5mpygOZ.js → chat-ui/assets/dist-DS1ScAMf.js} +1 -1
  9. package/dist/apps/chat-ui/assets/{dist-B_8EcoB6.js → dist-DUgrMhHX.js} +1 -1
  10. package/dist/apps/chat-ui/assets/{dist-CVLKu7NV.js → dist-Dq3i1LVZ.js} +1 -1
  11. package/dist/apps/chat-ui/assets/{dist-DugRsLaG.js → dist-Dy5L1xOD.js} +1 -1
  12. package/dist/apps/{context-files-ui/assets/dist-D6pYe6d_.js → chat-ui/assets/dist-Zy3KnDp7.js} +1 -1
  13. package/dist/apps/chat-ui/assets/index-BZO4zpMi.js +237 -0
  14. package/dist/apps/chat-ui/assets/index-CoCl8FG8.css +1 -0
  15. package/dist/apps/chat-ui/index.html +2 -2
  16. package/dist/apps/chat-vscode-web/assets/index-V_pKPVAm.js +41 -0
  17. package/dist/apps/chat-vscode-web/index.html +1 -1
  18. package/dist/apps/{chat-ui/assets/dist-CnX234jg.js → context-files-ui/assets/dist-B7gXtTZC.js} +1 -1
  19. package/dist/apps/{chat-ui/assets/dist-CELRUR8b.js → context-files-ui/assets/dist-BD262Ouw.js} +1 -1
  20. package/dist/apps/context-files-ui/assets/{dist-Em3LWmNi.js → dist-BKuGdcnX.js} +1 -1
  21. package/dist/apps/{chat-ui/assets/dist-CVmpu9FK.js → context-files-ui/assets/dist-C-Jm45P4.js} +1 -1
  22. package/dist/apps/context-files-ui/assets/{dist-DM6yvMZv.js → dist-CHdx1Jri.js} +1 -1
  23. package/dist/apps/context-files-ui/assets/{dist-BLI6sQZG.js → dist-CULMH-fB.js} +1 -1
  24. package/dist/apps/context-files-ui/assets/{dist-CLrlJjPP.js → dist-Cjq6RWEt.js} +1 -1
  25. package/dist/apps/context-files-ui/assets/{dist-D8ppb7gk.js → dist-CntqFySz.js} +1 -1
  26. package/dist/apps/context-files-ui/assets/{dist-SEP88Pg7.js → dist-DxEGebCR.js} +1 -1
  27. package/dist/apps/{chat-ui/assets/dist-KOXty1JB.js → context-files-ui/assets/dist-kPuQud7t.js} +1 -1
  28. package/dist/apps/context-files-ui/assets/index-DZWsbuvg.js +202 -0
  29. package/dist/apps/context-files-ui/index.html +1 -1
  30. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  31. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.15.0.vsix +0 -0
  32. package/dist/cli-session/localSessionSource.js +2 -0
  33. package/dist/core/runtime.js +27 -5
  34. package/dist/core/session-router.js +3 -0
  35. package/dist/gateway/client.js +8 -0
  36. package/dist/gateway/server.js +18 -2
  37. package/dist/gateway/web.js +1 -0
  38. package/dist/local/extension.js +2 -0
  39. package/dist/loops/plugin.js +34 -1
  40. package/dist/loops/service.js +85 -0
  41. package/dist/mcp/config-command.js +5 -3
  42. package/dist/plugins/builtin.js +7 -0
  43. package/dist/plugins/context-files-store.js +287 -101
  44. package/dist/plugins/context-files.js +102 -154
  45. package/dist/session-ui/terminalRows.js +12 -2
  46. package/dist/shared/trace-live-reducer.js +6 -1
  47. package/dist/tools/browser-use-leases.js +16 -0
  48. package/dist/tools/codex-browser-node-repl.js +224 -0
  49. package/dist/tools/codex-browser-node-worker-source.js +403 -0
  50. package/dist/tools/codex-browser.js +461 -0
  51. package/dist/web/channel.js +105 -16
  52. package/dist/web/http.js +14 -2
  53. package/package.json +13 -7
  54. package/dist/apps/chat-ui/assets/index-DhFQ1th-.js +0 -237
  55. package/dist/apps/chat-ui/assets/index-hjygk9Ol.css +0 -1
  56. package/dist/apps/chat-vscode-web/assets/index-1wWonouX.js +0 -41
  57. package/dist/apps/context-files-ui/assets/index-C7X2UvWe.js +0 -202
  58. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.13.0.vsix +0 -0
@@ -0,0 +1,224 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createRequire } from "node:module";
3
+ import { createInterface } from "node:readline";
4
+ import { CODEX_BROWSER_NODE_WORKER_SOURCE } from "./codex-browser-node-worker-source.js";
5
+ const packageRequire = createRequire(import.meta.url);
6
+ const ACORN_PATH = packageRequire.resolve("acorn");
7
+ const ACORN_WALK_PATH = packageRequire.resolve("acorn-walk");
8
+ function errorSummary(error, name = "NodeReplError") {
9
+ return error instanceof Error
10
+ ? { name: error.name || name, message: error.message, stack: error.stack }
11
+ : { name, message: String(error) };
12
+ }
13
+ export class CodexBrowserNodeRepl {
14
+ bridge;
15
+ child;
16
+ pending = new Map();
17
+ requestCounter = 0;
18
+ executionCount = 0;
19
+ alive = true;
20
+ busy = false;
21
+ diagnostics = "";
22
+ readyPromise;
23
+ readyResolve;
24
+ readyReject;
25
+ constructor(bridge, cwd) {
26
+ this.bridge = bridge;
27
+ this.readyPromise = new Promise((resolve, reject) => {
28
+ this.readyResolve = resolve;
29
+ this.readyReject = reject;
30
+ });
31
+ this.child = spawn("node", ["-e", CODEX_BROWSER_NODE_WORKER_SOURCE, ACORN_PATH, ACORN_WALK_PATH], {
32
+ cwd,
33
+ env: minimalWorkerEnvironment(),
34
+ stdio: "pipe",
35
+ });
36
+ const stdout = createInterface({ input: this.child.stdout });
37
+ stdout.on("line", (line) => this.handleLine(line));
38
+ this.child.stderr.on("data", (chunk) => {
39
+ this.diagnostics += String(chunk);
40
+ });
41
+ this.child.once("error", (error) => {
42
+ this.alive = false;
43
+ this.readyReject(error);
44
+ this.rejectAll(error);
45
+ });
46
+ this.child.once("close", (code, signal) => {
47
+ this.alive = false;
48
+ const error = new Error(`node_repl worker exited with code ${code ?? "null"}${signal ? ` signal ${signal}` : ""}`);
49
+ this.readyReject(error);
50
+ this.rejectAll(error);
51
+ });
52
+ }
53
+ static async start(bridge, cwd, timeoutMs = 10_000) {
54
+ const repl = new CodexBrowserNodeRepl(bridge, cwd);
55
+ await repl.waitReady(timeoutMs);
56
+ return repl;
57
+ }
58
+ isAlive() {
59
+ return this.alive && !this.child.killed;
60
+ }
61
+ async exec(code, timeoutMs = 30_000) {
62
+ const startedAt = Date.now();
63
+ if (this.busy) {
64
+ return {
65
+ status: "failed",
66
+ stdout: "",
67
+ stderr: "",
68
+ durationMs: 0,
69
+ executionCount: this.executionCount,
70
+ error: { name: "NodeReplBusy", message: "node_repl.js is already executing code." },
71
+ };
72
+ }
73
+ this.busy = true;
74
+ try {
75
+ const response = await this.request("exec", { code, timeoutMs }, timeoutMs + 1_000);
76
+ this.executionCount += 1;
77
+ return {
78
+ status: response.status === "ok" ? "ok" : "error",
79
+ stdout: response.stdout ?? "",
80
+ stderr: response.stderr ?? "",
81
+ result: response.result,
82
+ error: response.error,
83
+ durationMs: Date.now() - startedAt,
84
+ executionCount: this.executionCount,
85
+ };
86
+ }
87
+ catch (error) {
88
+ return {
89
+ status: error instanceof NodeReplTimeoutError ? "timeout" : "failed",
90
+ stdout: "",
91
+ stderr: this.diagnostics,
92
+ durationMs: Date.now() - startedAt,
93
+ executionCount: this.executionCount,
94
+ error: errorSummary(error),
95
+ };
96
+ }
97
+ finally {
98
+ this.busy = false;
99
+ }
100
+ }
101
+ async reset(timeoutMs = 10_000) {
102
+ if (this.busy) {
103
+ return {
104
+ status: "failed",
105
+ reset: false,
106
+ error: { name: "NodeReplBusy", message: "node_repl.js cannot reset while code is executing." },
107
+ };
108
+ }
109
+ try {
110
+ const response = await this.request("reset", {}, timeoutMs);
111
+ this.executionCount = 0;
112
+ return { status: response.status === "ok" ? "ok" : "failed", reset: response.reset === true, error: response.error };
113
+ }
114
+ catch (error) {
115
+ return { status: "failed", reset: false, error: errorSummary(error) };
116
+ }
117
+ }
118
+ dispose() {
119
+ if (!this.isAlive())
120
+ return;
121
+ this.alive = false;
122
+ this.child.kill("SIGTERM");
123
+ this.rejectAll(new Error("node_repl worker was disposed"));
124
+ }
125
+ waitReady(timeoutMs) {
126
+ return new Promise((resolve, reject) => {
127
+ const timer = setTimeout(() => reject(new Error(`Timed out waiting for node_repl worker. ${this.diagnostics}`)), timeoutMs);
128
+ this.readyPromise.then(() => {
129
+ clearTimeout(timer);
130
+ resolve();
131
+ }, (error) => {
132
+ clearTimeout(timer);
133
+ reject(error);
134
+ });
135
+ });
136
+ }
137
+ request(type, payload, timeoutMs) {
138
+ if (!this.isAlive())
139
+ return Promise.reject(new Error("node_repl worker is not alive"));
140
+ const id = `request_${++this.requestCounter}`;
141
+ return new Promise((resolve, reject) => {
142
+ const timer = setTimeout(() => {
143
+ this.pending.delete(id);
144
+ reject(new NodeReplTimeoutError(`node_repl request ${type} timed out after ${timeoutMs}ms`));
145
+ }, timeoutMs);
146
+ this.pending.set(id, { resolve, reject, timer });
147
+ this.write({ type, id, ...payload });
148
+ });
149
+ }
150
+ handleLine(line) {
151
+ let message;
152
+ try {
153
+ message = JSON.parse(line);
154
+ }
155
+ catch {
156
+ this.alive = false;
157
+ this.rejectAll(new Error(`Invalid node_repl worker protocol line: ${line}`));
158
+ this.child.kill("SIGTERM");
159
+ return;
160
+ }
161
+ if (message.type === "ready" && message.status === "ready") {
162
+ this.readyResolve();
163
+ return;
164
+ }
165
+ if (message.type === "browser_request") {
166
+ void this.handleBrowserRequest(message);
167
+ return;
168
+ }
169
+ const id = message.id;
170
+ if (!id)
171
+ return;
172
+ const pending = this.pending.get(id);
173
+ if (!pending)
174
+ return;
175
+ this.pending.delete(id);
176
+ clearTimeout(pending.timer);
177
+ pending.resolve(message);
178
+ }
179
+ async handleBrowserRequest(message) {
180
+ if (!message.id)
181
+ return;
182
+ try {
183
+ let result;
184
+ if (message.operation === "open_tabs") {
185
+ result = await this.bridge.openTabs();
186
+ }
187
+ else if (message.operation === "use") {
188
+ if (!message.input || typeof message.input !== "object" || Array.isArray(message.input)) {
189
+ throw new Error("browser.use requires an action object");
190
+ }
191
+ result = await this.bridge.use(message.input);
192
+ }
193
+ else {
194
+ throw new Error(`Unknown browser bridge operation ${message.operation ?? ""}`);
195
+ }
196
+ this.write({ type: "browser_response", id: message.id, result });
197
+ }
198
+ catch (error) {
199
+ this.write({ type: "browser_response", id: message.id, error: errorSummary(error, "BrowserUseError") });
200
+ }
201
+ }
202
+ write(message) {
203
+ if (!this.isAlive())
204
+ return;
205
+ this.child.stdin.write(`${JSON.stringify(message)}\n`);
206
+ }
207
+ rejectAll(error) {
208
+ for (const [id, pending] of this.pending) {
209
+ this.pending.delete(id);
210
+ clearTimeout(pending.timer);
211
+ pending.reject(error);
212
+ }
213
+ }
214
+ }
215
+ function minimalWorkerEnvironment() {
216
+ const env = {};
217
+ for (const name of ["PATH", "HOME", "TMPDIR", "TEMP", "TMP", "LANG", "LC_ALL", "TZ"]) {
218
+ if (process.env[name] !== undefined)
219
+ env[name] = process.env[name];
220
+ }
221
+ return env;
222
+ }
223
+ class NodeReplTimeoutError extends Error {
224
+ }
@@ -0,0 +1,403 @@
1
+ export const CODEX_BROWSER_NODE_WORKER_SOURCE = String.raw `
2
+ const vm = require("node:vm");
3
+ const util = require("node:util");
4
+ const { Parser } = require(process.argv[1]);
5
+ const walk = require(process.argv[2]);
6
+
7
+ let context;
8
+ let currentOutput = null;
9
+ let browserRequestCounter = 0;
10
+ let timerCounter = 0;
11
+ const pendingBrowserRequests = new Map();
12
+ const timers = new Map();
13
+
14
+ function hardenFunction(fn) {
15
+ Object.setPrototypeOf(fn, null);
16
+ return Object.freeze(fn);
17
+ }
18
+
19
+ function nullObject(properties) {
20
+ return Object.freeze(Object.assign(Object.create(null), properties));
21
+ }
22
+
23
+ function bounded(value, maxBytes = 8192) {
24
+ const text = String(value);
25
+ const bytes = Buffer.byteLength(text, "utf8");
26
+ if (bytes <= maxBytes) return text;
27
+ return Buffer.from(text, "utf8").subarray(0, maxBytes).toString("utf8") + "\n...<truncated>";
28
+ }
29
+
30
+ function safeInspect(value, maxBytes = 4096) {
31
+ try {
32
+ return bounded(util.inspect(value, { depth: 4, maxArrayLength: 100, breakLength: 120 }), maxBytes);
33
+ } catch (error) {
34
+ return "<inspect failed: " + (error?.name || "Error") + ": " + (error?.message || String(error)) + ">";
35
+ }
36
+ }
37
+
38
+ function summarize(value, maxBytes = 4096) {
39
+ const type = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
40
+ const result = { type, repr: safeInspect(value, maxBytes) };
41
+ try {
42
+ if (value != null && typeof value.length === "number") result.length = value.length;
43
+ if (value && typeof value === "object") {
44
+ const keys = Object.keys(value).slice(0, 100);
45
+ if (keys.length > 0) result.keys = keys;
46
+ }
47
+ } catch {}
48
+ return result;
49
+ }
50
+
51
+ function errorSummary(error) {
52
+ const stack = error && error.stack ? String(error.stack) : String(error);
53
+ const out = {
54
+ name: error && error.name ? String(error.name) : "Error",
55
+ message: error && error.message ? String(error.message) : String(error),
56
+ stack,
57
+ };
58
+ const match = stack.match(/<node_repl>:(\d+):(\d+)/) || stack.match(/evalmachine\.<anonymous>:(\d+):(\d+)/);
59
+ if (match) {
60
+ out.line = Number(match[1]);
61
+ out.column = Number(match[2]);
62
+ }
63
+ return out;
64
+ }
65
+
66
+ function appendStdout(chunk) {
67
+ if (currentOutput) currentOutput.stdout += String(chunk);
68
+ }
69
+
70
+ function appendStderr(chunk) {
71
+ if (currentOutput) currentOutput.stderr += String(chunk);
72
+ }
73
+
74
+ function requestBrowser(operation, input = {}) {
75
+ const id = "browser_" + (++browserRequestCounter);
76
+ const promise = new Promise((resolve, reject) => {
77
+ pendingBrowserRequests.set(id, { resolve, reject });
78
+ writeMessage({ type: "browser_request", id, operation, input });
79
+ });
80
+ return nullObject({
81
+ then: hardenFunction((resolve, reject) => promise.then(resolve, reject)),
82
+ });
83
+ }
84
+
85
+ function createTimer(repeat, callback, delay, args) {
86
+ if (typeof callback !== "function") throw new TypeError("Timer callback must be a function");
87
+ const id = ++timerCounter;
88
+ const invoke = () => callback(...args);
89
+ const timer = repeat ? setInterval(invoke, delay) : setTimeout(() => {
90
+ timers.delete(id);
91
+ invoke();
92
+ }, delay);
93
+ timers.set(id, { repeat, timer });
94
+ return id;
95
+ }
96
+
97
+ function clearTimer(id) {
98
+ const entry = timers.get(id);
99
+ if (!entry) return;
100
+ timers.delete(id);
101
+ if (entry.repeat) clearInterval(entry.timer);
102
+ else clearTimeout(entry.timer);
103
+ }
104
+
105
+ function clearAllTimers() {
106
+ for (const id of [...timers.keys()]) clearTimer(id);
107
+ }
108
+
109
+ function createContext() {
110
+ clearAllTimers();
111
+ const consoleProxy = nullObject({
112
+ log: hardenFunction((...args) => appendStdout(util.format(...args) + "\n")),
113
+ info: hardenFunction((...args) => appendStdout(util.format(...args) + "\n")),
114
+ debug: hardenFunction((...args) => appendStdout(util.format(...args) + "\n")),
115
+ warn: hardenFunction((...args) => appendStderr(util.format(...args) + "\n")),
116
+ error: hardenFunction((...args) => appendStderr(util.format(...args) + "\n")),
117
+ dir: hardenFunction((value, options) => appendStdout(util.inspect(value, options) + "\n")),
118
+ });
119
+ const browser = nullObject({
120
+ openTabs: hardenFunction(() => requestBrowser("open_tabs")),
121
+ use: hardenFunction((action, params = {}) => {
122
+ const input = action && typeof action === "object" ? action : { ...params, action };
123
+ return requestBrowser("use", input);
124
+ }),
125
+ });
126
+ const sandbox = Object.assign(Object.create(null), {
127
+ console: consoleProxy,
128
+ browser,
129
+ setTimeout: hardenFunction((callback, delay = 0, ...args) => createTimer(false, callback, delay, args)),
130
+ clearTimeout: hardenFunction((id) => clearTimer(id)),
131
+ setInterval: hardenFunction((callback, delay = 0, ...args) => createTimer(true, callback, delay, args)),
132
+ clearInterval: hardenFunction((id) => clearTimer(id)),
133
+ setImmediate: hardenFunction((callback, ...args) => createTimer(false, callback, 0, args)),
134
+ clearImmediate: hardenFunction((id) => clearTimer(id)),
135
+ queueMicrotask: hardenFunction((callback) => { Promise.resolve().then(callback); }),
136
+ });
137
+ context = vm.createContext(sandbox, {
138
+ name: "node_repl",
139
+ codeGeneration: { strings: false, wasm: false },
140
+ });
141
+ context.global = context;
142
+ context.globalThis = context;
143
+ }
144
+
145
+ async function withTimeout(value, timeoutMs) {
146
+ if (!value || typeof value.then !== "function") return value;
147
+ let timer;
148
+ try {
149
+ return await Promise.race([
150
+ value,
151
+ new Promise((_, reject) => {
152
+ timer = setTimeout(() => reject(new Error("JavaScript execution timed out after " + timeoutMs + "ms")), timeoutMs);
153
+ }),
154
+ ]);
155
+ } finally {
156
+ clearTimeout(timer);
157
+ }
158
+ }
159
+
160
+ // Adapt Node REPL's top-level-await rewrite to the restricted vm context.
161
+ function isTopLevelDeclaration(state) {
162
+ return state.ancestors[state.ancestors.length - 2] === state.body;
163
+ }
164
+
165
+ const noopVisitor = () => {};
166
+ const topLevelAwaitVisitorsWithoutAncestors = {
167
+ ClassDeclaration(node, state, c) {
168
+ if (isTopLevelDeclaration(state)) {
169
+ state.prepend(node, node.id.name + "=");
170
+ state.hoistedDeclarationStatements.push("let " + node.id.name + "; ");
171
+ }
172
+ walk.base.ClassDeclaration(node, state, c);
173
+ },
174
+ ForOfStatement(node, state, c) {
175
+ if (node.await === true) state.containsAwait = true;
176
+ walk.base.ForOfStatement(node, state, c);
177
+ },
178
+ FunctionDeclaration(node, state) {
179
+ state.prepend(node, "this." + node.id.name + " = " + node.id.name + "; ");
180
+ state.hoistedDeclarationStatements.push("var " + node.id.name + "; ");
181
+ },
182
+ FunctionExpression: noopVisitor,
183
+ ArrowFunctionExpression: noopVisitor,
184
+ MethodDefinition: noopVisitor,
185
+ AwaitExpression(node, state, c) {
186
+ state.containsAwait = true;
187
+ walk.base.AwaitExpression(node, state, c);
188
+ },
189
+ ReturnStatement(node, state, c) {
190
+ state.containsReturn = true;
191
+ walk.base.ReturnStatement(node, state, c);
192
+ },
193
+ VariableDeclaration(node, state, c) {
194
+ const variableKind = node.kind;
195
+ const parent = state.ancestors[state.ancestors.length - 2];
196
+ const isIterableForDeclaration = parent.type === "ForOfStatement" || parent.type === "ForInStatement";
197
+ if (variableKind === "var" || isTopLevelDeclaration(state)) {
198
+ state.replace(
199
+ node.start,
200
+ node.start + variableKind.length + (isIterableForDeclaration ? 1 : 0),
201
+ variableKind === "var" && isIterableForDeclaration ? "" : "void" + (node.declarations.length === 1 ? "" : " ("),
202
+ );
203
+ if (!isIterableForDeclaration) {
204
+ for (const declaration of node.declarations) {
205
+ state.prepend(declaration, "(");
206
+ state.append(declaration, declaration.init ? ")" : "=undefined)");
207
+ }
208
+ if (node.declarations.length !== 1) state.append(node.declarations[node.declarations.length - 1], ")");
209
+ }
210
+
211
+ const variableIdentifiersToHoist = { var: [], let: [] };
212
+ function registerVariableDeclarationIdentifiers(pattern) {
213
+ if (!pattern) return;
214
+ if (pattern.type === "Identifier") {
215
+ variableIdentifiersToHoist[variableKind === "var" ? "var" : "let"].push(pattern.name);
216
+ return;
217
+ }
218
+ if (pattern.type === "ObjectPattern") {
219
+ for (const property of pattern.properties) registerVariableDeclarationIdentifiers(property.value || property.argument);
220
+ return;
221
+ }
222
+ if (pattern.type === "ArrayPattern") {
223
+ for (const element of pattern.elements) registerVariableDeclarationIdentifiers(element);
224
+ }
225
+ }
226
+ for (const declaration of node.declarations) registerVariableDeclarationIdentifiers(declaration.id);
227
+ for (const kind of ["var", "let"]) {
228
+ const identifiers = variableIdentifiersToHoist[kind];
229
+ if (identifiers.length > 0) state.hoistedDeclarationStatements.push(kind + " " + identifiers.join(", ") + "; ");
230
+ }
231
+ }
232
+ walk.base.VariableDeclaration(node, state, c);
233
+ },
234
+ };
235
+
236
+ const topLevelAwaitVisitors = {};
237
+ for (const nodeType of Object.keys(walk.base)) {
238
+ const callback = topLevelAwaitVisitorsWithoutAncestors[nodeType] || walk.base[nodeType];
239
+ topLevelAwaitVisitors[nodeType] = (node, state, c) => {
240
+ const isNew = node !== state.ancestors[state.ancestors.length - 1];
241
+ if (isNew) state.ancestors.push(node);
242
+ callback(node, state, c);
243
+ if (isNew) state.ancestors.pop();
244
+ };
245
+ }
246
+
247
+ function processTopLevelAwait(source) {
248
+ const wrapPrefix = "(async () => { ";
249
+ const wrapped = wrapPrefix + source + " })()";
250
+ const chars = wrapped.split("");
251
+ let root;
252
+ try {
253
+ root = Parser.parse(wrapped, { ecmaVersion: "latest" });
254
+ } catch {
255
+ return null;
256
+ }
257
+ const body = root.body[0].expression.callee.body;
258
+ const state = {
259
+ body,
260
+ ancestors: [],
261
+ hoistedDeclarationStatements: [],
262
+ containsAwait: false,
263
+ containsReturn: false,
264
+ replace(from, to, text) {
265
+ for (let index = from; index < to; index += 1) chars[index] = "";
266
+ if (from === to) text += chars[from];
267
+ chars[from] = text;
268
+ },
269
+ prepend(node, text) {
270
+ chars[node.start] = text + chars[node.start];
271
+ },
272
+ append(node, text) {
273
+ chars[node.end - 1] += text;
274
+ },
275
+ };
276
+ walk.recursive(body, state, topLevelAwaitVisitors);
277
+ if (!state.containsAwait || state.containsReturn) return null;
278
+
279
+ for (let index = body.body.length - 1; index >= 0; index -= 1) {
280
+ const node = body.body[index];
281
+ if (node.type === "EmptyStatement") continue;
282
+ if (node.type === "ExpressionStatement") {
283
+ state.prepend(node.expression, "{ value: (");
284
+ state.prepend(node, "return ");
285
+ state.append(node.expression, ") }");
286
+ }
287
+ break;
288
+ }
289
+ return state.hoistedDeclarationStatements.join("") + chars.join("");
290
+ }
291
+
292
+ async function execute(req) {
293
+ const output = { stdout: "", stderr: "" };
294
+ currentOutput = output;
295
+ try {
296
+ const timeoutMs = Number(req.timeoutMs || 30000);
297
+ const code = req.code || "";
298
+ let transformedTopLevelAwait = false;
299
+ let value;
300
+ try {
301
+ value = vm.runInContext(code, context, {
302
+ filename: "<node_repl>",
303
+ timeout: timeoutMs,
304
+ });
305
+ } catch (error) {
306
+ const transformed = error && error.name === "SyntaxError" ? processTopLevelAwait(code) : null;
307
+ if (!transformed) throw error;
308
+ transformedTopLevelAwait = true;
309
+ value = vm.runInContext(transformed, context, {
310
+ filename: "<node_repl>",
311
+ timeout: timeoutMs,
312
+ });
313
+ }
314
+ const settled = await withTimeout(value, timeoutMs);
315
+ const result = transformedTopLevelAwait && settled && Object.prototype.hasOwnProperty.call(settled, "value")
316
+ ? settled.value
317
+ : settled;
318
+ return {
319
+ type: "response",
320
+ id: req.id,
321
+ status: "ok",
322
+ stdout: output.stdout,
323
+ stderr: output.stderr,
324
+ result: result === undefined ? null : summarize(result),
325
+ };
326
+ } catch (error) {
327
+ return {
328
+ type: "response",
329
+ id: req.id,
330
+ status: "error",
331
+ stdout: output.stdout,
332
+ stderr: output.stderr,
333
+ error: errorSummary(error),
334
+ };
335
+ } finally {
336
+ currentOutput = null;
337
+ }
338
+ }
339
+
340
+ function reset(req) {
341
+ createContext();
342
+ return { type: "response", id: req.id, status: "ok", reset: true };
343
+ }
344
+
345
+ function handleBrowserResponse(req) {
346
+ const pending = pendingBrowserRequests.get(req.id);
347
+ if (!pending) return;
348
+ pendingBrowserRequests.delete(req.id);
349
+ if (req.error) {
350
+ const error = new Error(req.error.message || String(req.error));
351
+ error.name = req.error.name || "BrowserUseError";
352
+ pending.reject(error);
353
+ return;
354
+ }
355
+ const json = JSON.stringify(req.result === undefined ? null : req.result);
356
+ const contextValue = vm.runInContext("JSON.parse(" + JSON.stringify(json) + ")", context);
357
+ pending.resolve(contextValue);
358
+ }
359
+
360
+ function writeMessage(message) {
361
+ process.stdout.write(JSON.stringify(message) + "\n");
362
+ }
363
+
364
+ function handleRequest(req) {
365
+ if (req.type === "browser_response") {
366
+ handleBrowserResponse(req);
367
+ return;
368
+ }
369
+ if (req.type === "exec") {
370
+ void execute(req).then(writeMessage);
371
+ return;
372
+ }
373
+ if (req.type === "reset") {
374
+ writeMessage(reset(req));
375
+ return;
376
+ }
377
+ if (req.type === "shutdown") {
378
+ writeMessage({ type: "response", id: req.id, status: "ok", shutdown: true });
379
+ process.exit(0);
380
+ return;
381
+ }
382
+ writeMessage({ type: "response", id: req.id, status: "error", error: { name: "NodeReplProtocolError", message: "Unknown request type " + req.type } });
383
+ }
384
+
385
+ createContext();
386
+ writeMessage({ type: "ready", id: "ready", status: "ready" });
387
+ let buffer = "";
388
+ process.stdin.setEncoding("utf8");
389
+ process.stdin.on("data", (chunk) => {
390
+ buffer += chunk;
391
+ let index;
392
+ while ((index = buffer.indexOf("\n")) >= 0) {
393
+ const line = buffer.slice(0, index);
394
+ buffer = buffer.slice(index + 1);
395
+ if (!line.trim()) continue;
396
+ try {
397
+ handleRequest(JSON.parse(line));
398
+ } catch (error) {
399
+ writeMessage({ type: "response", status: "error", error: errorSummary(error) });
400
+ }
401
+ }
402
+ });
403
+ `;