@mindstudio-ai/local-model-tunnel 0.5.23 → 0.5.24

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.
@@ -22,9 +22,9 @@ import {
22
22
  syncSchema,
23
23
  watchConfigFile,
24
24
  watchTableFiles
25
- } from "./chunk-HZZVPY7J.js";
25
+ } from "./chunk-RTXRTIYF.js";
26
26
 
27
- // src/dev/ipc.ts
27
+ // src/dev/ipc/ipc.ts
28
28
  function emitEvent(event, data) {
29
29
  process.stdout.write(JSON.stringify({ event, ...data }) + "\n");
30
30
  }
@@ -34,7 +34,7 @@ function emitResponse(action, requestId, status, data) {
34
34
  );
35
35
  }
36
36
 
37
- // src/dev/session-events.ts
37
+ // src/dev/ipc/session-events.ts
38
38
  function subscribeDevEvents(shutdown) {
39
39
  const unsubs = [];
40
40
  unsubs.push(
@@ -173,7 +173,7 @@ async function injectScreenshotUploads(ctx, steps) {
173
173
  if (!session || !appId) return steps;
174
174
  const prepared = [];
175
175
  for (const step of steps) {
176
- if (step.command === "screenshot") {
176
+ if (step.command === "screenshotViewport") {
177
177
  try {
178
178
  const { uploadUrl, uploadFields, publicUrl } = await getUploadUrl(
179
179
  appId,
@@ -192,8 +192,8 @@ async function injectScreenshotUploads(ctx, steps) {
192
192
  return prepared;
193
193
  }
194
194
 
195
- // src/dev/stdin-commands/screenshot.ts
196
- async function handleScreenshot(ctx, cmd) {
195
+ // src/dev/stdin-commands/screenshot-full-page.ts
196
+ async function handleScreenshotFullPage(ctx, cmd) {
197
197
  if (!ctx.state.proxy) throw new Error("No active proxy");
198
198
  if (!ctx.state.proxy.isBrowserConnected()) {
199
199
  throw new Error("No browser connected, please refresh the MindStudio preview");
@@ -209,9 +209,8 @@ async function handleScreenshot(ctx, cmd) {
209
209
  "jpg",
210
210
  "image/jpeg"
211
211
  );
212
- const fullPage = cmd.fullPage !== false;
213
212
  const result = await ctx.state.proxy.dispatchBrowserCommand(
214
- [{ command: "screenshot", fullPage, scrollToTop: true, uploadUrl, uploadFields }],
213
+ [{ command: "screenshotFullPage", uploadUrl, uploadFields }],
215
214
  12e4
216
215
  );
217
216
  const stepResult = result.steps?.[0]?.result;
@@ -253,7 +252,7 @@ var handlers = {
253
252
  "impersonate": handleImpersonate,
254
253
  "clear-impersonation": handleClearImpersonation,
255
254
  "browser": handleBrowser,
256
- "screenshot": handleScreenshot,
255
+ "screenshotFullPage": handleScreenshotFullPage,
257
256
  "browser-status": handleBrowserStatus,
258
257
  "reset-browser": handleResetBrowser,
259
258
  "dev-server-restarting": handleDevServerRestarting
@@ -518,4 +517,4 @@ async function startHeadless(opts = {}) {
518
517
  export {
519
518
  startHeadless
520
519
  };
521
- //# sourceMappingURL=chunk-ALEWMAQ4.js.map
520
+ //# sourceMappingURL=chunk-65SO6VZY.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('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('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 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 emitResponse(action, requestId, 'completed', result);\n } catch (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 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('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 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('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('Schema sync complete', { created: result.created, altered: result.altered });\n } else {\n log.warn('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('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('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) return;\n restarting = true;\n try {\n log.info('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"]}
@@ -86,7 +86,7 @@ function deleteLocalInterfacePath(key) {
86
86
  config.set("localInterfaces", interfaces);
87
87
  }
88
88
 
89
- // src/dev/logger.ts
89
+ // src/dev/logging/logger.ts
90
90
  import fs from "fs";
91
91
  var LEVELS = {
92
92
  error: 0,
@@ -274,7 +274,7 @@ var DevPollError = class extends Error {
274
274
  }
275
275
  };
276
276
 
277
- // src/dev/ndjson-log.ts
277
+ // src/dev/logging/ndjson-log.ts
278
278
  import fs2 from "fs";
279
279
  import { join } from "path";
280
280
  var NdjsonLog = class {
@@ -366,7 +366,7 @@ var NdjsonLog = class {
366
366
  }
367
367
  };
368
368
 
369
- // src/dev/request-log.ts
369
+ // src/dev/logging/request-log.ts
370
370
  var ndjsonLog = new NdjsonLog("requests.ndjson");
371
371
  function initRequestLog(projectRoot) {
372
372
  ndjsonLog.init(projectRoot);
@@ -415,7 +415,7 @@ function closeRequestLog() {
415
415
  ndjsonLog.close();
416
416
  }
417
417
 
418
- // src/dev/events.ts
418
+ // src/dev/ipc/events.ts
419
419
  import { EventEmitter } from "events";
420
420
  var DevEventEmitter = class extends EventEmitter {
421
421
  emitStart(event) {
@@ -477,7 +477,7 @@ var DevEventEmitter = class extends EventEmitter {
477
477
  };
478
478
  var devRequestEvents = new DevEventEmitter();
479
479
 
480
- // src/dev/transpiler.ts
480
+ // src/dev/execution/transpiler.ts
481
481
  import { unlink, mkdir, readdir } from "fs/promises";
482
482
  import { existsSync } from "fs";
483
483
  import { resolve, dirname, basename, join as join2 } from "path";
@@ -573,7 +573,7 @@ function findNearestNodeModules(startDir) {
573
573
  return null;
574
574
  }
575
575
 
576
- // src/dev/executor.ts
576
+ // src/dev/execution/executor.ts
577
577
  import { fork } from "child_process";
578
578
  import { writeFile, unlink as unlink2 } from "fs/promises";
579
579
  import { join as join3 } from "path";
@@ -939,10 +939,10 @@ async function disconnectHeartbeat() {
939
939
  }
940
940
  }
941
941
 
942
- // src/dev/runner.ts
942
+ // src/dev/execution/runner.ts
943
943
  import { randomBytes as randomBytes2 } from "crypto";
944
944
 
945
- // src/dev/format-error.ts
945
+ // src/dev/execution/format-error.ts
946
946
  function formatErrorForDisplay(error) {
947
947
  const parts = [];
948
948
  if (error.message) {
@@ -966,7 +966,7 @@ function formatErrorForDisplay(error) {
966
966
  return parts.join("\n");
967
967
  }
968
968
 
969
- // src/dev/runner.ts
969
+ // src/dev/execution/runner.ts
970
970
  var DevRunner = class {
971
971
  constructor(appId, projectRoot, startOpts = {}) {
972
972
  this.appId = appId;
@@ -1396,7 +1396,7 @@ var DevRunner = class {
1396
1396
  }
1397
1397
  };
1398
1398
 
1399
- // src/dev/browser-log.ts
1399
+ // src/dev/logging/browser-log.ts
1400
1400
  var ndjsonLog2 = new NdjsonLog("browser.ndjson");
1401
1401
  function initBrowserLog(projectRoot) {
1402
1402
  ndjsonLog2.init(projectRoot);
@@ -1413,12 +1413,12 @@ function closeBrowserLog() {
1413
1413
  ndjsonLog2.close();
1414
1414
  }
1415
1415
 
1416
- // src/dev/proxy.ts
1416
+ // src/dev/proxy/proxy.ts
1417
1417
  import http from "http";
1418
1418
  import { randomBytes as randomBytes4 } from "crypto";
1419
1419
  import { WebSocketServer } from "ws";
1420
1420
 
1421
- // src/dev/ws-clients.ts
1421
+ // src/dev/proxy/ws-clients.ts
1422
1422
  import { randomBytes as randomBytes3 } from "crypto";
1423
1423
  var ClientRegistry = class {
1424
1424
  clients = /* @__PURE__ */ new Map();
@@ -1507,7 +1507,7 @@ var ClientRegistry = class {
1507
1507
  }
1508
1508
  };
1509
1509
 
1510
- // src/dev/proxy.ts
1510
+ // src/dev/proxy/proxy.ts
1511
1511
  var DevProxy = class _DevProxy {
1512
1512
  constructor(upstreamPort, clientContext, bindAddress = "127.0.0.1", browserAgentUrl) {
1513
1513
  this.upstreamPort = upstreamPort;
@@ -2049,7 +2049,7 @@ ${agentScript}`;
2049
2049
  }
2050
2050
  };
2051
2051
 
2052
- // src/dev/app-config.ts
2052
+ // src/dev/config/app-config.ts
2053
2053
  import { readFileSync, existsSync as existsSync2 } from "fs";
2054
2054
  import { join as join4, dirname as dirname2 } from "path";
2055
2055
  function detectAppConfig(cwd = process.cwd()) {
@@ -2188,7 +2188,7 @@ function detectGitBranch() {
2188
2188
  }
2189
2189
  }
2190
2190
 
2191
- // src/dev/table-watcher.ts
2191
+ // src/dev/config/table-watcher.ts
2192
2192
  import { watch } from "chokidar";
2193
2193
  import { join as join5, dirname as dirname3, basename as basename2 } from "path";
2194
2194
  function watchTableFiles(tables, cwd, onChanged) {
@@ -2223,7 +2223,7 @@ function watchTableFiles(tables, cwd, onChanged) {
2223
2223
  };
2224
2224
  }
2225
2225
 
2226
- // src/dev/config-watcher.ts
2226
+ // src/dev/config/config-watcher.ts
2227
2227
  import { watch as watch2 } from "chokidar";
2228
2228
  import { join as join6 } from "path";
2229
2229
  function watchConfigFile(cwd, onChanged) {
@@ -2294,4 +2294,4 @@ export {
2294
2294
  watchTableFiles,
2295
2295
  watchConfigFile
2296
2296
  };
2297
- //# sourceMappingURL=chunk-HZZVPY7J.js.map
2297
+ //# sourceMappingURL=chunk-RTXRTIYF.js.map