@tetherto/mdk-cli 0.0.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +447 -0
  3. package/dist/commands/add.js +61 -0
  4. package/dist/commands/add.js.map +1 -0
  5. package/dist/commands/agent.js +42 -0
  6. package/dist/commands/agent.js.map +1 -0
  7. package/dist/commands/create.js +261 -0
  8. package/dist/commands/create.js.map +1 -0
  9. package/dist/commands/inspect.js +171 -0
  10. package/dist/commands/inspect.js.map +1 -0
  11. package/dist/commands/meta.js +12 -0
  12. package/dist/commands/meta.js.map +1 -0
  13. package/dist/commands/onboard.js +447 -0
  14. package/dist/commands/onboard.js.map +1 -0
  15. package/dist/commands/run.js +203 -0
  16. package/dist/commands/run.js.map +1 -0
  17. package/dist/commands/validate.js +125 -0
  18. package/dist/commands/validate.js.map +1 -0
  19. package/dist/index.js +9 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/lib/add-package.js +148 -0
  22. package/dist/lib/add-package.js.map +1 -0
  23. package/dist/lib/app-package.js +182 -0
  24. package/dist/lib/app-package.js.map +1 -0
  25. package/dist/lib/app-scaffold.js +221 -0
  26. package/dist/lib/app-scaffold.js.map +1 -0
  27. package/dist/lib/app-ui-check.js +178 -0
  28. package/dist/lib/app-ui-check.js.map +1 -0
  29. package/dist/lib/app-ui-runner.js +122 -0
  30. package/dist/lib/app-ui-runner.js.map +1 -0
  31. package/dist/lib/catalog.js +69 -0
  32. package/dist/lib/catalog.js.map +1 -0
  33. package/dist/lib/checkout.js +14 -0
  34. package/dist/lib/checkout.js.map +1 -0
  35. package/dist/lib/dashboard-apps.js +71 -0
  36. package/dist/lib/dashboard-apps.js.map +1 -0
  37. package/dist/lib/dashboard.js +318 -0
  38. package/dist/lib/dashboard.js.map +1 -0
  39. package/dist/lib/detect.js +38 -0
  40. package/dist/lib/detect.js.map +1 -0
  41. package/dist/lib/env-file.js +57 -0
  42. package/dist/lib/env-file.js.map +1 -0
  43. package/dist/lib/fetch-template.js +33 -0
  44. package/dist/lib/fetch-template.js.map +1 -0
  45. package/dist/lib/help.js +22 -0
  46. package/dist/lib/help.js.map +1 -0
  47. package/dist/lib/names.js +36 -0
  48. package/dist/lib/names.js.map +1 -0
  49. package/dist/lib/npm.js +113 -0
  50. package/dist/lib/npm.js.map +1 -0
  51. package/dist/lib/package-source.js +76 -0
  52. package/dist/lib/package-source.js.map +1 -0
  53. package/dist/lib/pkg.js +5 -0
  54. package/dist/lib/pkg.js.map +1 -0
  55. package/dist/lib/plugin-setup.js +204 -0
  56. package/dist/lib/plugin-setup.js.map +1 -0
  57. package/dist/lib/ports.js +31 -0
  58. package/dist/lib/ports.js.map +1 -0
  59. package/dist/lib/project.js +297 -0
  60. package/dist/lib/project.js.map +1 -0
  61. package/dist/lib/run-lock.js +188 -0
  62. package/dist/lib/run-lock.js.map +1 -0
  63. package/dist/lib/runtime.js +683 -0
  64. package/dist/lib/runtime.js.map +1 -0
  65. package/dist/lib/scaffold.js +82 -0
  66. package/dist/lib/scaffold.js.map +1 -0
  67. package/dist/lib/shutdown.js +53 -0
  68. package/dist/lib/shutdown.js.map +1 -0
  69. package/dist/lib/skill.js +38 -0
  70. package/dist/lib/skill.js.map +1 -0
  71. package/dist/lib/spec.js +375 -0
  72. package/dist/lib/spec.js.map +1 -0
  73. package/dist/lib/status.js +245 -0
  74. package/dist/lib/status.js.map +1 -0
  75. package/dist/lib/theme.js +50 -0
  76. package/dist/lib/theme.js.map +1 -0
  77. package/dist/lib/worker-package.js +74 -0
  78. package/dist/lib/worker-package.js.map +1 -0
  79. package/dist/lib/worker-scaffold.js +122 -0
  80. package/dist/lib/worker-scaffold.js.map +1 -0
  81. package/dist/lib/worker-smoke.js +243 -0
  82. package/dist/lib/worker-smoke.js.map +1 -0
  83. package/dist/program.js +58 -0
  84. package/dist/program.js.map +1 -0
  85. package/package.json +59 -1
  86. package/templates/app/README.md +66 -0
  87. package/templates/app/mdk-app.yaml +10 -0
  88. package/templates/app/package.json +17 -0
  89. package/templates/app/plugin/controllers/summary.js +36 -0
  90. package/templates/app/plugin/lib/client.js +11 -0
  91. package/templates/app/plugin/mdk-plugin.json +16 -0
  92. package/templates/app-ui/tsconfig.json +29 -0
  93. package/templates/app-ui/ui-src/components/summary-panel.tsx +24 -0
  94. package/templates/app-ui/ui-src/data/summary.ts +18 -0
  95. package/templates/app-ui/ui-src/hooks/use-summary.ts +54 -0
  96. package/templates/app-ui/ui-src/index.tsx +15 -0
  97. package/templates/app-ui/ui-src/pages/overview-page.tsx +18 -0
  98. package/templates/app-ui/ui-src/styles.css +13 -0
  99. package/templates/app-ui/ui-src/styles.d.ts +3 -0
  100. package/templates/app-ui/ui-src/vite.config.ts +26 -0
  101. package/templates/worker/README.md +93 -0
  102. package/templates/worker/mdk-contract.json +26 -0
  103. package/templates/worker/mock/server.js +93 -0
  104. package/templates/worker/package.json +23 -0
  105. package/templates/worker/smoke.config.js +31 -0
  106. package/templates/worker/src/client.js +47 -0
  107. package/templates/worker/src/commands/reboot.js +5 -0
  108. package/templates/worker/src/commands/set-power-mode.js +10 -0
  109. package/templates/worker/src/telemetry/hashrate-rt.js +5 -0
  110. package/templates/worker/src/telemetry/power-mode.js +5 -0
  111. package/templates/worker/src/telemetry/power.js +5 -0
  112. package/templates/worker/src/telemetry/temperature.js +5 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/lib/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequire } from 'node:module';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { once, type EventEmitter } from 'node:events';\nimport { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { isLocalPackagePath, unresolvedEnvRef, type AppInstance, type StackSpec, type WorkerInstance } from './spec.js';\nimport { DEFAULT_HOST, findFreePort, isPortFree } from './ports.js';\nimport { APP_MANIFEST } from './app-package.js';\nimport { WORKER_CONTRACT } from './worker-package.js';\nimport { theme, tick, arrow } from './theme.js';\nimport { appChildFile, appChildPid } from './run-lock.js';\n\nconst require = createRequire(import.meta.url);\n\n// --- @tetherto/mdk-core runtime -------------------------------------------\n// The backend runtime is a typeless CommonJS package. It is `require`d lazily\n// (inside the boot functions, never at module top level) so the heavy backend\n// deps are only loaded when a `run` target actually boots — not on every CLI\n// invocation. These interfaces type just the surface `mdk run` touches.\n\n/** Handle returned by `getKernel`. */\nexport interface KernelHandle {\n getPublicKey?: () => Buffer | string;\n /** The key the Kernel dials workers with — what a worker must allow. */\n getClientPublicKey?: () => Buffer | null;\n /** Admit an HRPC caller (the Kernel is fail-closed). */\n allowKey?: (key: Buffer | string, roles?: string[]) => string;\n /** Hex keys admitted with at least `minRole`. */\n listAllowedKeys?: (minRole?: string) => string[];\n _cleanup: Array<() => unknown>;\n stop: () => Promise<void>;\n [key: string]: unknown;\n}\n\n/** Handle returned by `startGateway`. */\nexport interface GatewayHandle {\n stop: (cb?: () => void) => void;\n [key: string]: unknown;\n}\n\ninterface GetKernelOpts {\n root?: string;\n keyFile?: string | false;\n discovery?: { mode: 'local' | 'dht'; dir?: string; topic?: string };\n [key: string]: unknown;\n}\n\ninterface StartGatewayOpts {\n root?: string;\n port?: number;\n kernel?: KernelHandle;\n kernelKey?: unknown;\n keyFile?: string;\n /** The Gateway reads each app's route table and forwards to the runtime whose key is in `keyFile`. */\n apps?: Array<{ name: string; appDir: string; keyFile: string; mount?: string }>;\n /** Where the Gateway publishes its own public key; the app runtimes accept calls from it only. */\n gatewayKeyFile?: string;\n /** Overrides merged into the gateway's `config/common.json`. */\n common?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\ninterface MdkCore {\n getKernel(opts?: GetKernelOpts): Promise<KernelHandle>;\n startGateway(opts?: StartGatewayOpts): Promise<GatewayHandle>;\n [key: string]: unknown;\n}\n\n/** Lazily loads the `@tetherto/mdk-core` runtime (a workspace dependency). */\nfunction mdkCore(): MdkCore {\n return require('@tetherto/mdk-core') as MdkCore;\n}\n\n// --- @tetherto/mdk-worker runtime -----------------------------------------\n// Same lazy-`require` contract as `mdkCore` above: the WorkerRuntimeV2 host\n// lives in the backend and is only loaded when a worker actually boots. `mdk\n// run` plays the \"caller\" role (the way a host process\n// would) — it constructs the runtime from the package's directory, seeds\n// devices, and owns the lifecycle, while the worker *package* ships only its\n// `mdk-contract.json` + handlers and an optional mock.\n\n/** A booted WorkerRuntimeV2 host instance. */\ninterface WorkerRuntimeV2Instance {\n start(): Promise<{ publicKey: Buffer }>;\n stop(): Promise<void>;\n getPublicKey(): Buffer;\n [key: string]: unknown;\n}\ninterface WorkerRuntimeV2Ctor {\n new (dir: string, opts: Record<string, unknown>): WorkerRuntimeV2Instance;\n}\ninterface MdkWorker {\n WorkerRuntimeV2: WorkerRuntimeV2Ctor;\n [key: string]: unknown;\n}\n\n/** Lazily loads the `@tetherto/mdk-worker` runtime host (a workspace dependency). */\nfunction mdkWorker(): MdkWorker {\n return require('@tetherto/mdk-worker') as MdkWorker;\n}\n\n/** A persistent caller identity (`callerKeyPair`), in hyperdht's key format. */\nexport interface CallerKeyPair {\n publicKey: Buffer;\n secretKey: Buffer;\n}\n\n/** Local-discovery helpers from mdk-core — how components publish and trust each other's keys. */\ninterface LocalDiscovery {\n publishWorkerKey(dir: string, workerId: string, rpcKeyHex: string): void;\n publishTrustedKey(dir: string, name: string, key: Buffer | string): string;\n readTrustedKeys(dir: string): Array<{ name: string; key: string }>;\n trustKernel(runtime: WorkerRuntimeV2Instance, kernel: KernelHandle): { stop(): void };\n trustKeysFromDir(runtime: WorkerRuntimeV2Instance, dir: string): { stop(): void };\n callerKeyPair(dir: string): CallerKeyPair;\n}\nexport function localDiscovery(): LocalDiscovery {\n return require('@tetherto/mdk-core/lib/local-discovery') as LocalDiscovery;\n}\n\n// --- @tetherto/mdk-mcp runtime ---------------------------------------------\n// Same lazy-`require` contract as `mdkCore` above: the standalone MCP server\n// is only loaded when `mdk run mcp` actually boots it.\n\n/** Handle returned by `createMcpServer`. */\ninterface McpServerHandle {\n close: (cb?: () => void) => void;\n [key: string]: unknown;\n}\n/** `createMcpServer`'s security/lifecycle options (see backend/core/mcp/server.js). */\ninterface McpServerOptions {\n token?: string;\n allowWrite?: boolean;\n allowTools?: string[];\n identity?: Record<string, unknown>;\n allowedHosts?: string[];\n allowedOrigins?: string[];\n maxOutputLength?: number;\n handleSignals?: boolean;\n keyFile?: string;\n}\ninterface MdkMcp {\n createMcpServer(\n root: string,\n port: number,\n config: Record<string, unknown>,\n pluginDirs: string[],\n apps: ForwardedApp[],\n options?: McpServerOptions,\n ): Promise<McpServerHandle>;\n [key: string]: unknown;\n}\n\n/** Lazily loads the `@tetherto/mdk-mcp` runtime (a workspace dependency). */\nfunction mdkMcp(): MdkMcp {\n return require('@tetherto/mdk-mcp') as MdkMcp;\n}\n\n// --- @tetherto/mdk-app-runtime ----------------------------------------------\n// Same lazy-`require` contract as `mdkCore` above. `mdk run app` creates the\n// runtime in-process; `mdk run` spawns the package's `bin/run-app.js` per app.\n\n/** `createAppRuntime` options; a supervised child gets them JSON-encoded in `MDK_APP_RUNTIME_OPTIONS`. */\nexport interface AppRuntimeOptions {\n name: string;\n appDir: string;\n config: Record<string, unknown>;\n kernelKey?: string;\n storeDir: string;\n allowedKeyFiles: string[];\n keyFile: string;\n kernelKeyFile: string;\n}\n\ninterface MdkAppRuntime {\n createAppRuntime(opts: AppRuntimeOptions): {\n start(): Promise<unknown>;\n stop(): Promise<void>;\n };\n}\n\n/** Lazily loads the `@tetherto/mdk-app-runtime` package (a workspace dependency). */\nfunction mdkAppRuntime(): MdkAppRuntime {\n return require('@tetherto/mdk-app-runtime') as MdkAppRuntime;\n}\n\n// --- project-local runtime state ------------------------------------------\n// Everything `mdk run` writes lives under `<project>/.mdk` — gitignored and safe\n// to delete. Each component gets its own data root so no two of them share a\n// directory, while the two cross-process handoff artifacts sit at the top level\n// where either side can find them: `kernel.key` (read by the gateway) and\n// `keys/` (written by workers, watched by the kernel). This leaves room for\n// `logs/` and `run/` when `mdk logs` and `mdk status` land.\n\n/** Absolute runtime-state root for a project — `<project>/.mdk`. */\nexport function mdkDir(projectDir: string): string {\n return resolve(projectDir, '.mdk');\n}\n\n/** A path under `.mdk`, e.g. `componentDir(dir, 'workers', name)`. */\nfunction componentDir(projectDir: string, ...parts: string[]): string {\n return join(mdkDir(projectDir), ...parts);\n}\n\n/** Shared Kernel HRPC key file — how a separate gateway process discovers the Kernel. */\nexport function kernelKeyFile(projectDir: string): string {\n return componentDir(projectDir, 'kernel.key');\n}\n\n/** Where the Gateway publishes its public key; app runtimes accept calls from it and the MCP server's. */\nexport function gatewayKeyFile(projectDir: string): string {\n return componentDir(projectDir, 'gateway.key');\n}\n\n/** Where the MCP server publishes the key it calls app runtimes with. */\nexport function mcpKeyFile(projectDir: string): string {\n return componentDir(projectDir, 'mcp.key');\n}\n\n/** Where an app runtime publishes its public key, for the Gateway to find it. */\nexport function appKeyFile(projectDir: string, name: string): string {\n return componentDir(projectDir, 'apps', name, 'runtime.key');\n}\n\n/**\n * Shared worker-key dir for `discovery: { mode: 'local' }`, passed *explicitly*\n * to both sides. mdk-core otherwise defaults it to `<root>/.worker-keys`, which\n * would nest it inside whichever component's data root was passed — the Kernel\n * and its workers must agree on one path independent of their own roots.\n */\nexport function workerKeysDir(projectDir: string): string {\n return componentDir(projectDir, 'keys');\n}\n\n/**\n * Shared trusted-keys dir. The Kernel and every Worker reject unknown RPC\n * callers, so each component publishes the key it dials with here\n * (`kernel.pub`, `gateway.pub`, `mcp.pub`, `app-<name>.pub`): the Kernel admits callers from it\n * and separately-run workers allow what they find in it.\n */\nexport function trustedKeysDir(projectDir: string): string {\n return componentDir(projectDir, 'trusted-keys');\n}\n\n/** Default gap between the Gateway's port and the MCP server's, when `spec.mcp.port` is not set. */\nexport const DEFAULT_MCP_PORT_OFFSET = 100;\n\n/**\n * Splits a spec `package` into the npm package to resolve and the subdirectory\n * inside it, if any: `@scope/pkg/plugin-a` is one installed package and one\n * directory within it, not a package called `.../plugin-a`.\n *\n * Subpaths exist for packages that ship several plugins as subdirectories of a\n * single package, with no `package.json` of their own.\n */\nfunction splitPackageSubpath(pkgName: string): { pkg: string; subpath: string } {\n const parts = pkgName.split('/');\n const nameLength = pkgName.startsWith('@') ? 2 : 1;\n return { pkg: parts.slice(0, nameLength).join('/'), subpath: parts.slice(nameLength).join('/') };\n}\n\n/** The spec the project's package.json gives `pkg`, if it lists it. */\nfunction declaredDependency(projectDir: string, pkg: string): string | undefined {\n try {\n const manifest = JSON.parse(readFileSync(join(resolve(projectDir), 'package.json'), 'utf8')) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n return manifest.dependencies?.[pkg] ?? manifest.devDependencies?.[pkg];\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolves an installed package's root directory from the target project's\n * `node_modules` (via its `package.json`), or a subdirectory of one. Throws an\n * actionable error naming the missing package — a package `mdk.yaml` declares\n * must be installed into the project first.\n */\nexport function resolveProjectPackageDir(projectDir: string, pkgName: string): string {\n const { pkg, subpath } = splitPackageSubpath(pkgName);\n\n let root: string;\n try {\n root = dirname(require.resolve(`${pkg}/package.json`, { paths: [resolve(projectDir)] }));\n } catch {\n // A dependency package.json already declares (a `file:` app, say) only needs\n // `npm install`; naming it would fetch an unrelated registry package. The\n // install command names the *package*: `npm install @scope/pkg/plugin-a`\n // is not a thing npm can do.\n const declared = declaredDependency(projectDir, pkg);\n throw new Error(\n `Package \"${pkgName}\" is not installed in ${resolve(projectDir)}.\\n` +\n (declared\n ? `package.json declares it (\"${declared}\"), so run: npm install`\n : `Install it first, e.g.: npm install ${pkg}`),\n );\n }\n\n if (!subpath) return root;\n\n // A subpath that is not there is a typo in mdk.yaml, not a missing install, and\n // the two want different fixes. Whether the directory holds a plugin manifest\n // (or a worker contract) is left to whoever loads it — that check belongs with\n // the loader that knows which of the two it wanted.\n const dir = join(root, subpath);\n if (!existsSync(dir)) {\n throw new Error(\n `Package \"${pkgName}\" is installed as \"${pkg}\", but it has no \"${subpath}\" in it (looked in ${dir}).\\n` +\n `Check the subpath — e.g. package: \"${pkg}/<dir>\".`,\n );\n }\n return dir;\n}\n\n/** Boots the Kernel (local discovery, project-local state), logging its key. */\nexport async function runKernel(projectDir: string): Promise<KernelHandle> {\n const core = mdkCore();\n const root = componentDir(projectDir, 'kernel');\n mkdirSync(root, { recursive: true });\n\n process.stderr.write(`${theme.muted('Starting kernel')} ${arrow} ${theme.value(root)}\\n`);\n const kernel = await core.getKernel({\n root,\n keyFile: kernelKeyFile(projectDir),\n discovery: { mode: 'local', dir: workerKeysDir(projectDir) },\n trustedKeysDir: trustedKeysDir(projectDir),\n });\n\n const key = typeof kernel.getPublicKey === 'function' ? kernel.getPublicKey() : undefined;\n const keyHex = key ? key.toString('hex') : '(unknown)';\n process.stderr.write(`${tick} ${theme.label('kernel')} ready ${theme.muted(keyHex)}\\n`);\n return kernel;\n}\n\n/**\n * Shared by assertGatewayPortFree/assertMcpPortFree below: unlike a mock port,\n * these are published endpoints (the dashboard proxies to the gateway's, agents\n * dial the MCP one directly), so neither is ever relocated behind the\n * operator's back — fail with the fix instead of a bare EADDRINUSE from deep\n * inside the server. One implementation keeps the two messages from drifting\n * apart as the wording around them changes.\n */\nasync function assertPortFree(port: number, label: string, specKey: string): Promise<void> {\n if (await isPortFree(port)) return;\n throw new Error(\n `${label} port ${port} is already in use.\\n` +\n `Another stack is probably running — stop it, or change \\`${specKey}\\` in mdk.yaml.`,\n );\n}\n\n/** Exported so `run all` can check the gateway's port before booting anything. */\nexport async function assertGatewayPortFree(port: number): Promise<void> {\n return assertPortFree(port, 'Gateway', 'spec.gateway.port');\n}\n\n/** An app as the Gateway and the MCP server take it: its route table, and its runtime's key file to forward to. */\ninterface ForwardedApp {\n name: string;\n appDir: string;\n keyFile: string;\n mount?: string;\n}\n\n/** Neither runs app code, so an app's config goes to its runtime only. */\nfunction forwardedApps(projectDir: string, apps: AppInstance[]): ForwardedApp[] {\n return apps.map((a) => ({\n name: a.name,\n appDir: resolveAppPackageDir(projectDir, a.package),\n keyFile: appKeyFile(projectDir, a.name),\n ...(a.mount !== undefined ? { mount: a.mount } : {}),\n }));\n}\n\n/**\n * Boots the Gateway. When `kernel` is provided the connection is in-process\n * (target `all`); otherwise the Kernel key is resolved from the shared key\n * file written by a separate `mdk run kernel` process.\n */\nexport async function runGateway(\n projectDir: string,\n spec: StackSpec,\n kernel?: KernelHandle,\n): Promise<GatewayHandle> {\n const core = mdkCore();\n\n // Resolve every path to an absolute one *before* the chdir below, so nothing\n // depends on the (about-to-change) working directory.\n const keyFile = kernelKeyFile(projectDir);\n const root = componentDir(projectDir, 'gateway');\n const ownKeyFile = gatewayKeyFile(projectDir);\n const trustDir = trustedKeysDir(projectDir);\n const apps = forwardedApps(projectDir, spec.spec.apps);\n\n // Out-of-process gateway: the Kernel must already be running so its HRPC key\n // is on disk. Fail early with an actionable message instead of silently\n // booting a gateway that can never reach a Kernel.\n if (!kernel && !existsSync(keyFile)) {\n throw new Error(\n 'Kernel is not running (no key file found).\\nStart it first: `mdk run kernel`.',\n );\n }\n\n await assertGatewayPortFree(spec.spec.gateway.port);\n\n mkdirSync(root, { recursive: true });\n\n // The gateway's corestore (`store/http`) is created relative to the working\n // directory (tether-wrk-base convention — the mvp-site example likewise runs\n // each process with its own `cwd`). Run from the gateway state root so that\n // store nests under `.mdk/gateway/` instead of polluting the project.\n process.chdir(root);\n\n process.stderr.write(\n `${theme.muted('Starting gateway')} ${arrow} ${theme.value(`:${spec.spec.gateway.port}`)}\\n`,\n );\n const gateway = await core.startGateway({\n root,\n port: spec.spec.gateway.port,\n ...(kernel ? { kernel } : { keyFile }),\n apps,\n gatewayKeyFile: ownKeyFile,\n // Admits the gateway's client key on the Kernel and on workers in other processes.\n trustedKeysDir: trustDir,\n ...(Object.keys(spec.spec.gateway.config ?? {}).length ? { common: spec.spec.gateway.config } : {}),\n // `auth: true` routes (the agent chat) admit callers on this machine unless\n // mdk.yaml names another built-in verifier in gateway.config.authVerifier.\n ...(spec.spec.gateway.config?.authVerifier === undefined ? { authVerifier: 'loopback' } : {}),\n });\n\n process.stderr.write(\n `${tick} ${theme.label('gateway')} listening on ${theme.value(`http://localhost:${spec.spec.gateway.port}`)}\\n`,\n );\n return gateway;\n}\n\n/** Same reasoning as `assertGatewayPortFree` — see `assertPortFree` above. */\nexport async function assertMcpPortFree(port: number): Promise<void> {\n return assertPortFree(port, 'MCP', 'spec.mcp.port');\n}\n\n/** The Kernel's public key (hex): from an in-process handle, else from the key file `mdk run kernel` wrote. */\nfunction resolveKernelKey(projectDir: string, kernel?: KernelHandle): string | undefined {\n if (kernel) {\n const key = typeof kernel.getPublicKey === 'function' ? kernel.getPublicKey() : undefined;\n return key ? (Buffer.isBuffer(key) ? key.toString('hex') : String(key)) : undefined;\n }\n const keyFile = kernelKeyFile(projectDir);\n if (existsSync(keyFile)) return readFileSync(keyFile, 'utf8').trim();\n throw new Error(\n 'Kernel is not running (no key file found).\\nStart it first: `mdk run kernel`.',\n );\n}\n\n/** Handle returned by `runMcp`. */\nexport interface McpHandle {\n stop: () => Promise<void>;\n}\n\n/**\n * Boots the standalone MCP server, pointed at the apps `mdk.yaml` declares\n * under `spec.apps` — their routes become MCP\n * tools with no separate mcp-specific plugin authoring required, each call\n * forwarded to the app's runtime, so an app's tools answer once `mdk run` has\n * started it. Requires the\n * Kernel to already be reachable: an in-process `kernel` handle (target\n * `all`, if this is ever wired into it), or the shared key file written by a\n * separately-running `mdk run kernel` — the same contract `runGateway` uses.\n */\nexport async function runMcp(\n projectDir: string,\n spec: StackSpec,\n kernel?: KernelHandle,\n flags: { allowWrite?: boolean } = {},\n): Promise<McpHandle> {\n // An unset variable leaves the literal `${NAME}` as the token: /mcp would look\n // protected while accepting a bearer anyone can read in mdk.yaml.\n const token = spec.spec.mcp.token;\n const unresolved = token ? unresolvedEnvRef(token) : undefined;\n if (unresolved) {\n throw new Error(\n `\\`spec.mcp.token\\` uses \\${${unresolved}}, but ${unresolved} is not set in the environment or the project .env.\\n` +\n `Set ${unresolved}, or remove \\`spec.mcp.token\\` to run /mcp without a bearer token.`,\n );\n }\n\n const mcp = mdkMcp();\n\n const root = componentDir(projectDir, 'mcp');\n const apps = forwardedApps(projectDir, spec.spec.apps);\n const kernelKey = resolveKernelKey(projectDir, kernel);\n\n const port = spec.spec.mcp.port ?? spec.spec.gateway.port + DEFAULT_MCP_PORT_OFFSET;\n await assertMcpPortFree(port);\n mkdirSync(root, { recursive: true });\n\n // A persistent caller identity every tool plugin's client dials with\n // (config.kernelKeyPair), admitted on the in-process Kernel and published\n // for a separately-running Kernel and its workers.\n const discovery = localDiscovery();\n const kernelKeyPair = discovery.callerKeyPair(join(root, 'identity'));\n if (kernel && typeof kernel.allowKey === 'function') kernel.allowKey(kernelKeyPair.publicKey);\n discovery.publishTrustedKey(trustedKeysDir(projectDir), 'mcp', kernelKeyPair.publicKey);\n\n process.stderr.write(`${theme.muted('Starting mcp')} ${arrow} ${theme.value(`:${port}`)}\\n`);\n // Like the Gateway, the MCP server runs no app code: every tool call is\n // forwarded to the app's runtime (started by `mdk run`), bounded by\n // `spec.gateway.config.appTimeoutMs`.\n const server = await mcp.createMcpServer(\n root,\n port,\n { ...(spec.spec.gateway.config ?? {}), kernelKey, kernelKeyPair },\n // `pluginDirs` (native, author-written `mcp-plugin.json` tools) is not yet\n // reachable from `mdk.yaml` — there is no `spec.mcp.plugins` list. App\n // route tools (via `apps` below) are deliberately the only\n // CLI path today: every declared app's routes already become\n // tools with no separate authoring, which covers the common case. Add a\n // `spec.mcp.plugins` list here if native MCP-only plugins need a CLI path.\n [],\n apps,\n {\n // Write tools are opt-in: `--allow-write` or `spec.mcp.allowWrite`.\n allowWrite: flags.allowWrite === true || spec.spec.mcp.allowWrite === true,\n ...(spec.spec.mcp.allowTools ? { allowTools: spec.spec.mcp.allowTools } : {}),\n ...(spec.spec.mcp.token ? { token: spec.spec.mcp.token } : {}),\n ...(spec.spec.mcp.identity ? { identity: spec.spec.mcp.identity } : {}),\n // The CLI owns SIGINT/SIGTERM (installShutdown) and stops via close().\n handleSignals: false,\n // Every app runtime allow-lists this key (resolveAppOptions).\n keyFile: mcpKeyFile(projectDir),\n },\n );\n process.stderr.write(\n `${tick} ${theme.label('mcp')} listening on ${theme.value(`http://localhost:${port}/mcp`)}\\n`,\n );\n\n return { stop: () => new Promise((resolve) => server.close(() => resolve())) };\n}\n\n// --- worker boot ----------------------------------------------------------\n\n/** A seed device from a worker's `config.devices` — `opts` is plugin-defined. */\ninterface SeedDevice {\n id: string;\n opts?: Record<string, unknown>;\n}\n\n/** A running mock device server (the shape mvp-site and scaffolded-worker mocks return). */\ninterface MockServer {\n /**\n * The listening object, whose type depends on the mock's transport: a\n * `net.Server` for the TCP/Modbus mocks, a Fastify instance for the HTTP ones.\n * Only the members both share can be relied on here.\n */\n server?: {\n on?: (event: string, cb: () => void) => void;\n /** Present on a `net.Server`; a Fastify instance nests it under `.server`. */\n listening?: boolean;\n /** Node >= 18.2 — drops lingering keep-alive sockets so the port frees now. */\n closeAllConnections?: () => void;\n /** Fastify's underlying `http.Server`. */\n server?: { closeAllConnections?: () => void };\n };\n /** Resolves once the transport is bound — the mock framework's own signal. */\n ready?: Promise<unknown>;\n exit: () => void;\n}\ninterface MockModule {\n createServer(opts: Record<string, unknown>): MockServer;\n}\n\n/**\n * Waits until a freshly created mock is actually accepting connections.\n *\n * The mock framework binds differently per transport, so neither signal alone is\n * enough: HTTP mocks hand back a Fastify instance and expose the bind as a\n * `ready` promise, while TCP mocks hand back a `net.Server` that has already\n * been told to listen and reports it by emitting `'listening'`. Waiting on the\n * event alone would hang forever on a Fastify instance (it never emits it), and\n * waiting on `ready` alone would return before a TCP mock is bound.\n */\nasync function awaitMockReady(mock: MockServer): Promise<void> {\n if (mock.ready) await mock.ready;\n\n const server = mock.server;\n // `listening` is the tell for a real net.Server; guarding on it also avoids\n // waiting for an event that has already fired.\n if (server?.on && server.listening === false) {\n await once(server as unknown as EventEmitter, 'listening');\n }\n}\n\n/** Frees the listener now, whether the mock is Fastify-shaped or net.Server-shaped. */\nfunction dropMockConnections(mock: MockServer): void {\n mock.server?.closeAllConnections?.();\n mock.server?.server?.closeAllConnections?.();\n}\n\n/**\n * Resolves each seed device's mock port to one that is actually bindable.\n *\n * A mock port is a private contract between the simulator this CLI starts and\n * the plugin that dials it — nothing outside the process cares which number it\n * is. So when the configured port is taken (a second stack on the machine, a\n * worker left running, two devices sharing a port), relocating beats refusing to\n * boot. The configured port is always tried first, so a free one stays stable\n * and matches mdk.yaml. Real (non-mock) devices are never touched: their port\n * belongs to the hardware.\n */\nasync function resolveMockPorts(\n workerName: string,\n seeds: SeedDevice[],\n reserved?: Iterable<number>,\n): Promise<Array<Record<string, unknown>>> {\n // Seeded with the ports later workers have declared but not yet bound, so one\n // squatter cannot cascade: a relocated worker steps over its neighbours'\n // ports instead of pushing each of them along in turn.\n const claimed = new Set<number>(reserved ?? []);\n const resolved: Array<Record<string, unknown>> = [];\n\n for (const d of seeds) {\n const opts = { ...(d.opts ?? {}) };\n const wanted = opts.port;\n if (typeof wanted === 'number') {\n const host = typeof opts.host === 'string' ? opts.host : DEFAULT_HOST;\n const available = !claimed.has(wanted) && (await isPortFree(wanted, host));\n const port = available ? wanted : await findFreePort({ start: wanted + 1, host, taken: claimed });\n if (port !== wanted) {\n process.stderr.write(\n theme.muted(\n ` mock port ${wanted} is in use — device \"${d.id}\" of \"${workerName}\" moved to ${port}\\n`,\n ),\n );\n }\n opts.port = port;\n claimed.add(port);\n }\n resolved.push(opts);\n }\n\n return resolved;\n}\n\n/** Mock ports a worker's seed devices ask for — what a later boot must avoid. */\nexport function declaredMockPorts(worker: WorkerInstance): number[] {\n const config = worker.config as { mock?: boolean; devices?: SeedDevice[] };\n if (!config.mock || !Array.isArray(config.devices)) return [];\n return config.devices\n .map((d) => d.opts?.port)\n .filter((port): port is number => typeof port === 'number');\n}\n\n/** A handle over a booted worker: its runtime plus any mock servers it owns. */\nexport interface WorkerHandle {\n runtime: WorkerRuntimeV2Instance;\n mocks: MockServer[];\n stop: () => Promise<void>;\n}\n\n/**\n * Resolves a spec `package` to its directory, the one way `run`, `status` and\n * `add` all do it: a **local path** (`./…`, `../…` or absolute, resolved against\n * the project dir) or an installed package, optionally with a subpath (see\n * `resolveProjectPackageDir`). Throws naming the fix when it does not resolve.\n */\nexport function resolvePackageDir(projectDir: string, pkg: string): string {\n if (!isLocalPackagePath(pkg)) return resolveProjectPackageDir(projectDir, pkg);\n const dir = resolve(projectDir, pkg);\n if (!existsSync(dir)) {\n throw new Error(`Package path \"${pkg}\" does not exist (looked in ${dir}).`);\n }\n return dir;\n}\n\n/**\n * Resolves a worker package to its directory, and confirms it is a\n * contract-first Worker Plugin (an `mdk-contract.json` at its root) — the only\n * shape `mdk run worker` supports. Checked here, rather than left to surface\n * as `WorkerRuntimeV2`'s own `ERR_CONTRACT_NOT_FOUND`/`ERR_WORKER_DIR_REQUIRED`,\n * so a misshapen package gets a message that names the fix.\n */\nfunction resolveWorkerContractDir(projectDir: string, pkg: string): string {\n const dir = resolvePackageDir(projectDir, pkg);\n if (!existsSync(join(dir, WORKER_CONTRACT))) {\n throw new Error(\n `Worker package \"${pkg}\" has no ${WORKER_CONTRACT} — mdk run only supports contract-first ` +\n 'Worker Plugins (a directory with mdk-contract.json at its root; `mdk create worker` scaffolds one).',\n );\n }\n return dir;\n}\n\n/**\n * Boots one worker on WorkerRuntimeV2, exactly the way the mvp-site example\n * does: start a mock device server per seed device (so the plugin talks to a\n * simulator instead of hardware), build the runtime's device set from\n * `config.devices`, then register with the Kernel.\n *\n * Discovery follows the same contract as the rest of MDK: an in-process\n * `kernel` handle registers the worker by key (target `all`); otherwise the\n * worker publishes its RPC key to the shared keys dir for a separately-running\n * local-mode Kernel to pick up.\n */\nexport async function runWorker(\n projectDir: string,\n worker: WorkerInstance,\n kernel?: KernelHandle,\n opts: { reservedPorts?: Iterable<number> } = {},\n): Promise<WorkerHandle> {\n const dir = resolveWorkerContractDir(projectDir, worker.package);\n\n const config = worker.config as {\n devices?: SeedDevice[];\n mock?: boolean;\n };\n const seeds = Array.isArray(config.devices) ? config.devices : [];\n if (seeds.length === 0) {\n throw new Error(\n `Worker \"${worker.name}\" has no \\`config.devices\\` in mdk.yaml — nothing to run.`,\n );\n }\n\n // Per-worker persistent state: plugin SQLite under storeDir, DHT/RPC seeds\n // under storeDir/identity so the worker's public key survives restarts.\n const storeDir = componentDir(projectDir, 'workers', worker.name);\n mkdirSync(storeDir, { recursive: true });\n const dbPath = join(storeDir, 'data.db');\n const identityDir = join(storeDir, 'identity');\n // Holds the worker's key seeds: owner-only, also for a dir an older CLI made.\n mkdirSync(identityDir, { recursive: true, mode: 0o700 });\n chmodSync(identityDir, 0o700);\n\n process.stderr.write(\n `${theme.muted('Starting worker')} ${arrow} ${theme.value(worker.name)} ` +\n `${theme.muted(`(${seeds.length} device${seeds.length === 1 ? '' : 's'})`)}\\n`,\n );\n\n // Per-device options, with mock ports resolved to something actually bindable\n // before anything starts. Both the mock and the plugin read from this same\n // list, so they cannot disagree about which port the device is on.\n const deviceOpts = config.mock\n ? await resolveMockPorts(worker.name, seeds, opts.reservedPorts)\n : seeds.map((d) => ({ ...(d.opts ?? {}) }));\n\n // Mock device servers — one per seed device, from `<pkg>/mock/server`.\n const mocks: MockServer[] = [];\n if (config.mock) {\n const mockPath = join(dir, 'mock', 'server.js');\n if (!existsSync(mockPath)) {\n throw new Error(`config.mock is set but ${worker.package} has no mock/server.js.`);\n }\n const mockMod = require(mockPath) as MockModule;\n for (const [i, d] of seeds.entries()) {\n const opts = deviceOpts[i];\n try {\n const m = mockMod.createServer(opts);\n mocks.push(m);\n // Wait until the mock is actually listening — the plugin's connect()\n // probes the device on boot, so a not-yet-bound port would fail startup.\n await awaitMockReady(m);\n } catch (error) {\n throw new Error(\n `Could not start the mock device for worker \"${worker.name}\", device \"${d.id}\" ` +\n `on ${String(opts.host ?? DEFAULT_HOST)}:${String(opts.port)}: ` +\n `${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n }\n\n // Device set: `config.devices[].opts` is the plugin's own per-device config;\n // dbPath is threaded through for the sample plugin's SQLite persistence.\n const devices = seeds.map((d, i) => ({ deviceId: d.id, config: { ...deviceOpts[i], dbPath } }));\n\n // Worker RPC is fail-closed: allow the Kernel's client key and the callers\n // it admitted (in process), or what the Kernel and Gateway published to the\n // trusted-keys dir (separate process), and keep following later admissions.\n const discovery = localDiscovery();\n const trustDir = trustedKeysDir(projectDir);\n let allowedKeys: Array<Buffer | string>;\n if (kernel) {\n const client = typeof kernel.getClientPublicKey === 'function' ? kernel.getClientPublicKey() : null;\n if (!client) throw new Error('Kernel has no client key (is HRPC enabled?) — cannot admit it on the worker.');\n allowedKeys = [client, ...(kernel.listAllowedKeys?.('write') ?? [])];\n } else {\n const trusted = discovery.readTrustedKeys(trustDir);\n if (!trusted.some((k) => k.name === 'kernel')) {\n throw new Error(\n 'Kernel is not running (no kernel key in the trusted-keys dir).\\nStart it first: `mdk run kernel`.',\n );\n }\n allowedKeys = trusted.map((k) => k.key);\n }\n\n const { WorkerRuntimeV2 } = mdkWorker();\n const runtime = new WorkerRuntimeV2(dir, {\n workerId: worker.name,\n devices,\n env: worker.env ?? {},\n storeDir: identityDir,\n allowedKeys,\n });\n const trust = kernel ? discovery.trustKernel(runtime, kernel) : discovery.trustKeysFromDir(runtime, trustDir);\n try {\n await runtime.start();\n } catch (error) {\n trust.stop();\n throw error;\n }\n\n const rpcKeyHex = runtime.getPublicKey().toString('hex');\n if (kernel) {\n await (kernel as unknown as { registerWorker: (k: Buffer) => Promise<void> }).registerWorker(\n runtime.getPublicKey(),\n );\n if (Array.isArray(kernel._cleanup)) {\n kernel._cleanup.push(() => {\n trust.stop();\n return runtime.stop();\n });\n }\n } else {\n discovery.publishWorkerKey(workerKeysDir(projectDir), worker.name, rpcKeyHex);\n }\n\n process.stderr.write(\n `${tick} ${theme.label('worker')} ${theme.value(worker.name)} ready ${theme.muted(rpcKeyHex.slice(0, 16))}\\n`,\n );\n\n // Signals are owned by the `run` command (see lib/shutdown.ts), which stops\n // every component in reverse boot order and exits even if one of them wedges.\n const stop = async (): Promise<void> => {\n trust.stop();\n await runtime.stop();\n for (const m of mocks) {\n try {\n m.exit();\n // `server.close()` only stops new connections; a keep-alive socket from\n // the plugin's poller would hold the listener (and its port) open.\n dropMockConnections(m);\n } catch {\n /* best-effort */\n }\n }\n };\n\n return { runtime, mocks, stop };\n}\n\n// --- app runtimes ---------------------------------------------------------\n\n/**\n * Resolves an app package's directory — a local path (resolved against the\n * project dir) or an installed package, optionally with a subpath — and\n * confirms it holds an `mdk-app.yaml`.\n */\nexport function resolveAppPackageDir(projectDir: string, pkg: string): string {\n const dir = resolvePackageDir(projectDir, pkg);\n if (!existsSync(join(dir, APP_MANIFEST))) {\n throw new Error(`App package \"${pkg}\" has no ${APP_MANIFEST} (looked in ${dir}).`);\n }\n return dir;\n}\n\n/** The `createAppRuntime` options for one `spec.apps[]` entry. */\nexport function resolveAppOptions(\n projectDir: string,\n app: AppInstance,\n kernel?: KernelHandle,\n): AppRuntimeOptions {\n return {\n name: app.name,\n appDir: resolveAppPackageDir(projectDir, app.package),\n config: app.config,\n kernelKey: resolveKernelKey(projectDir, kernel),\n storeDir: componentDir(projectDir, 'apps', app.name, 'store'),\n // Read per connection by the runtime, so the Gateway and MCP server may start after the app.\n allowedKeyFiles: [gatewayKeyFile(projectDir), mcpKeyFile(projectDir)],\n keyFile: appKeyFile(projectDir, app.name),\n // The Kernel is fail-closed and admits every `<name>.pub` in the trusted-keys dir.\n kernelKeyFile: join(trustedKeysDir(projectDir), `app-${app.name}.pub`),\n };\n}\n\n/** Boots one app runtime in this process (`mdk run app <name>`); the Kernel must already be running. */\nexport async function runApp(projectDir: string, app: AppInstance): Promise<{ stop: () => Promise<void> }> {\n const options = resolveAppOptions(projectDir, app);\n mkdirSync(options.storeDir, { recursive: true });\n\n process.stderr.write(`${theme.muted('Starting app')} ${arrow} ${theme.value(app.name)}\\n`);\n const runtime = mdkAppRuntime().createAppRuntime(options);\n await runtime.start();\n process.stderr.write(`${tick} ${theme.label('app')} ${theme.value(app.name)} ready\\n`);\n return { stop: () => runtime.stop() };\n}\n\nconst APP_STARTUP_TIMEOUT_MS = 30_000;\n/** First restart delay; doubled after each crash, up to the max. */\nconst APP_RESTART_DELAY_MS = 1000;\nconst APP_RESTART_MAX_MS = 30_000;\n/** A child that ran this long before exiting was healthy: its restart starts the backoff over. */\nconst APP_STABLE_MS = 60_000;\nconst APP_STOP_GRACE_MS = 3000;\n\n/**\n * Runs one app runtime as a child process (`mdk run`). Resolves once the child\n * reports `ready`; rejects if it reports an `error`, exits first, or stays\n * silent past the startup timeout. An exit `stop()` did not ask for is\n * restarted, with a delay that doubles while the app keeps crashing. `entry`\n * defaults to the runtime's `bin/run-app.js`.\n */\nexport async function superviseApp(\n projectDir: string,\n app: AppInstance,\n kernel?: KernelHandle,\n entry = require.resolve('@tetherto/mdk-app-runtime/bin/run-app.js'),\n): Promise<{ stop: () => Promise<void> }> {\n const options = resolveAppOptions(projectDir, app, kernel);\n mkdirSync(options.storeDir, { recursive: true });\n // Resolved now: a restart runs after the CLI has chdir'd into the Gateway's state root.\n const cwd = resolve(projectDir);\n // The options carry the app's config, so they go in the environment, not argv.\n const env = { ...process.env, MDK_APP_RUNTIME_OPTIONS: JSON.stringify(options) };\n const log = (line: string): boolean => process.stderr.write(`${line}\\n`);\n const childFile = appChildFile(projectDir, app.name);\n\n let child: ChildProcess | undefined;\n let stopped = false;\n let restartTimer: NodeJS.Timeout | undefined;\n let restartDelay = APP_RESTART_DELAY_MS;\n let readyAt = 0;\n\n const launch = (): Promise<void> =>\n new Promise((resolveReady, rejectReady) => {\n // `cwd` is pinned because the CLI later chdirs into the Gateway's state\n // root. `detached` keeps a terminal Ctrl+C from reaching the child: the\n // CLI stops it, and the child exits by itself when its IPC channel closes.\n const proc = spawn(process.execPath, [entry], {\n stdio: ['ignore', 'inherit', 'inherit', 'ipc'],\n env,\n cwd,\n // On Windows `detached` opens a console window per child instead.\n detached: process.platform !== 'win32',\n });\n child = proc;\n let ready = false;\n const fail = (error: Error): void => {\n clearTimeout(timer);\n proc.kill('SIGKILL');\n rejectReady(error);\n };\n const timer = setTimeout(\n () => fail(new Error(`App \"${app.name}\" did not report ready within ${APP_STARTUP_TIMEOUT_MS}ms.`)),\n APP_STARTUP_TIMEOUT_MS,\n );\n\n proc.on('message', (raw) => {\n const msg = (raw ?? {}) as { type?: string; message?: string };\n if (ready) return;\n if (msg.type === 'ready') {\n ready = true;\n readyAt = Date.now();\n clearTimeout(timer);\n mkdirSync(dirname(childFile), { recursive: true });\n writeFileSync(childFile, String(proc.pid));\n resolveReady();\n } else if (msg.type === 'error') {\n fail(new Error(msg.message ?? 'unknown error'));\n }\n });\n proc.on('error', fail);\n proc.on('exit', (code, signal) => {\n clearTimeout(timer);\n if (child === proc) child = undefined;\n if (appChildPid(projectDir, app.name) === proc.pid) rmSync(childFile, { force: true });\n const how = signal ? `signal ${signal}` : `code ${code}`;\n if (!ready) {\n rejectReady(new Error(`App \"${app.name}\" exited before it was ready (${how}).`));\n } else if (!stopped) {\n if (Date.now() - readyAt >= APP_STABLE_MS) restartDelay = APP_RESTART_DELAY_MS;\n log(\n `${theme.warn('!')} app ${theme.value(app.name)} exited unexpectedly (${how}); ` +\n `restarting in ${Math.round(restartDelay / 1000)}s`,\n );\n scheduleRestart();\n }\n });\n });\n\n const scheduleRestart = (): void => {\n const delay = restartDelay;\n restartDelay = Math.min(restartDelay * 2, APP_RESTART_MAX_MS);\n restartTimer = setTimeout(() => {\n launch().then(\n () => log(`${tick} ${theme.label('app')} ${theme.value(app.name)} restarted`),\n (error: Error) => {\n if (stopped) return;\n log(`${theme.warn('!')} app ${theme.value(app.name)} failed to restart: ${error.message}`);\n scheduleRestart();\n },\n );\n }, delay);\n };\n\n log(`${theme.muted('Starting app')} ${arrow} ${theme.value(app.name)}`);\n await launch();\n log(`${tick} ${theme.label('app')} ${theme.value(app.name)} ready`);\n\n return {\n stop: async () => {\n stopped = true;\n clearTimeout(restartTimer);\n const proc = child;\n if (!proc) return;\n const killTimer = setTimeout(() => proc.kill('SIGKILL'), APP_STOP_GRACE_MS);\n const exited = once(proc, 'exit');\n proc.kill('SIGTERM');\n await exited;\n clearTimeout(killTimer);\n },\n };\n}\n"]}
@@ -0,0 +1,82 @@
1
+ import { cpSync, existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { isMap, isSeq, parseDocument, YAMLSeq } from 'yaml';
4
+ import { addFileDependency, ensureProjectManifest, readStackName } from './project.js';
5
+ const errorText = (error) => (error instanceof Error ? error.message : String(error));
6
+ function walk(dir, out = []) {
7
+ for (const entry of readdirSync(dir)) {
8
+ const full = join(dir, entry);
9
+ if (statSync(full).isDirectory())
10
+ walk(full, out);
11
+ else
12
+ out.push(full);
13
+ }
14
+ return out;
15
+ }
16
+ /** Copies a bundled template to `dest`, replacing every `tokens` key with its value in each file. */
17
+ export function copyTemplate(src, dest, tokens) {
18
+ cpSync(src, dest, { recursive: true });
19
+ for (const file of walk(dest)) {
20
+ const content = readFileSync(file, 'utf8');
21
+ const replaced = Object.entries(tokens).reduce((text, [token, value]) => text.replaceAll(token, value), content);
22
+ if (replaced !== content)
23
+ writeFileSync(file, replaced, 'utf8');
24
+ }
25
+ }
26
+ /** Sets `fields` on a JSON file and writes it back (2-space indented). */
27
+ export function updateJson(file, fields) {
28
+ const json = JSON.parse(readFileSync(file, 'utf8'));
29
+ writeFileSync(file, `${JSON.stringify({ ...json, ...fields }, null, 2)}\n`, 'utf8');
30
+ }
31
+ /**
32
+ * `mdk run` resolves components through the project's node_modules via a `file:`
33
+ * dependency (no `workspaces` key on the root manifest). Best effort: an unparseable
34
+ * project package.json must not undo the files already scaffolded, so a failure is
35
+ * returned as a warning instead of thrown. No-op outside an MDK project.
36
+ */
37
+ export function linkIntoProject(parentDir, packageName, packageDir) {
38
+ const stackName = readStackName(parentDir);
39
+ if (!stackName)
40
+ return undefined;
41
+ try {
42
+ ensureProjectManifest(parentDir, stackName);
43
+ addFileDependency(parentDir, packageName, packageDir);
44
+ return undefined;
45
+ }
46
+ catch (error) {
47
+ return `Could not link ${packageName} into package.json: ${errorText(error)}`;
48
+ }
49
+ }
50
+ /**
51
+ * Appends one entry named `name` to `spec.<list>` in `<parentDir>/mdk.yaml`, editing
52
+ * the parsed document so the file's formatting and comments survive. An entry
53
+ * `isSame` accepts makes a re-add a no-op (`'exists'`); any other entry with the
54
+ * same name is a conflict (`'name-taken'`). `build` gets the document so it can
55
+ * derive values from it.
56
+ */
57
+ export function appendToStackList(parentDir, list, name, isSame, build) {
58
+ const specPath = resolve(parentDir, 'mdk.yaml');
59
+ if (!existsSync(specPath))
60
+ return 'no-file';
61
+ try {
62
+ const doc = parseDocument(readFileSync(specPath, 'utf8'));
63
+ if (!isSeq(doc.getIn(['spec', list])))
64
+ doc.setIn(['spec', list], new YAMLSeq());
65
+ const items = doc.getIn(['spec', list]);
66
+ // Onboarding writes an empty list as `[]`, a flow sequence: anything appended
67
+ // to it would render inline, so switch it to block style first.
68
+ items.flow = false;
69
+ const entries = items.items.filter(isMap);
70
+ if (entries.some(isSame))
71
+ return 'exists';
72
+ if (entries.some((entry) => entry.get('name') === name))
73
+ return 'name-taken';
74
+ items.add(build(doc));
75
+ writeFileSync(specPath, doc.toString(), 'utf8');
76
+ return 'added';
77
+ }
78
+ catch {
79
+ return 'error';
80
+ }
81
+ }
82
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/lib/scaffold.ts"],"names":[],"mappings":"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","sourcesContent":["import { cpSync, existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { isMap, isSeq, parseDocument, YAMLSeq, type Document, type YAMLMap } from 'yaml';\nimport { addFileDependency, ensureProjectManifest, readStackName } from './project.js';\n\n/** Outcome of writing a component into `mdk.yaml`. */\n/**\n * Outcome of writing a component into `mdk.yaml`. `exists`: this package is\n * already listed (a re-add is a no-op). `name-taken`: another package holds the\n * name, so nothing was written.\n */\nexport type StackFileUpdate = 'added' | 'exists' | 'name-taken' | 'no-file' | 'error';\n\nconst errorText = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\nfunction walk(dir: string, out: string[] = []): string[] {\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry);\n if (statSync(full).isDirectory()) walk(full, out);\n else out.push(full);\n }\n return out;\n}\n\n/** Copies a bundled template to `dest`, replacing every `tokens` key with its value in each file. */\nexport function copyTemplate(src: string, dest: string, tokens: Record<string, string>): void {\n cpSync(src, dest, { recursive: true });\n for (const file of walk(dest)) {\n const content = readFileSync(file, 'utf8');\n const replaced = Object.entries(tokens).reduce((text, [token, value]) => text.replaceAll(token, value), content);\n if (replaced !== content) writeFileSync(file, replaced, 'utf8');\n }\n}\n\n/** Sets `fields` on a JSON file and writes it back (2-space indented). */\nexport function updateJson(file: string, fields: Record<string, unknown>): void {\n const json = JSON.parse(readFileSync(file, 'utf8')) as Record<string, unknown>;\n writeFileSync(file, `${JSON.stringify({ ...json, ...fields }, null, 2)}\\n`, 'utf8');\n}\n\n/**\n * `mdk run` resolves components through the project's node_modules via a `file:`\n * dependency (no `workspaces` key on the root manifest). Best effort: an unparseable\n * project package.json must not undo the files already scaffolded, so a failure is\n * returned as a warning instead of thrown. No-op outside an MDK project.\n */\nexport function linkIntoProject(parentDir: string, packageName: string, packageDir: string): string | undefined {\n const stackName = readStackName(parentDir);\n if (!stackName) return undefined;\n try {\n ensureProjectManifest(parentDir, stackName);\n addFileDependency(parentDir, packageName, packageDir);\n return undefined;\n } catch (error) {\n return `Could not link ${packageName} into package.json: ${errorText(error)}`;\n }\n}\n\n/**\n * Appends one entry named `name` to `spec.<list>` in `<parentDir>/mdk.yaml`, editing\n * the parsed document so the file's formatting and comments survive. An entry\n * `isSame` accepts makes a re-add a no-op (`'exists'`); any other entry with the\n * same name is a conflict (`'name-taken'`). `build` gets the document so it can\n * derive values from it.\n */\nexport function appendToStackList(\n parentDir: string,\n list: 'apps' | 'workers',\n name: string,\n isSame: (entry: YAMLMap) => boolean,\n build: (doc: Document) => unknown,\n): StackFileUpdate {\n const specPath = resolve(parentDir, 'mdk.yaml');\n if (!existsSync(specPath)) return 'no-file';\n\n try {\n const doc = parseDocument(readFileSync(specPath, 'utf8'));\n if (!isSeq(doc.getIn(['spec', list]))) doc.setIn(['spec', list], new YAMLSeq());\n const items = doc.getIn(['spec', list]) as YAMLSeq;\n // Onboarding writes an empty list as `[]`, a flow sequence: anything appended\n // to it would render inline, so switch it to block style first.\n items.flow = false;\n\n const entries = items.items.filter(isMap) as YAMLMap[];\n if (entries.some(isSame)) return 'exists';\n if (entries.some((entry) => entry.get('name') === name)) return 'name-taken';\n\n items.add(build(doc));\n writeFileSync(specPath, doc.toString(), 'utf8');\n return 'added';\n } catch {\n return 'error';\n }\n}\n"]}
@@ -0,0 +1,53 @@
1
+ import { theme } from './theme.js';
2
+ /** Exit status for a signal-initiated stop — 128 + SIGINT, the shell convention. */
3
+ const SIGNAL_EXIT = 130;
4
+ /** How long a graceful stop gets before the process is taken down anyway. */
5
+ const GRACE_MS = 5000;
6
+ /**
7
+ * Takes ownership of Ctrl+C for a foreground `mdk run`.
8
+ *
9
+ * Components are stopped in the order given (reverse boot order), but the
10
+ * process exits no matter what they do: a stop that throws is skipped, a stop
11
+ * that hangs is abandoned after `GRACE_MS`, and a second signal exits on the
12
+ * spot. That guarantee is the point — every port this process holds is released
13
+ * when the user asks it to stop, instead of a wedged shutdown leaving a listener
14
+ * bound to a port nobody can find.
15
+ */
16
+ export function installShutdown(components) {
17
+ let stopping = false;
18
+ const shutdown = (signal) => {
19
+ if (stopping) {
20
+ // Second Ctrl+C: the user is done waiting.
21
+ process.exit(SIGNAL_EXIT);
22
+ }
23
+ stopping = true;
24
+ process.stderr.write(`\n${theme.muted(`Stopping (${signal})…`)}\n`);
25
+ const gracefully = (async () => {
26
+ for (const component of components) {
27
+ try {
28
+ await component.stop();
29
+ }
30
+ catch (error) {
31
+ process.stderr.write(theme.muted(` ${component.label} did not stop cleanly: ` +
32
+ `${error instanceof Error ? error.message : String(error)}\n`));
33
+ }
34
+ }
35
+ })();
36
+ // `unref` so a finished shutdown is never held open by this timer.
37
+ const forced = new Promise((resolve) => {
38
+ setTimeout(() => resolve('timeout'), GRACE_MS).unref();
39
+ });
40
+ void Promise.race([gracefully, forced]).then((outcome) => {
41
+ if (outcome === 'timeout') {
42
+ process.stderr.write(theme.warn(`Shutdown timed out after ${GRACE_MS}ms — forcing exit.\n`));
43
+ }
44
+ else {
45
+ process.stderr.write(`${theme.muted('Stopped.')}\n`);
46
+ }
47
+ process.exit(SIGNAL_EXIT);
48
+ });
49
+ };
50
+ process.on('SIGINT', () => shutdown('SIGINT'));
51
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
52
+ }
53
+ //# sourceMappingURL=shutdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../src/lib/shutdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AASnC,oFAAoF;AACpF,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB,6EAA6E;AAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC;AAEtB;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,UAAuB;IACrD,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,QAAQ,GAAG,CAAC,MAAsB,EAAQ,EAAE;QAChD,IAAI,QAAQ,EAAE,CAAC;YACb,2CAA2C;YAC3C,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5B,CAAC;QACD,QAAQ,GAAG,IAAI,CAAC;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,aAAa,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAEpE,MAAM,UAAU,GAAG,CAAC,KAAK,IAAI,EAAE;YAC7B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;gBACnC,IAAI,CAAC;oBACH,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;gBACzB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,CAAC,KAAK,CACT,KAAK,SAAS,CAAC,KAAK,yBAAyB;wBAC3C,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAChE,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QAEL,mEAAmE;QACnE,MAAM,MAAM,GAAG,IAAI,OAAO,CAAY,CAAC,OAAO,EAAE,EAAE;YAChD,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACvD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,4BAA4B,QAAQ,sBAAsB,CAAC,CAAC,CAAC;YAC/F,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACvD,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AACnD,CAAC","sourcesContent":["import { theme } from './theme.js';\n\n/** A booted component the CLI must take down before it exits. */\nexport interface Stoppable {\n /** Shown while it is being stopped, e.g. `worker demo-miner`. */\n label: string;\n stop: () => unknown | Promise<unknown>;\n}\n\n/** Exit status for a signal-initiated stop — 128 + SIGINT, the shell convention. */\nconst SIGNAL_EXIT = 130;\n\n/** How long a graceful stop gets before the process is taken down anyway. */\nconst GRACE_MS = 5000;\n\n/**\n * Takes ownership of Ctrl+C for a foreground `mdk run`.\n *\n * Components are stopped in the order given (reverse boot order), but the\n * process exits no matter what they do: a stop that throws is skipped, a stop\n * that hangs is abandoned after `GRACE_MS`, and a second signal exits on the\n * spot. That guarantee is the point — every port this process holds is released\n * when the user asks it to stop, instead of a wedged shutdown leaving a listener\n * bound to a port nobody can find.\n */\nexport function installShutdown(components: Stoppable[]): void {\n let stopping = false;\n\n const shutdown = (signal: NodeJS.Signals): void => {\n if (stopping) {\n // Second Ctrl+C: the user is done waiting.\n process.exit(SIGNAL_EXIT);\n }\n stopping = true;\n process.stderr.write(`\\n${theme.muted(`Stopping (${signal})…`)}\\n`);\n\n const gracefully = (async () => {\n for (const component of components) {\n try {\n await component.stop();\n } catch (error) {\n process.stderr.write(\n theme.muted(\n ` ${component.label} did not stop cleanly: ` +\n `${error instanceof Error ? error.message : String(error)}\\n`,\n ),\n );\n }\n }\n })();\n\n // `unref` so a finished shutdown is never held open by this timer.\n const forced = new Promise<'timeout'>((resolve) => {\n setTimeout(() => resolve('timeout'), GRACE_MS).unref();\n });\n\n void Promise.race([gracefully, forced]).then((outcome) => {\n if (outcome === 'timeout') {\n process.stderr.write(theme.warn(`Shutdown timed out after ${GRACE_MS}ms — forcing exit.\\n`));\n } else {\n process.stderr.write(`${theme.muted('Stopped.')}\\n`);\n }\n process.exit(SIGNAL_EXIT);\n });\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n"]}
@@ -0,0 +1,38 @@
1
+ import { installSkills } from '@tetherto/mdk-skill';
2
+ /**
3
+ * Installs the MDK Developer Skill suite into the target project via the
4
+ * `@tetherto/mdk-skill` package's programmatic entry point. The package is a
5
+ * regular dependency (resolved through node/npm), so this works the same when
6
+ * the CLI is installed from npm and when it runs inside the monorepo workspace.
7
+ */
8
+ export function installSkill(client, targetDir) {
9
+ try {
10
+ // Skips are reported once, through `detail`, rather than via console.warn
11
+ // mid-spinner.
12
+ const result = installSkills({ client, target: targetDir, onWarn: () => { } });
13
+ const clients = result.installed.map((entry) => entry.client).join(', ');
14
+ // Existing dirs the suite does not own (symlinks, unmarked skills) are left
15
+ // alone; name them so the user knows which skills did not land. Stale suite
16
+ // skills swept from an earlier install are reported separately as info.
17
+ const skipped = result.skipped ?? [];
18
+ const removed = [...new Set(result.installed.flatMap((entry) => entry.removed ?? []))];
19
+ return {
20
+ ok: true,
21
+ message: `Installed ${result.skills.length} skill(s) for ${clients}.`,
22
+ ...(removed.length
23
+ ? { detail: `Removed ${removed.length} stale skill(s): ${removed.join(', ')}.` }
24
+ : {}),
25
+ ...(skipped.length
26
+ ? {
27
+ warning: `Skipped ${skipped.length} existing skill dir(s): ${skipped
28
+ .map((entry) => `${entry.dir} (${entry.reason})`)
29
+ .join('; ')}`,
30
+ }
31
+ : {}),
32
+ };
33
+ }
34
+ catch (error) {
35
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
36
+ }
37
+ }
38
+ //# sourceMappingURL=skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.js","sourceRoot":"","sources":["../../src/lib/skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAYpD;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,MAAmB,EAAE,SAAiB;IACjE,IAAI,CAAC;QACH,0EAA0E;QAC1E,eAAe;QACf,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;QAC9E,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzE,4EAA4E;QAC5E,4EAA4E;QAC5E,wEAAwE;QACxE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACvF,OAAO;YACL,EAAE,EAAE,IAAI;YACR,OAAO,EAAE,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,iBAAiB,OAAO,GAAG;YACrE,GAAG,CAAC,OAAO,CAAC,MAAM;gBAChB,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,OAAO,CAAC,MAAM,oBAAoB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;gBAChF,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,CAAC,MAAM;gBAChB,CAAC,CAAC;oBACE,OAAO,EAAE,WAAW,OAAO,CAAC,MAAM,2BAA2B,OAAO;yBACjE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC;yBAChD,IAAI,CAAC,IAAI,CAAC,EAAE;iBAChB;gBACH,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACxF,CAAC;AACH,CAAC","sourcesContent":["import { installSkills } from '@tetherto/mdk-skill';\n\nexport type SkillClient = 'cursor' | 'claude' | 'all';\n\nexport interface SkillResult {\n ok: boolean;\n message: string;\n detail?: string;\n // Set on success when some existing skill dirs were left untouched.\n warning?: string;\n}\n\n/**\n * Installs the MDK Developer Skill suite into the target project via the\n * `@tetherto/mdk-skill` package's programmatic entry point. The package is a\n * regular dependency (resolved through node/npm), so this works the same when\n * the CLI is installed from npm and when it runs inside the monorepo workspace.\n */\nexport function installSkill(client: SkillClient, targetDir: string): SkillResult {\n try {\n // Skips are reported once, through `detail`, rather than via console.warn\n // mid-spinner.\n const result = installSkills({ client, target: targetDir, onWarn: () => {} });\n const clients = result.installed.map((entry) => entry.client).join(', ');\n // Existing dirs the suite does not own (symlinks, unmarked skills) are left\n // alone; name them so the user knows which skills did not land. Stale suite\n // skills swept from an earlier install are reported separately as info.\n const skipped = result.skipped ?? [];\n const removed = [...new Set(result.installed.flatMap((entry) => entry.removed ?? []))];\n return {\n ok: true,\n message: `Installed ${result.skills.length} skill(s) for ${clients}.`,\n ...(removed.length\n ? { detail: `Removed ${removed.length} stale skill(s): ${removed.join(', ')}.` }\n : {}),\n ...(skipped.length\n ? {\n warning: `Skipped ${skipped.length} existing skill dir(s): ${skipped\n .map((entry) => `${entry.dir} (${entry.reason})`)\n .join('; ')}`,\n }\n : {}),\n };\n } catch (error) {\n return { ok: false, message: error instanceof Error ? error.message : String(error) };\n }\n}\n"]}