@mindstudio-ai/local-model-tunnel 0.5.47 → 0.5.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WDCWN4FW.js → chunk-LX7WOPLA.js} +2 -2
- package/dist/{chunk-YWEWXNQ6.js → chunk-PVWSKNUB.js} +71 -42
- package/dist/chunk-PVWSKNUB.js.map +1 -0
- package/dist/{chunk-UT7QCCAJ.js → chunk-UFGZOWZ6.js} +92 -52
- package/dist/chunk-UFGZOWZ6.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/headless.js +2 -2
- package/dist/index.js +3 -3
- package/dist/{tui-AJTMDHAG.js → tui-MMG7DSI5.js} +6 -6
- package/package.json +14 -16
- package/dist/chunk-UT7QCCAJ.js.map +0 -1
- package/dist/chunk-YWEWXNQ6.js.map +0 -1
- /package/dist/{chunk-WDCWN4FW.js.map → chunk-LX7WOPLA.js.map} +0 -0
- /package/dist/{tui-AJTMDHAG.js.map → tui-MMG7DSI5.js.map} +0 -0
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
setProviderInstallPath,
|
|
8
8
|
submitProgress,
|
|
9
9
|
submitResult
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-UFGZOWZ6.js";
|
|
11
11
|
|
|
12
12
|
// src/providers/ollama/index.ts
|
|
13
13
|
import { Ollama } from "ollama";
|
|
@@ -1395,4 +1395,4 @@ export {
|
|
|
1395
1395
|
requestEvents,
|
|
1396
1396
|
TunnelRunner
|
|
1397
1397
|
};
|
|
1398
|
-
//# sourceMappingURL=chunk-
|
|
1398
|
+
//# sourceMappingURL=chunk-LX7WOPLA.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CommandError,
|
|
2
3
|
DevProxy,
|
|
3
4
|
DevRunner,
|
|
4
5
|
closeBrowserLog,
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
syncSchema,
|
|
25
26
|
watchConfigFile,
|
|
26
27
|
watchTableFiles
|
|
27
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-UFGZOWZ6.js";
|
|
28
29
|
|
|
29
30
|
// src/dev/ipc/ipc.ts
|
|
30
31
|
function emitEvent(event, data) {
|
|
@@ -90,10 +91,10 @@ function subscribeDevEvents(shutdown) {
|
|
|
90
91
|
|
|
91
92
|
// src/dev/stdin-commands/run-scenario.ts
|
|
92
93
|
async function handleRunScenario(ctx, cmd) {
|
|
93
|
-
if (!ctx.state.runner) throw new
|
|
94
|
+
if (!ctx.state.runner) throw new CommandError("No active session", "NO_SESSION");
|
|
94
95
|
const freshConfig = detectAppConfig(ctx.cwd) ?? ctx.state.appConfig;
|
|
95
96
|
const scenario = freshConfig?.scenarios.find((s) => s.id === cmd.scenarioId);
|
|
96
|
-
if (!scenario) throw new
|
|
97
|
+
if (!scenario) throw new CommandError(`Unknown scenario: ${cmd.scenarioId}`, "INVALID_INPUT");
|
|
97
98
|
const scenarioName = scenario.name ?? scenario.export;
|
|
98
99
|
ctx.started({ scenarioId: scenario.id, name: scenarioName });
|
|
99
100
|
const skipTruncate = cmd.skipTruncate === true;
|
|
@@ -105,18 +106,19 @@ async function handleRunScenario(ctx, cmd) {
|
|
|
105
106
|
success: result.success,
|
|
106
107
|
scenarioId: scenario.id,
|
|
107
108
|
name: scenarioName,
|
|
108
|
-
|
|
109
|
+
error: result.error ?? null,
|
|
110
|
+
errorCode: result.success ? void 0 : "EXECUTION_ERROR"
|
|
109
111
|
};
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
// src/dev/stdin-commands/run-method.ts
|
|
113
115
|
async function handleRunMethod(ctx, cmd) {
|
|
114
|
-
if (!ctx.state.runner) throw new
|
|
116
|
+
if (!ctx.state.runner) throw new CommandError("No active session", "NO_SESSION");
|
|
115
117
|
const methodName = cmd.method;
|
|
116
|
-
if (!methodName) throw new
|
|
118
|
+
if (!methodName) throw new CommandError('run-method requires "method" (export name or ID)', "INVALID_INPUT");
|
|
117
119
|
const freshConfig = detectAppConfig(ctx.cwd) ?? ctx.state.appConfig;
|
|
118
120
|
const method = freshConfig?.methods.find((m) => m.export === methodName) ?? freshConfig?.methods.find((m) => m.id === methodName);
|
|
119
|
-
if (!method) throw new
|
|
121
|
+
if (!method) throw new CommandError(`Unknown method: ${methodName}`, "INVALID_INPUT");
|
|
120
122
|
ctx.started({ method: method.export });
|
|
121
123
|
const result = await ctx.state.runner.runMethod({
|
|
122
124
|
methodExport: method.export,
|
|
@@ -129,7 +131,9 @@ async function handleRunMethod(ctx, cmd) {
|
|
|
129
131
|
success: result.success,
|
|
130
132
|
method: method.export,
|
|
131
133
|
output: result.output ?? null,
|
|
132
|
-
error: result.error ?? null,
|
|
134
|
+
error: result.error?.message ?? null,
|
|
135
|
+
errorCode: result.success ? void 0 : "EXECUTION_ERROR",
|
|
136
|
+
errorDetail: result.error ?? null,
|
|
133
137
|
stdout: result.stdout ?? [],
|
|
134
138
|
duration: result.duration
|
|
135
139
|
};
|
|
@@ -137,24 +141,24 @@ async function handleRunMethod(ctx, cmd) {
|
|
|
137
141
|
|
|
138
142
|
// src/dev/stdin-commands/impersonate.ts
|
|
139
143
|
async function handleImpersonate(ctx, cmd) {
|
|
140
|
-
if (!ctx.state.runner) throw new
|
|
144
|
+
if (!ctx.state.runner) throw new CommandError("No active session", "NO_SESSION");
|
|
141
145
|
const roles = cmd.roles;
|
|
142
|
-
if (!Array.isArray(roles)) throw new
|
|
146
|
+
if (!Array.isArray(roles)) throw new CommandError("impersonate requires roles array", "INVALID_INPUT");
|
|
143
147
|
await ctx.state.runner.setImpersonation(roles);
|
|
144
|
-
return { roles };
|
|
148
|
+
return { success: true, roles };
|
|
145
149
|
}
|
|
146
150
|
async function handleClearImpersonation(ctx) {
|
|
147
|
-
if (!ctx.state.runner) throw new
|
|
151
|
+
if (!ctx.state.runner) throw new CommandError("No active session", "NO_SESSION");
|
|
148
152
|
await ctx.state.runner.clearImpersonation();
|
|
149
|
-
return { roles: null };
|
|
153
|
+
return { success: true, roles: null };
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
// src/dev/stdin-commands/browser.ts
|
|
153
157
|
async function handleBrowser(ctx, cmd) {
|
|
154
|
-
if (!ctx.state.proxy) throw new
|
|
158
|
+
if (!ctx.state.proxy) throw new CommandError("No active proxy", "NO_BROWSER");
|
|
155
159
|
const steps = cmd.steps;
|
|
156
160
|
if (!Array.isArray(steps) || steps.length === 0) {
|
|
157
|
-
throw new
|
|
161
|
+
throw new CommandError('browser action requires a non-empty "steps" array', "INVALID_INPUT");
|
|
158
162
|
}
|
|
159
163
|
const preparedSteps = await injectScreenshotUploads(ctx, steps);
|
|
160
164
|
const result = await ctx.state.proxy.dispatchBrowserCommand(preparedSteps);
|
|
@@ -168,7 +172,10 @@ async function handleBrowser(ctx, cmd) {
|
|
|
168
172
|
delete stepResult.image;
|
|
169
173
|
}
|
|
170
174
|
}
|
|
175
|
+
const hasStepError = resultSteps.some((s) => s.error);
|
|
171
176
|
return {
|
|
177
|
+
success: !hasStepError,
|
|
178
|
+
...hasStepError ? { errorCode: "BROWSER_ERROR" } : {},
|
|
172
179
|
steps: resultSteps,
|
|
173
180
|
snapshot: result.snapshot,
|
|
174
181
|
logs: result.logs,
|
|
@@ -202,12 +209,12 @@ async function injectScreenshotUploads(ctx, steps) {
|
|
|
202
209
|
|
|
203
210
|
// src/dev/stdin-commands/screenshot-full-page.ts
|
|
204
211
|
async function handleScreenshotFullPage(ctx, cmd) {
|
|
205
|
-
if (!ctx.state.proxy) throw new
|
|
212
|
+
if (!ctx.state.proxy) throw new CommandError("No active proxy", "NO_BROWSER");
|
|
206
213
|
if (!ctx.state.proxy.isBrowserConnected()) {
|
|
207
|
-
throw new
|
|
214
|
+
throw new CommandError("No browser connected", "NO_BROWSER");
|
|
208
215
|
}
|
|
209
216
|
if (!ctx.state.runner?.getSession() || !ctx.state.appConfig?.appId) {
|
|
210
|
-
throw new
|
|
217
|
+
throw new CommandError("No active session", "NO_SESSION");
|
|
211
218
|
}
|
|
212
219
|
const startTime = Date.now();
|
|
213
220
|
const session = ctx.state.runner.getSession();
|
|
@@ -226,9 +233,10 @@ async function handleScreenshotFullPage(ctx, cmd) {
|
|
|
226
233
|
const resultSteps = result.steps;
|
|
227
234
|
const stepResult = resultSteps?.[resultSteps.length - 1]?.result;
|
|
228
235
|
if (!stepResult?.uploaded) {
|
|
229
|
-
throw new
|
|
236
|
+
throw new CommandError("Screenshot capture or upload failed", "UPLOAD_FAILED");
|
|
230
237
|
}
|
|
231
238
|
return {
|
|
239
|
+
success: true,
|
|
232
240
|
url: publicUrl,
|
|
233
241
|
width: stepResult.width,
|
|
234
242
|
height: stepResult.height,
|
|
@@ -239,9 +247,9 @@ async function handleScreenshotFullPage(ctx, cmd) {
|
|
|
239
247
|
|
|
240
248
|
// src/dev/stdin-commands/dev-server-restarting.ts
|
|
241
249
|
async function handleDevServerRestarting(ctx) {
|
|
242
|
-
if (!ctx.state.proxy) throw new
|
|
250
|
+
if (!ctx.state.proxy) throw new CommandError("No active proxy", "NO_BROWSER");
|
|
243
251
|
ctx.state.proxy.markUpstreamDown();
|
|
244
|
-
return {};
|
|
252
|
+
return { success: true };
|
|
245
253
|
}
|
|
246
254
|
|
|
247
255
|
// src/dev/stdin-commands/browser-status.ts
|
|
@@ -251,26 +259,40 @@ async function handleBrowserStatus(ctx) {
|
|
|
251
259
|
|
|
252
260
|
// src/dev/stdin-commands/reset-browser.ts
|
|
253
261
|
async function handleResetBrowser(ctx) {
|
|
254
|
-
if (!ctx.state.proxy) throw new
|
|
255
|
-
if (!ctx.state.proxy.isBrowserConnected()) throw new
|
|
262
|
+
if (!ctx.state.proxy) throw new CommandError("No active proxy", "NO_BROWSER");
|
|
263
|
+
if (!ctx.state.proxy.isBrowserConnected()) throw new CommandError("No browser connected", "NO_BROWSER");
|
|
264
|
+
const restoreResult = await ctx.state.proxy.dispatchBrowserCommand([
|
|
265
|
+
{ command: "restoreState" }
|
|
266
|
+
]);
|
|
267
|
+
const stepResult = restoreResult.steps?.[0];
|
|
268
|
+
const restored = stepResult?.result;
|
|
269
|
+
if (restored?.restored) {
|
|
270
|
+
const steps = [{ command: "reload" }];
|
|
271
|
+
if (restored.path && restored.path !== "/") {
|
|
272
|
+
steps.push({ command: "navigate", url: restored.path });
|
|
273
|
+
}
|
|
274
|
+
steps.push({ command: "snapshot" });
|
|
275
|
+
await ctx.state.proxy.dispatchBrowserCommand(steps);
|
|
276
|
+
return { success: true, restored: true, path: restored.path };
|
|
277
|
+
}
|
|
256
278
|
ctx.state.proxy.broadcastToClients("reload");
|
|
257
|
-
return {};
|
|
279
|
+
return { success: true, restored: false };
|
|
258
280
|
}
|
|
259
281
|
|
|
260
282
|
// src/dev/stdin-commands/db-query.ts
|
|
261
283
|
async function handleDbQuery(ctx, cmd) {
|
|
262
|
-
if (!ctx.state.runner) throw new
|
|
284
|
+
if (!ctx.state.runner) throw new CommandError("No active session", "NO_SESSION");
|
|
263
285
|
const session = ctx.state.runner.getSession();
|
|
264
|
-
if (!session) throw new
|
|
286
|
+
if (!session) throw new CommandError("No active session", "NO_SESSION");
|
|
265
287
|
const sql = cmd.sql;
|
|
266
|
-
if (!sql) throw new
|
|
288
|
+
if (!sql) throw new CommandError('db-query requires "sql"', "INVALID_INPUT");
|
|
267
289
|
let databaseId = cmd.databaseId;
|
|
268
290
|
if (!databaseId) {
|
|
269
|
-
if (session.databases.length === 0) throw new
|
|
291
|
+
if (session.databases.length === 0) throw new CommandError("No databases available", "NO_SESSION");
|
|
270
292
|
databaseId = session.databases[0].id;
|
|
271
293
|
}
|
|
272
294
|
const appId = ctx.state.appConfig?.appId;
|
|
273
|
-
if (!appId) throw new
|
|
295
|
+
if (!appId) throw new CommandError("No app config available", "NO_SESSION");
|
|
274
296
|
ctx.started({ databaseId, sql });
|
|
275
297
|
const token = await fetchCallbackToken(appId, session.sessionId);
|
|
276
298
|
const url = `${getApiBaseUrl()}/_internal/v2/db/query`;
|
|
@@ -287,7 +309,7 @@ async function handleDbQuery(ctx, cmd) {
|
|
|
287
309
|
});
|
|
288
310
|
if (!res.ok) {
|
|
289
311
|
const text = await res.text().catch(() => "");
|
|
290
|
-
throw new
|
|
312
|
+
throw new CommandError(`Database query failed: ${res.status} ${text}`, "EXECUTION_ERROR");
|
|
291
313
|
}
|
|
292
314
|
const data = await res.json();
|
|
293
315
|
return {
|
|
@@ -299,29 +321,32 @@ async function handleDbQuery(ctx, cmd) {
|
|
|
299
321
|
|
|
300
322
|
// src/dev/stdin-commands/setup-browser.ts
|
|
301
323
|
async function handleSetupBrowser(ctx, cmd) {
|
|
302
|
-
if (!ctx.state.proxy) throw new
|
|
324
|
+
if (!ctx.state.proxy) throw new CommandError("No active proxy", "NO_BROWSER");
|
|
303
325
|
if (!ctx.state.proxy.isBrowserConnected()) {
|
|
304
|
-
throw new
|
|
326
|
+
throw new CommandError("No browser connected", "NO_BROWSER");
|
|
305
327
|
}
|
|
306
|
-
if (!ctx.state.appConfig?.appId) throw new
|
|
328
|
+
if (!ctx.state.appConfig?.appId) throw new CommandError("No active session", "NO_SESSION");
|
|
307
329
|
const auth = cmd.auth;
|
|
308
330
|
const path = cmd.path || "/";
|
|
309
331
|
const steps = [];
|
|
332
|
+
steps.push({ command: "stashState" });
|
|
333
|
+
steps.push({
|
|
334
|
+
command: "evaluate",
|
|
335
|
+
script: `document.cookie = '__ms_auth=; Max-Age=0; Path=/; Secure; SameSite=None'`
|
|
336
|
+
});
|
|
310
337
|
if (auth) {
|
|
311
338
|
const { cookie } = await createAuthSession(ctx.state.appConfig.appId, auth);
|
|
312
339
|
steps.push({
|
|
313
340
|
command: "evaluate",
|
|
314
341
|
script: `document.cookie = '__ms_auth=${cookie}; Path=/; Secure; SameSite=None'`
|
|
315
342
|
});
|
|
316
|
-
steps.push({ command: "reload" });
|
|
317
343
|
}
|
|
344
|
+
steps.push({ command: "reload" });
|
|
318
345
|
if (path !== "/") {
|
|
319
346
|
steps.push({ command: "navigate", url: path });
|
|
320
347
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
await ctx.state.proxy.dispatchBrowserCommand(steps);
|
|
324
|
-
}
|
|
348
|
+
steps.push({ command: "snapshot" });
|
|
349
|
+
await ctx.state.proxy.dispatchBrowserCommand(steps);
|
|
325
350
|
return { success: true, path, authenticated: !!auth };
|
|
326
351
|
}
|
|
327
352
|
|
|
@@ -371,7 +396,8 @@ async function handleStdinCommand(cmd, state, cwd) {
|
|
|
371
396
|
if (!handler) {
|
|
372
397
|
emitResponse(action ?? "unknown", requestId, "completed", {
|
|
373
398
|
success: false,
|
|
374
|
-
error: `Unknown action: ${action}
|
|
399
|
+
error: `Unknown action: ${action}`,
|
|
400
|
+
errorCode: "UNKNOWN_ACTION"
|
|
375
401
|
});
|
|
376
402
|
return;
|
|
377
403
|
}
|
|
@@ -387,10 +413,13 @@ async function handleStdinCommand(cmd, state, cwd) {
|
|
|
387
413
|
log.info("stdin", "Command complete", { requestId, action, success: result.success !== false });
|
|
388
414
|
emitResponse(action, requestId, "completed", result);
|
|
389
415
|
} catch (err) {
|
|
390
|
-
|
|
416
|
+
const code = err instanceof CommandError ? err.code : "INFRASTRUCTURE";
|
|
417
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
418
|
+
log.warn("stdin", "Command failed", { requestId, action, error: message, errorCode: code });
|
|
391
419
|
emitResponse(action, requestId, "completed", {
|
|
392
420
|
success: false,
|
|
393
|
-
error:
|
|
421
|
+
error: message,
|
|
422
|
+
errorCode: code
|
|
394
423
|
});
|
|
395
424
|
}
|
|
396
425
|
}
|
|
@@ -669,4 +698,4 @@ async function startHeadless(opts = {}) {
|
|
|
669
698
|
export {
|
|
670
699
|
startHeadless
|
|
671
700
|
};
|
|
672
|
-
//# sourceMappingURL=chunk-
|
|
701
|
+
//# sourceMappingURL=chunk-PVWSKNUB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dev/ipc/ipc.ts","../src/dev/ipc/session-events.ts","../src/dev/stdin-commands/run-scenario.ts","../src/dev/stdin-commands/run-method.ts","../src/dev/stdin-commands/impersonate.ts","../src/dev/stdin-commands/browser.ts","../src/dev/stdin-commands/screenshot-full-page.ts","../src/dev/stdin-commands/dev-server-restarting.ts","../src/dev/stdin-commands/browser-status.ts","../src/dev/stdin-commands/reset-browser.ts","../src/dev/stdin-commands/db-query.ts","../src/dev/stdin-commands/setup-browser.ts","../src/dev/stdin-commands/index.ts","../src/headless.ts"],"sourcesContent":["/**\n * Central IPC module for headless mode.\n *\n * All stdout writes go through here. Two distinct message types:\n * - System events: unsolicited, no requestId (session lifecycle, connection, etc.)\n * - Command responses: always have requestId + status (started/completed)\n *\n * The caller distinguishes them by the presence of `requestId`.\n */\n\n/**\n * Emit a system event (no requestId).\n * Used for unsolicited events: session lifecycle, connection health, auth, etc.\n */\nexport function emitEvent(event: string, data?: Record<string, unknown>): void {\n process.stdout.write(JSON.stringify({ event, ...data }) + '\\n');\n}\n\n/**\n * Emit a command response (always has requestId).\n * Used for responses to stdin commands.\n */\nexport function emitResponse(\n action: string,\n requestId: string,\n status: 'started' | 'completed',\n data?: Record<string, unknown>,\n): void {\n process.stdout.write(\n JSON.stringify({ event: action, requestId, status, ...data }) + '\\n',\n );\n}\n","/**\n * Subscribe to DevRunner events and relay them as system events to stdout.\n * Only relays genuinely unsolicited events (poll-loop methods, connection, auth).\n * Command responses (scenarios, impersonation) are handled by stdin handlers directly.\n *\n * Returns an array of unsubscribe functions for cleanup on teardown.\n */\n\nimport { devRequestEvents } from './events';\nimport { emitEvent } from './ipc';\n\nexport function subscribeDevEvents(\n shutdown: () => Promise<void>,\n): Array<() => void> {\n const unsubs: Array<() => void> = [];\n\n // Platform-triggered method execution (poll loop)\n unsubs.push(\n devRequestEvents.onStart((event) => {\n emitEvent('platform-method-started', { id: event.id, method: event.method });\n }),\n );\n\n unsubs.push(\n devRequestEvents.onComplete((event) => {\n emitEvent('platform-method-completed', {\n id: event.id,\n success: event.success,\n duration: event.duration,\n ...(event.error ? { error: event.error } : {}),\n });\n }),\n );\n\n // Connection health\n unsubs.push(\n devRequestEvents.onConnectionWarning((message) => {\n emitEvent('connection-lost', { message });\n }),\n );\n\n unsubs.push(\n devRequestEvents.onConnectionRestored(() => {\n emitEvent('connection-restored');\n }),\n );\n\n // Session expiry\n unsubs.push(\n devRequestEvents.onSessionExpired(() => {\n emitEvent('session-expired');\n shutdown().then(() => process.exit(1));\n }),\n );\n\n // Auth refresh\n unsubs.push(\n devRequestEvents.onAuthRefreshStart((url) => {\n emitEvent('auth-refresh-start', { url });\n }),\n );\n\n unsubs.push(\n devRequestEvents.onAuthRefreshSuccess(() => {\n emitEvent('auth-refresh-success');\n }),\n );\n\n unsubs.push(\n devRequestEvents.onAuthRefreshFailed(() => {\n emitEvent('auth-refresh-failed');\n }),\n );\n\n return unsubs;\n}\n","import { detectAppConfig } from '../config/app-config';\nimport { CommandError } from './types';\nimport type { CommandContext } from './types';\n\nexport async function handleRunScenario(\n ctx: CommandContext,\n cmd: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.runner) throw new CommandError('No active session', 'NO_SESSION');\n\n const freshConfig = detectAppConfig(ctx.cwd) ?? ctx.state.appConfig;\n const scenario = freshConfig?.scenarios.find((s) => s.id === cmd.scenarioId);\n if (!scenario) throw new CommandError(`Unknown scenario: ${cmd.scenarioId}`, 'INVALID_INPUT');\n\n const scenarioName = scenario.name ?? scenario.export;\n ctx.started({ scenarioId: scenario.id, name: scenarioName });\n\n const skipTruncate = cmd.skipTruncate === true;\n const result = await ctx.state.runner.runScenario(scenario, { skipTruncate });\n\n // Reset the browser so it picks up the new data/roles from the scenario\n if (result.success && ctx.state.proxy?.isBrowserConnected()) {\n ctx.state.proxy.broadcastToClients('reload');\n }\n\n return {\n success: result.success,\n scenarioId: scenario.id,\n name: scenarioName,\n error: result.error ?? null,\n errorCode: result.success ? undefined : 'EXECUTION_ERROR',\n };\n}\n","import { detectAppConfig } from '../config/app-config';\nimport { CommandError } from './types';\nimport type { CommandContext } from './types';\n\nexport async function handleRunMethod(\n ctx: CommandContext,\n cmd: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.runner) throw new CommandError('No active session', 'NO_SESSION');\n\n const methodName = cmd.method as string;\n if (!methodName) throw new CommandError('run-method requires \"method\" (export name or ID)', 'INVALID_INPUT');\n\n const freshConfig = detectAppConfig(ctx.cwd) ?? ctx.state.appConfig;\n const method =\n freshConfig?.methods.find((m) => m.export === methodName) ??\n freshConfig?.methods.find((m) => m.id === methodName);\n if (!method) throw new CommandError(`Unknown method: ${methodName}`, 'INVALID_INPUT');\n\n ctx.started({ method: method.export });\n\n const result = await ctx.state.runner.runMethod({\n methodExport: method.export,\n methodPath: method.path,\n input: cmd.input ?? {},\n roles: Array.isArray(cmd.roles) ? cmd.roles as string[] : undefined,\n userId: typeof cmd.userId === 'string' ? cmd.userId : undefined,\n });\n\n return {\n success: result.success,\n method: method.export,\n output: result.output ?? null,\n error: result.error?.message ?? null,\n errorCode: result.success ? undefined : 'EXECUTION_ERROR',\n errorDetail: result.error ?? null,\n stdout: result.stdout ?? [],\n duration: result.duration,\n };\n}\n","import { CommandError } from './types';\nimport type { CommandContext } from './types';\n\nexport async function handleImpersonate(\n ctx: CommandContext,\n cmd: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.runner) throw new CommandError('No active session', 'NO_SESSION');\n\n const roles = cmd.roles as string[];\n if (!Array.isArray(roles)) throw new CommandError('impersonate requires roles array', 'INVALID_INPUT');\n\n await ctx.state.runner.setImpersonation(roles);\n return { success: true, roles };\n}\n\nexport async function handleClearImpersonation(\n ctx: CommandContext,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.runner) throw new CommandError('No active session', 'NO_SESSION');\n\n await ctx.state.runner.clearImpersonation();\n return { success: true, roles: null };\n}\n","import { getUploadUrl } from '../api';\nimport { CommandError } from './types';\nimport type { CommandContext } from './types';\n\nexport async function handleBrowser(\n ctx: CommandContext,\n cmd: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.proxy) throw new CommandError('No active proxy', 'NO_BROWSER');\n\n const steps = cmd.steps as Array<Record<string, unknown>>;\n if (!Array.isArray(steps) || steps.length === 0) {\n throw new CommandError('browser action requires a non-empty \"steps\" array', 'INVALID_INPUT');\n }\n\n // Inject upload details into any screenshotViewport steps so the browser\n // uploads directly to S3 instead of sending base64 over the WS connection.\n const preparedSteps = await injectScreenshotUploads(ctx, steps);\n\n const result = await ctx.state.proxy.dispatchBrowserCommand(preparedSteps);\n\n // Replace uploaded screenshot results with the public URL\n const resultSteps = (result.steps as Array<Record<string, unknown>>) ?? [];\n for (const step of resultSteps) {\n const stepResult = step.result as Record<string, unknown> | undefined;\n if (stepResult?.uploaded && stepResult?._publicUrl) {\n stepResult.url = stepResult._publicUrl;\n delete stepResult.uploaded;\n delete stepResult._publicUrl;\n delete stepResult.image;\n }\n }\n\n // Check for step-level errors from the browser agent\n const hasStepError = resultSteps.some((s) => s.error);\n\n return {\n success: !hasStepError,\n ...(hasStepError ? { errorCode: 'BROWSER_ERROR' } : {}),\n steps: resultSteps,\n snapshot: result.snapshot,\n logs: result.logs,\n duration: result.duration,\n };\n}\n\n/**\n * For each screenshotViewport step, get a presigned upload URL and attach it.\n * Non-screenshot steps are passed through unchanged.\n */\nasync function injectScreenshotUploads(\n ctx: CommandContext,\n steps: Array<Record<string, unknown>>,\n): Promise<Array<Record<string, unknown>>> {\n const session = ctx.state.runner?.getSession();\n const appId = ctx.state.appConfig?.appId;\n if (!session || !appId) return steps;\n\n const prepared: Array<Record<string, unknown>> = [];\n for (const step of steps) {\n if (step.command === 'screenshotViewport') {\n try {\n const { uploadUrl, uploadFields, publicUrl } = await getUploadUrl(\n appId,\n session.sessionId,\n 'jpg',\n 'image/jpeg',\n );\n prepared.push({ ...step, uploadUrl, uploadFields, _publicUrl: publicUrl });\n } catch {\n // If we can't get an upload URL, fall back to inline base64\n prepared.push(step);\n }\n } else {\n prepared.push(step);\n }\n }\n return prepared;\n}\n","import { getUploadUrl } from '../api';\nimport { CommandError } from './types';\nimport type { CommandContext } from './types';\n\nexport async function handleScreenshotFullPage(\n ctx: CommandContext,\n cmd: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.proxy) throw new CommandError('No active proxy', 'NO_BROWSER');\n if (!ctx.state.proxy.isBrowserConnected()) {\n throw new CommandError('No browser connected', 'NO_BROWSER');\n }\n if (!ctx.state.runner?.getSession() || !ctx.state.appConfig?.appId) {\n throw new CommandError('No active session', 'NO_SESSION');\n }\n\n const startTime = Date.now();\n\n // 1. Get presigned upload URL before dispatching to browser\n const session = ctx.state.runner.getSession()!;\n const { uploadUrl, uploadFields, publicUrl } = await getUploadUrl(\n ctx.state.appConfig.appId,\n session.sessionId,\n 'jpg',\n 'image/jpeg',\n );\n\n // 2. Dispatch to browser — optionally navigate first, then full-page screenshot\n const steps: Array<Record<string, unknown>> = [];\n if (cmd.path) {\n steps.push({ command: 'navigate', url: cmd.path as string });\n }\n steps.push({ command: 'screenshotFullPage', uploadUrl, uploadFields });\n\n const result = await ctx.state.proxy.dispatchBrowserCommand(steps, 120_000);\n\n // The screenshot result is the last step\n const resultSteps = result.steps as Array<Record<string, unknown>>;\n const stepResult = resultSteps?.[resultSteps.length - 1]\n ?.result as { width: number; height: number; uploaded?: boolean; styleMap?: string } | undefined;\n\n if (!stepResult?.uploaded) {\n throw new CommandError('Screenshot capture or upload failed', 'UPLOAD_FAILED');\n }\n\n return {\n success: true,\n url: publicUrl,\n width: stepResult.width,\n height: stepResult.height,\n ...(stepResult.styleMap ? { styleMap: stepResult.styleMap } : {}),\n duration: Date.now() - startTime,\n };\n}\n","import { CommandError } from './types';\nimport type { CommandContext } from './types';\n\nexport async function handleDevServerRestarting(\n ctx: CommandContext,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.proxy) throw new CommandError('No active proxy', 'NO_BROWSER');\n\n ctx.state.proxy.markUpstreamDown();\n return { success: true };\n}\n","import type { CommandContext } from './types';\n\nexport async function handleBrowserStatus(\n ctx: CommandContext,\n): Promise<Record<string, unknown>> {\n return { connected: ctx.state.proxy?.isBrowserConnected() ?? false };\n}\n","import { CommandError } from './types';\nimport type { CommandContext } from './types';\n\nexport async function handleResetBrowser(\n ctx: CommandContext,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.proxy) throw new CommandError('No active proxy', 'NO_BROWSER');\n if (!ctx.state.proxy.isBrowserConnected()) throw new CommandError('No browser connected', 'NO_BROWSER');\n\n // Try to restore stashed browser state (saved by setup-browser).\n // restoreState sets the cookie in the browser and clears the stash.\n const restoreResult = await ctx.state.proxy.dispatchBrowserCommand([\n { command: 'restoreState' },\n ]);\n\n const stepResult = (restoreResult.steps as Array<Record<string, unknown>>)?.[0];\n const restored = stepResult?.result as { restored: boolean; path?: string } | undefined;\n\n if (restored?.restored) {\n // Reload so the proxy resolves the restored cookie, then navigate back\n const steps: Array<Record<string, unknown>> = [{ command: 'reload' }];\n if (restored.path && restored.path !== '/') {\n steps.push({ command: 'navigate', url: restored.path });\n }\n steps.push({ command: 'snapshot' });\n await ctx.state.proxy.dispatchBrowserCommand(steps);\n return { success: true, restored: true, path: restored.path };\n }\n\n // No stash — fall back to broadcast reload (clears cookie, navigates to /)\n ctx.state.proxy.broadcastToClients('reload');\n return { success: true, restored: false };\n}\n","import { getApiBaseUrl } from '../../config';\nimport { fetchCallbackToken } from '../api';\nimport { CommandError } from './types';\nimport type { CommandContext } from './types';\n\nexport async function handleDbQuery(\n ctx: CommandContext,\n cmd: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.runner) throw new CommandError('No active session', 'NO_SESSION');\n\n const session = ctx.state.runner.getSession();\n if (!session) throw new CommandError('No active session', 'NO_SESSION');\n\n const sql = cmd.sql as string;\n if (!sql) throw new CommandError('db-query requires \"sql\"', 'INVALID_INPUT');\n\n // Resolve database — use explicit databaseId, or default to the first one\n let databaseId = cmd.databaseId as string | undefined;\n if (!databaseId) {\n if (session.databases.length === 0) throw new CommandError('No databases available', 'NO_SESSION');\n databaseId = session.databases[0].id;\n }\n\n const appId = ctx.state.appConfig?.appId;\n if (!appId) throw new CommandError('No app config available', 'NO_SESSION');\n\n ctx.started({ databaseId, sql });\n\n const token = await fetchCallbackToken(appId, session.sessionId);\n const url = `${getApiBaseUrl()}/_internal/v2/db/query`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: token,\n },\n body: JSON.stringify({\n databaseId,\n queries: [{ sql }],\n }),\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new CommandError(`Database query failed: ${res.status} ${text}`, 'EXECUTION_ERROR');\n }\n\n const data = await res.json() as { results: { rows: unknown[]; changes: number }[] };\n\n return {\n success: true,\n databaseId,\n results: data.results,\n };\n}\n","import { createAuthSession } from '../api';\nimport { CommandError } from './types';\nimport type { CommandContext } from './types';\n\nexport async function handleSetupBrowser(\n ctx: CommandContext,\n cmd: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.proxy) throw new CommandError('No active proxy', 'NO_BROWSER');\n if (!ctx.state.proxy.isBrowserConnected()) {\n throw new CommandError('No browser connected', 'NO_BROWSER');\n }\n if (!ctx.state.appConfig?.appId) throw new CommandError('No active session', 'NO_SESSION');\n\n const auth = cmd.auth as { email?: string; phone?: string; roles?: string[] } | undefined;\n const path = (cmd.path as string) || '/';\n\n const steps: Array<Record<string, unknown>> = [];\n\n // 1. Stash current browser state (cookie + URL) so reset-browser can restore it.\n // The browser agent only writes the stash if it's currently empty, so\n // multiple setup-browser calls preserve the user's original state.\n steps.push({ command: 'stashState' });\n\n // 2. Clear existing auth cookie (clean slate)\n steps.push({\n command: 'evaluate',\n script: `document.cookie = '__ms_auth=; Max-Age=0; Path=/; Secure; SameSite=None'`,\n });\n\n // 3. Mint and set automation auth cookie if requested\n if (auth) {\n const { cookie } = await createAuthSession(ctx.state.appConfig.appId, auth);\n steps.push({\n command: 'evaluate',\n script: `document.cookie = '__ms_auth=${cookie}; Path=/; Secure; SameSite=None'`,\n });\n }\n\n // 4. Reload so the proxy resolves the new (or cleared) cookie via /auth/me\n // and injects the correct window.__MINDSTUDIO__\n steps.push({ command: 'reload' });\n\n // 5. Navigate to target path if not root\n if (path !== '/') {\n steps.push({ command: 'navigate', url: path });\n }\n\n // Trailing snapshot ensures the command completes through the\n // stash/resume path after reload (reload kills the page; remaining\n // steps are stashed in sessionStorage and resumed on reconnect).\n steps.push({ command: 'snapshot' });\n\n await ctx.state.proxy.dispatchBrowserCommand(steps);\n\n return { success: true, path, authenticated: !!auth };\n}\n","/**\n * Stdin command router for headless mode.\n *\n * Reads NDJSON commands from stdin and dispatches to individual handlers.\n * Every command must include a `requestId` for response correlation.\n * The router wraps handlers with automatic response framing.\n */\n\nimport { emitResponse } from '../ipc/ipc';\nimport { log } from '../logging/logger';\nimport { handleRunScenario } from './run-scenario';\nimport { handleRunMethod } from './run-method';\nimport { handleImpersonate, handleClearImpersonation } from './impersonate';\nimport { handleBrowser } from './browser';\nimport { handleScreenshotFullPage } from './screenshot-full-page';\nimport { handleDevServerRestarting } from './dev-server-restarting';\nimport { handleBrowserStatus } from './browser-status';\nimport { handleResetBrowser } from './reset-browser';\nimport { handleDbQuery } from './db-query';\nimport { handleSetupBrowser } from './setup-browser';\nimport { CommandError } from './types';\nimport type { SessionState, CommandContext, CommandHandler } from './types';\n\nexport type { SessionState } from './types';\n\nconst handlers: Record<string, CommandHandler> = {\n 'run-method': handleRunMethod,\n 'run-scenario': handleRunScenario,\n 'impersonate': handleImpersonate,\n 'clear-impersonation': handleClearImpersonation,\n 'browser': handleBrowser,\n 'screenshotFullPage': handleScreenshotFullPage,\n 'browser-status': handleBrowserStatus,\n 'reset-browser': handleResetBrowser,\n 'db-query': handleDbQuery,\n 'setup-browser': handleSetupBrowser,\n 'dev-server-restarting': handleDevServerRestarting,\n};\n\nexport function setupStdinCommands(\n state: SessionState,\n cwd: string,\n): void {\n if (!process.stdin.readable) return;\n\n let buffer = '';\n process.stdin.setEncoding('utf-8');\n process.stdin.on('data', (chunk: string) => {\n buffer += chunk;\n let idx: number;\n while ((idx = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, idx).trim();\n buffer = buffer.slice(idx + 1);\n if (!line) continue;\n\n let cmd: { action: string; requestId?: string; [key: string]: unknown };\n try {\n cmd = JSON.parse(line);\n } catch {\n log.warn('stdin', 'Invalid JSON on stdin', { preview: line.slice(0, 100) });\n continue;\n }\n\n handleStdinCommand(cmd, state, cwd);\n }\n });\n}\n\nasync function handleStdinCommand(\n cmd: { action: string; requestId?: string; [key: string]: unknown },\n state: SessionState,\n cwd: string,\n): Promise<void> {\n const { requestId, action } = cmd;\n\n if (!requestId) {\n log.warn('stdin', 'Command rejected: missing requestId', { action });\n return;\n }\n\n const handler = handlers[action];\n if (!handler) {\n emitResponse(action ?? 'unknown', requestId, 'completed', {\n success: false,\n error: `Unknown action: ${action}`,\n errorCode: 'UNKNOWN_ACTION',\n });\n return;\n }\n\n log.info('stdin', 'Command received', { requestId, action });\n\n const ctx: CommandContext = {\n state,\n cwd,\n requestId,\n started: (data) => emitResponse(action, requestId, 'started', data),\n };\n\n try {\n const result = await handler(ctx, cmd);\n log.info('stdin', 'Command complete', { requestId, action, success: result.success !== false });\n emitResponse(action, requestId, 'completed', result);\n } catch (err) {\n const code = err instanceof CommandError ? err.code : 'INFRASTRUCTURE';\n const message = err instanceof Error ? err.message : String(err);\n log.warn('stdin', 'Command failed', { requestId, action, error: message, errorCode: code });\n emitResponse(action, requestId, 'completed', {\n success: false,\n error: message,\n errorCode: code,\n });\n }\n}\n","/**\n * Headless Dev Mode\n *\n * Runs the MindStudio dev tunnel without a TUI. Designed for programmatic\n * control by a parent process (e.g., a sandbox C&C server or CI pipeline).\n *\n * Outputs structured JSON events to stdout (one per line, newline-delimited).\n * The parent process reads these to track session state, method execution,\n * errors, and connection health.\n *\n * Does NOT start a dev server — the parent process manages that separately.\n * The tunnel just needs to know which port to proxy to.\n *\n * @module\n */\n\nimport { DevRunner } from './dev/execution/runner';\nimport { DevProxy } from './dev/proxy/proxy';\nimport { syncSchema } from './dev/api';\nimport {\n detectAppConfig,\n getWebInterfaceConfig,\n readTableSources,\n} from './dev/config/app-config';\nimport { initRequestLog, closeRequestLog } from './dev/logging/request-log';\nimport { initBrowserLog, closeBrowserLog } from './dev/logging/browser-log';\nimport { subscribeDevEvents } from './dev/ipc/session-events';\nimport { setupStdinCommands, type SessionState } from './dev/stdin-commands';\nimport { emitEvent } from './dev/ipc/ipc';\nimport {\n getApiKey,\n getApiBaseUrl,\n getUserId,\n getEnvironment,\n getConfigPath,\n} from './config';\nimport { initLoggerHeadless, log, type LogLevel } from './dev/logging/logger';\nimport { stablePort, detectGitBranch } from './dev/utils';\nimport { watchTableFiles } from './dev/config/table-watcher';\nimport { watchConfigFile } from './dev/config/config-watcher';\n\n/**\n * Options for headless dev mode.\n */\nexport interface HeadlessOptions {\n /** Working directory containing mindstudio.json. Defaults to process.cwd(). */\n cwd?: string;\n /** Port the dev server is running on. If omitted, reads from web.json. If neither, proxy is skipped. */\n devPort?: number;\n /** Preferred port for the local proxy. Defaults to a stable port derived from the app ID. */\n proxyPort?: number;\n /** Bind address for the proxy server. Use '0.0.0.0' for hosted sandboxes. Defaults to '127.0.0.1'. */\n bindAddress?: string;\n /** Log level for stderr output. Defaults to 'info'. */\n logLevel?: LogLevel;\n /** URL for the browser agent script. Defaults to unpkg latest. Set to an ngrok URL for development. */\n browserAgentUrl?: string;\n}\n\n\n// ---------------------------------------------------------------------------\n// Session lifecycle\n// ---------------------------------------------------------------------------\n\nasync function startSession(\n cwd: string,\n opts: HeadlessOptions,\n state: SessionState,\n shutdown: () => Promise<void>,\n): Promise<boolean> {\n const bindAddress = opts.bindAddress ?? '127.0.0.1';\n\n // Read fresh config\n const appConfig = detectAppConfig(cwd);\n if (!appConfig) {\n emitEvent('config-error', { message: 'No valid mindstudio.json found in ' + cwd });\n return false;\n }\n\n if (!appConfig.appId) {\n emitEvent('config-error', { message: 'Missing \"appId\" in mindstudio.json' });\n return false;\n }\n\n state.appConfig = appConfig;\n\n // Resolve dev port\n let devPort = opts.devPort ?? null;\n if (devPort === null) {\n const webConfig = getWebInterfaceConfig(appConfig, cwd);\n devPort = webConfig?.devPort ?? null;\n }\n\n emitEvent('session-starting', { appId: appConfig.appId, name: appConfig.name });\n\n try {\n // Start platform session\n const branch = detectGitBranch();\n const runner = new DevRunner(appConfig.appId, cwd, {\n branch,\n methods: appConfig.methods.map((m) => ({ id: m.id, export: m.export, path: m.path })),\n });\n runner.setAppConfig(appConfig);\n const session = await runner.start();\n state.runner = runner;\n\n // Initialize logs\n initRequestLog(cwd);\n initBrowserLog(cwd);\n\n // Sync schema\n if (appConfig.tables.length > 0) {\n try {\n const tableSources = readTableSources(appConfig, cwd);\n if (tableSources.length > 0) {\n const syncResult = await syncSchema(appConfig.appId, session.sessionId, tableSources);\n session.databases = syncResult.databases;\n emitEvent('schema-sync-completed', {\n created: syncResult.created,\n altered: syncResult.altered,\n errors: syncResult.errors,\n });\n } else {\n log.warn('session', 'No table source files found, skipping schema sync', {\n expected: appConfig.tables.map((t) => t.path),\n });\n }\n } catch (err) {\n emitEvent('schema-sync-completed', {\n created: [],\n altered: [],\n errors: [err instanceof Error ? err.message : 'Schema sync failed'],\n });\n }\n }\n\n // Start or reuse proxy\n if (devPort !== null && session.clientContext) {\n if (state.proxy) {\n // Proxy persists across restarts — just update the context\n state.proxy.updateClientContext(session.clientContext);\n } else {\n const proxy = new DevProxy(devPort, session.clientContext, appConfig.appId, bindAddress, opts.browserAgentUrl);\n const preferred = opts.proxyPort ?? stablePort(appConfig.appId);\n const proxyPort = await proxy.start(preferred);\n state.proxy = proxy;\n state.proxyPort = proxyPort;\n }\n\n runner.setProxyUrl(`http://${bindAddress === '0.0.0.0' ? 'localhost' : bindAddress}:${state.proxyPort}`);\n runner.setProxy(state.proxy);\n }\n\n emitEvent('session-started', {\n sessionId: session.sessionId,\n releaseId: session.releaseId,\n branch: session.branch,\n proxyPort: state.proxyPort,\n proxyUrl: state.proxyPort\n ? `http://${bindAddress === '0.0.0.0' ? 'localhost' : bindAddress}:${state.proxyPort}/`\n : null,\n webInterfaceUrl: session.webInterfaceUrl,\n roles: appConfig.roles.map((r) => ({ id: r.id, name: r.name ?? r.id, description: r.description })),\n scenarios: appConfig.scenarios.map((s) => ({\n id: s.id,\n name: s.name ?? s.export,\n description: s.description,\n path: s.path,\n roles: s.roles,\n })),\n });\n\n // Subscribe to runner events\n state.unsubscribers.push(...subscribeDevEvents(shutdown));\n\n // Watch table source files for changes — auto-sync without session restart\n setupTableWatchers(cwd, state);\n\n // Start polling for platform method requests now that schema sync,\n // proxy, and watchers are all set up. Starting earlier would risk\n // executing methods against stale session state (e.g. missing tables).\n runner.startPolling();\n\n return true;\n } catch (err) {\n emitEvent('config-error', {\n message: err instanceof Error ? err.message : 'Failed to start session',\n });\n return false;\n }\n}\n\nfunction setupTableWatchers(cwd: string, state: SessionState): void {\n if (!state.appConfig || state.appConfig.tables.length === 0) return;\n\n const cleanup = watchTableFiles(state.appConfig.tables, cwd, async () => {\n if (!state.runner || !state.appConfig?.appId) return;\n const session = state.runner.getSession();\n if (!session) return;\n\n emitEvent('schema-sync-started');\n log.info('session', 'Table source file changed, syncing schema');\n\n try {\n const tableSources = readTableSources(state.appConfig, cwd);\n if (tableSources.length > 0) {\n const result = await syncSchema(state.appConfig.appId, session.sessionId, tableSources);\n session.databases = result.databases;\n emitEvent('schema-sync-completed', {\n created: result.created,\n altered: result.altered,\n errors: result.errors,\n });\n log.info('session', 'Schema sync complete', { created: result.created, altered: result.altered });\n } else {\n log.warn('session', 'Table source file change detected but file(s) still missing', {\n expected: state.appConfig.tables.map((t) => t.path),\n });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Schema sync failed';\n emitEvent('schema-sync-completed', { created: [], altered: [], errors: [message] });\n log.warn('session', 'Schema sync failed', { error: message });\n }\n });\n\n state.unsubscribers.push(cleanup);\n}\n\n/** Tear down the runner, logs, and watchers. Proxy stays alive for reuse. */\nasync function teardownRunner(state: SessionState): Promise<void> {\n for (const unsub of state.unsubscribers) unsub();\n state.unsubscribers = [];\n\n if (state.runner) {\n await state.runner.stop().catch(() => {});\n state.runner = null;\n }\n\n closeRequestLog();\n closeBrowserLog();\n}\n\n/** Full teardown including proxy. Used on process shutdown. */\nasync function teardownAll(state: SessionState): Promise<void> {\n await teardownRunner(state);\n\n state.proxy?.stop();\n state.proxy = null;\n state.proxyPort = null;\n}\n\n// ---------------------------------------------------------------------------\n// Entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Start the dev tunnel in headless mode.\n */\nexport async function startHeadless(opts: HeadlessOptions = {}): Promise<void> {\n initLoggerHeadless(opts.logLevel ?? 'info');\n\n const cwd = opts.cwd ?? process.cwd();\n\n const apiKey = getApiKey();\n const userId = getUserId();\n log.info('session', 'Startup config', {\n configPath: getConfigPath(),\n environment: getEnvironment(),\n apiBaseUrl: getApiBaseUrl(),\n hasApiKey: !!apiKey,\n apiKeyPrefix: apiKey ? apiKey.slice(0, 8) + '...' : null,\n hasUserId: !!userId,\n userId: userId ?? null,\n cwd,\n });\n\n const state: SessionState = {\n runner: null,\n proxy: null,\n appConfig: null,\n proxyPort: null,\n unsubscribers: [],\n };\n\n let restarting = false;\n let cleanupConfigWatcher: (() => void) | undefined;\n\n let stopping = false;\n let degradedRetryTimer: ReturnType<typeof setInterval> | null = null;\n const shutdown = async () => {\n if (stopping) return;\n stopping = true;\n if (degradedRetryTimer) { clearInterval(degradedRetryTimer); degradedRetryTimer = null; }\n emitEvent('session-stopping');\n cleanupConfigWatcher?.();\n await teardownAll(state);\n emitEvent('session-stopped');\n };\n\n process.on('SIGTERM', () => { shutdown().then(() => process.exit(0)); });\n process.on('SIGINT', () => { shutdown().then(() => process.exit(0)); });\n\n // Initial session start — retry a few times with backoff before degrading.\n // Snapshot resumes often hit a transient 400 from /manage/start because the\n // platform-side session state is stale. A short retry usually recovers.\n const MAX_START_RETRIES = 5;\n let started = false;\n for (let attempt = 1; attempt <= MAX_START_RETRIES && !stopping; attempt++) {\n started = await startSession(cwd, opts, state, shutdown);\n if (started) break;\n if (attempt < MAX_START_RETRIES) {\n const delay = Math.min(1000 * 2 ** (attempt - 1), 10_000);\n log.info('session', `Start failed, retrying in ${delay}ms`, { attempt, maxAttempts: MAX_START_RETRIES });\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n\n if (!started && !stopping) {\n emitEvent('degraded-state', {\n reason: 'Config invalid or missing at boot. Waiting for valid mindstudio.json.',\n });\n log.warn('session', 'Booting in degraded state — no valid config. Watching for changes.');\n\n // Periodically retry in degraded state (covers transient platform issues\n // that outlast the initial retry window, e.g. long snapshot resume).\n degradedRetryTimer = setInterval(async () => {\n if (stopping || restarting || state.runner) {\n if (state.runner && degradedRetryTimer) {\n clearInterval(degradedRetryTimer);\n degradedRetryTimer = null;\n }\n return;\n }\n restarting = true;\n try {\n log.info('session', 'Retrying session start from degraded state');\n const ok = await startSession(cwd, opts, state, shutdown);\n if (ok) {\n emitEvent('degraded-state-resolved', { appId: state.appConfig?.appId });\n log.info('session', 'Recovered from degraded state');\n if (degradedRetryTimer) {\n clearInterval(degradedRetryTimer);\n degradedRetryTimer = null;\n }\n }\n } finally {\n restarting = false;\n }\n }, 15_000);\n }\n\n // Stdin command loop\n setupStdinCommands(state, cwd);\n\n // Watch mindstudio.json for changes — validate before teardown so corrupt\n // writes don't kill a running session. In degraded state (no runner), a\n // valid config triggers a fresh startSession.\n cleanupConfigWatcher = watchConfigFile(cwd, async () => {\n if (stopping || restarting) return;\n restarting = true;\n try {\n emitEvent('config-changed');\n\n // Validate BEFORE tearing down the running session\n const newConfig = detectAppConfig(cwd);\n if (!newConfig || !newConfig.appId) {\n emitEvent('config-error', {\n message: 'mindstudio.json is invalid — keeping current session',\n });\n log.warn('session', 'Config change detected but file is invalid, keeping current session');\n return;\n }\n\n const wasDegraded = !state.runner;\n await teardownRunner(state);\n const ok = await startSession(cwd, opts, state, shutdown);\n if (ok) {\n if (wasDegraded) {\n emitEvent('degraded-state-resolved', { appId: newConfig.appId });\n log.info('session', 'Recovered from degraded state');\n if (degradedRetryTimer) { clearInterval(degradedRetryTimer); degradedRetryTimer = null; }\n }\n if (state.proxy) {\n state.proxy.broadcastToClients('reload');\n }\n } else {\n emitEvent('degraded-state', {\n reason: 'Session restart failed after config change. Will retry on next change.',\n });\n log.warn('session', 'Session restart failed, entering degraded state');\n }\n } finally {\n restarting = false;\n }\n });\n\n // Keep the process alive — the poll loop runs in DevRunner\n await new Promise<void>(() => {});\n}\n"],"mappings":"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","names":[]}
|