@mindstudio-ai/local-model-tunnel 0.5.26 → 0.5.28

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-KAGWWCYB.js";
10
+ } from "./chunk-WECAPRKN.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-UP6M4UQ2.js.map
1398
+ //# sourceMappingURL=chunk-JCY2MXIQ.js.map
@@ -22,7 +22,7 @@ import {
22
22
  syncSchema,
23
23
  watchConfigFile,
24
24
  watchTableFiles
25
- } from "./chunk-KAGWWCYB.js";
25
+ } from "./chunk-WECAPRKN.js";
26
26
 
27
27
  // src/dev/ipc/ipc.ts
28
28
  function emitEvent(event, data) {
@@ -272,7 +272,7 @@ function setupStdinCommands(state, cwd) {
272
272
  try {
273
273
  cmd = JSON.parse(line);
274
274
  } catch {
275
- log.warn("Invalid JSON on stdin", { preview: line.slice(0, 100) });
275
+ log.warn("stdin", "Invalid JSON on stdin", { preview: line.slice(0, 100) });
276
276
  continue;
277
277
  }
278
278
  handleStdinCommand(cmd, state, cwd);
@@ -282,7 +282,7 @@ function setupStdinCommands(state, cwd) {
282
282
  async function handleStdinCommand(cmd, state, cwd) {
283
283
  const { requestId, action } = cmd;
284
284
  if (!requestId) {
285
- log.warn("Command rejected: missing requestId", { action });
285
+ log.warn("stdin", "Command rejected: missing requestId", { action });
286
286
  return;
287
287
  }
288
288
  const handler = handlers[action];
@@ -293,6 +293,7 @@ async function handleStdinCommand(cmd, state, cwd) {
293
293
  });
294
294
  return;
295
295
  }
296
+ log.info("stdin", "Command received", { requestId, action });
296
297
  const ctx = {
297
298
  state,
298
299
  cwd,
@@ -301,8 +302,10 @@ async function handleStdinCommand(cmd, state, cwd) {
301
302
  };
302
303
  try {
303
304
  const result = await handler(ctx, cmd);
305
+ log.info("stdin", "Command complete", { requestId, action, success: result.success !== false });
304
306
  emitResponse(action, requestId, "completed", result);
305
307
  } catch (err) {
308
+ log.warn("stdin", "Command failed", { requestId, action, error: err instanceof Error ? err.message : String(err) });
306
309
  emitResponse(action, requestId, "completed", {
307
310
  success: false,
308
311
  error: err instanceof Error ? err.message : String(err)
@@ -335,6 +338,7 @@ async function startSession(cwd, opts, state, shutdown) {
335
338
  branch,
336
339
  methods: appConfig.methods.map((m) => ({ id: m.id, export: m.export, path: m.path }))
337
340
  });
341
+ runner.setAppConfig(appConfig);
338
342
  const session = await runner.start();
339
343
  state.runner = runner;
340
344
  initRequestLog(cwd);
@@ -351,7 +355,7 @@ async function startSession(cwd, opts, state, shutdown) {
351
355
  errors: syncResult.errors
352
356
  });
353
357
  } else {
354
- log.warn("No table source files found, skipping schema sync", {
358
+ log.warn("session", "No table source files found, skipping schema sync", {
355
359
  expected: appConfig.tables.map((t) => t.path)
356
360
  });
357
361
  }
@@ -410,7 +414,7 @@ function setupTableWatchers(cwd, state) {
410
414
  const session = state.runner.getSession();
411
415
  if (!session) return;
412
416
  emitEvent("schema-sync-started");
413
- log.info("Table source file changed, syncing schema");
417
+ log.info("session", "Table source file changed, syncing schema");
414
418
  try {
415
419
  const tableSources = readTableSources(state.appConfig, cwd);
416
420
  if (tableSources.length > 0) {
@@ -421,16 +425,16 @@ function setupTableWatchers(cwd, state) {
421
425
  altered: result.altered,
422
426
  errors: result.errors
423
427
  });
424
- log.info("Schema sync complete", { created: result.created, altered: result.altered });
428
+ log.info("session", "Schema sync complete", { created: result.created, altered: result.altered });
425
429
  } else {
426
- log.warn("Table source file change detected but file(s) still missing", {
430
+ log.warn("session", "Table source file change detected but file(s) still missing", {
427
431
  expected: state.appConfig.tables.map((t) => t.path)
428
432
  });
429
433
  }
430
434
  } catch (err) {
431
435
  const message = err instanceof Error ? err.message : "Schema sync failed";
432
436
  emitEvent("schema-sync-completed", { created: [], altered: [], errors: [message] });
433
- log.warn("Schema sync failed", { error: message });
437
+ log.warn("session", "Schema sync failed", { error: message });
434
438
  }
435
439
  });
436
440
  state.unsubscribers.push(cleanup);
@@ -457,7 +461,7 @@ async function startHeadless(opts = {}) {
457
461
  const cwd = opts.cwd ?? process.cwd();
458
462
  const apiKey = getApiKey();
459
463
  const userId = getUserId();
460
- log.info("Startup config", {
464
+ log.info("session", "Startup config", {
461
465
  configPath: getConfigPath(),
462
466
  environment: getEnvironment(),
463
467
  apiBaseUrl: getApiBaseUrl(),
@@ -497,10 +501,13 @@ async function startHeadless(opts = {}) {
497
501
  }
498
502
  setupStdinCommands(state, cwd);
499
503
  cleanupConfigWatcher = watchConfigFile(cwd, async () => {
500
- if (stopping || restarting) return;
504
+ if (stopping || restarting) {
505
+ log.debug("session", "Config change ignored (restart in progress)");
506
+ return;
507
+ }
501
508
  restarting = true;
502
509
  try {
503
- log.info("mindstudio.json changed, restarting dev session");
510
+ log.info("session", "mindstudio.json changed, restarting dev session");
504
511
  emitEvent("config-changed");
505
512
  await teardownRunner(state);
506
513
  const ok2 = await startSession(cwd, opts, state, shutdown);
@@ -518,4 +525,4 @@ async function startHeadless(opts = {}) {
518
525
  export {
519
526
  startHeadless
520
527
  };
521
- //# sourceMappingURL=chunk-KVUXXJIF.js.map
528
+ //# sourceMappingURL=chunk-MCYHE5LW.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/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 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 Error('No active 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 Error(`Unknown scenario: ${cmd.scenarioId}`);\n\n const scenarioName = scenario.name ?? scenario.export;\n ctx.started({ scenarioId: scenario.id, name: scenarioName });\n\n const result = await ctx.state.runner.runScenario(scenario);\n\n return {\n success: result.success,\n scenarioId: scenario.id,\n name: scenarioName,\n ...(result.error ? { error: result.error } : {}),\n };\n}\n","import { detectAppConfig } from '../config/app-config';\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 Error('No active session');\n\n const methodName = cmd.method as string;\n if (!methodName) throw new Error('run-method requires \"method\" (export name or ID)');\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 Error(`Unknown method: ${methodName}`);\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 });\n\n return {\n success: result.success,\n method: method.export,\n output: result.output ?? null,\n error: result.error ?? null,\n stdout: result.stdout ?? [],\n duration: result.duration,\n };\n}\n","import 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 Error('No active session');\n\n const roles = cmd.roles as string[];\n if (!Array.isArray(roles)) throw new Error('impersonate requires roles array');\n\n await ctx.state.runner.setImpersonation(roles);\n return { roles };\n}\n\nexport async function handleClearImpersonation(\n ctx: CommandContext,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.runner) throw new Error('No active session');\n\n await ctx.state.runner.clearImpersonation();\n return { roles: null };\n}\n","import { getUploadUrl } from '../api';\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 Error('No active proxy — browser commands require a web interface');\n\n const steps = cmd.steps as Array<Record<string, unknown>>;\n if (!Array.isArray(steps) || steps.length === 0) {\n throw new Error('browser action requires a non-empty \"steps\" array');\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 return {\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 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 Error('No active proxy');\n if (!ctx.state.proxy.isBrowserConnected()) {\n throw new Error('No browser connected, please refresh the MindStudio preview');\n }\n if (!ctx.state.runner?.getSession() || !ctx.state.appConfig?.appId) {\n throw new Error('No active 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 — always full-page with scroll-to-top\n const result = await ctx.state.proxy.dispatchBrowserCommand(\n [{ command: 'screenshotFullPage', uploadUrl, uploadFields }],\n 120_000,\n );\n\n const stepResult = (result.steps as Array<Record<string, unknown>>)?.[0]\n ?.result as { width: number; height: number; uploaded?: boolean } | undefined;\n\n if (!stepResult?.uploaded) {\n throw new Error('Screenshot capture or upload failed');\n }\n\n return {\n url: publicUrl,\n width: stepResult.width,\n height: stepResult.height,\n duration: Date.now() - startTime,\n };\n}\n","import type { CommandContext } from './types';\n\nexport async function handleDevServerRestarting(\n ctx: CommandContext,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.proxy) throw new Error('No active proxy');\n\n ctx.state.proxy.markUpstreamDown();\n return {};\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 type { CommandContext } from './types';\n\nexport async function handleResetBrowser(\n ctx: CommandContext,\n): Promise<Record<string, unknown>> {\n if (!ctx.state.proxy) throw new Error('No active proxy');\n if (!ctx.state.proxy.isBrowserConnected()) throw new Error('No browser connected');\n\n ctx.state.proxy.broadcastToClients('reload');\n return {};\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 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 '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 });\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 log.warn('stdin', 'Command failed', { requestId, action, error: err instanceof Error ? err.message : String(err) });\n emitResponse(action, requestId, 'completed', {\n success: false,\n error: err instanceof Error ? err.message : String(err),\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, 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 const shutdown = async () => {\n if (stopping) return;\n stopping = true;\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 — crash if it fails so the process manager can retry\n const ok = await startSession(cwd, opts, state, shutdown);\n if (!ok) {\n process.exit(1);\n }\n\n // Stdin command loop\n setupStdinCommands(state, cwd);\n\n // Watch mindstudio.json for changes\n cleanupConfigWatcher = watchConfigFile(cwd, async () => {\n if (stopping || restarting) {\n log.debug('session', 'Config change ignored (restart in progress)');\n return;\n }\n restarting = true;\n try {\n log.info('session', 'mindstudio.json changed, restarting dev session');\n emitEvent('config-changed');\n await teardownRunner(state);\n const ok = await startSession(cwd, opts, state, shutdown);\n if (ok && state.proxy) {\n // Proxy stayed alive — clients are still connected, reload them\n state.proxy.broadcastToClients('reload');\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":["ok"]}