@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.
@@ -7,7 +7,7 @@ import {
7
7
  setProviderInstallPath,
8
8
  submitProgress,
9
9
  submitResult
10
- } from "./chunk-UT7QCCAJ.js";
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-WDCWN4FW.js.map
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-UT7QCCAJ.js";
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 Error("No active session");
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 Error(`Unknown scenario: ${cmd.scenarioId}`);
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
- ...result.error ? { error: result.error } : {}
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 Error("No active session");
116
+ if (!ctx.state.runner) throw new CommandError("No active session", "NO_SESSION");
115
117
  const methodName = cmd.method;
116
- if (!methodName) throw new Error('run-method requires "method" (export name or ID)');
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 Error(`Unknown method: ${methodName}`);
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 Error("No active session");
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 Error("impersonate requires roles array");
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 Error("No active session");
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 Error("No active proxy \u2014 browser commands require a web interface");
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 Error('browser action requires a non-empty "steps" array');
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 Error("No active proxy");
212
+ if (!ctx.state.proxy) throw new CommandError("No active proxy", "NO_BROWSER");
206
213
  if (!ctx.state.proxy.isBrowserConnected()) {
207
- throw new Error("No browser connected, please refresh the MindStudio preview");
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 Error("No active session");
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 Error("Screenshot capture or upload failed");
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 Error("No active proxy");
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 Error("No active proxy");
255
- if (!ctx.state.proxy.isBrowserConnected()) throw new Error("No browser connected");
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 Error("No active session");
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 Error("No active session");
286
+ if (!session) throw new CommandError("No active session", "NO_SESSION");
265
287
  const sql = cmd.sql;
266
- if (!sql) throw new Error('db-query requires "sql"');
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 Error("No databases available");
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 Error("No app config available");
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 Error(`Database query failed: ${res.status} ${text}`);
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 Error("No active proxy");
324
+ if (!ctx.state.proxy) throw new CommandError("No active proxy", "NO_BROWSER");
303
325
  if (!ctx.state.proxy.isBrowserConnected()) {
304
- throw new Error("No browser connected, please refresh the MindStudio preview");
326
+ throw new CommandError("No browser connected", "NO_BROWSER");
305
327
  }
306
- if (!ctx.state.appConfig?.appId) throw new Error("No active session");
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
- if (steps.length > 0) {
322
- steps.push({ command: "snapshot" });
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
- log.warn("stdin", "Command failed", { requestId, action, error: err instanceof Error ? err.message : String(err) });
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: err instanceof Error ? err.message : String(err)
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-YWEWXNQ6.js.map
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":[]}