acrylblends 1.4.0 → 1.5.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.
- package/README.md +5 -2
- package/dist/index.d.ts +7 -4
- package/dist/index.js +191 -62
- package/dist/index.js.map +1 -1
- package/legacy/README.md +1 -1
- package/legacy/agent/46c6eadf9230/src/app.ts +293 -0
- package/legacy/agent/46c6eadf9230/src/routes.ts +163 -0
- package/legacy/blank/a53d41e6ef42/src/app.ts +160 -0
- package/legacy/blank/a53d41e6ef42/src/routes.ts +79 -0
- package/legacy/layouts.json +30 -0
- package/lib/{client-modules-Bkdr83jO.js → client-modules-C5qiVroz.js} +1 -1
- package/lib/cordis/door.js +1 -1
- package/lib/node-server/register.js +6 -1
- package/lib/node-server.js +1 -1
- package/lib/{transpile-QUK9b3eG.js → transpile-CRRaixyS.js} +19 -4
- package/package.json +1 -1
- package/plugins/acryl-door/src/cli/acryl.ts +6 -2
- package/plugins/acryl-node-server/src/register.ts +10 -1
- package/plugins/acryl-node-server/src/transpile.ts +25 -6
- package/plugins/acryl-page/src/favicon.ts +19 -0
- package/plugins/acryl-page/src/index.ts +18 -13
- package/plugins/agent-panel/client/main.ts +9 -0
- package/plugins/agent-panel/headless.ts +2 -2
- package/plugins/desktop/app-page.ts +151 -0
- package/plugins/desktop/assets/acrylblends-logo.png +0 -0
- package/plugins/desktop/brand.ts +164 -0
- package/plugins/desktop/frame.ts +88 -7
- package/plugins/desktop/git.ts +44 -0
- package/plugins/desktop/host-supervisor.ts +63 -0
- package/plugins/desktop/launch.ts +17 -1
- package/plugins/desktop/main.ts +268 -17
- package/plugins/desktop/preload.cjs +8 -0
- package/plugins/desktop/product.ts +117 -0
- package/plugins/desktop/window.ts +4 -0
- package/plugins/desktop-web/main.ts +3 -2
- package/plugins/desktop-web/plugin.json +1 -1
- package/plugins/extensions/index.ts +52 -7
- package/plugins/instance/archive.ts +395 -0
- package/plugins/instance/bundle-overlay.ts +10 -0
- package/plugins/instance/bundle.ts +201 -0
- package/plugins/instance/home.ts +395 -0
- package/plugins/instance/index.ts +132 -24
- package/plugins/instance/storage.ts +413 -0
- package/shared/skills/acryl-remove-plugin/SKILL.md +1 -1
- package/templates/agent/README.md +1 -1
- package/templates/agent/gitignore +2 -0
- package/templates/blank/README.md +1 -1
- package/templates/blank/gitignore +2 -0
- package/templates/blank/src/routes.ts +9 -5
- package/templates/chat/gitignore +2 -0
- package/templates/chat/src/chat-app.ts +7 -3
- package/templates/chat/src/llm.ts +7 -1
- package/templates/chat/test/chat.test.ts +16 -1
- package/templates/nodejs/gitignore +2 -0
- package/templates/notes/gitignore +2 -0
- package/templates/todo/README.md +1 -1
- package/templates/todo/gitignore +2 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["release.scope","release.dshVersion","release.packages","parse","eolOf","parse","parse","posix"],"sources":["../../../frame/packages.json","../src/ide-profile.ts","../src/version.ts","../src/frame-release.ts","../src/frame-rows.ts","../src/templates.ts","../src/lock.ts","../src/rewrite.ts","../src/library.ts","../src/project.ts","../src/init.ts","../src/mount.ts","../src/add.ts","../src/build-client.ts","../src/check-deps.ts","../src/frame-url.ts","../src/spawn.ts","../src/io.ts","../src/kv-import.ts","../src/preflight.ts","../src/app-registration.ts","../src/diff.ts","../src/legacy.ts","../src/upgrade.ts","../src/cli.ts"],"sourcesContent":["","// biome-ignore-all lint/suspicious/noTemplateCurlyInString: the launcher's source is written out as lines of text, and its template literals are meant\n/**\n * The host an app runs on inside the ACRYL frame: the app's own node server and loader composition, with DSH riding as the chat/session driver\n * (`docs/plans/ide-host-inversion.md`). The overlay (`cordis.patch.yml`) is the app's own layer: the frame's rows, the app's rows and the switches.\n * `scripts/ide-host.mjs` is the launcher: it composes the driver rows from DSH's base and web bundles with this app's layer and boots them on the\n * framework's node server. No DSH profile, no `dsh` launcher, no `dsh plugin add`. Generation is deterministic and idempotent: asking twice writes\n * the same bytes and the second call reports nothing changed, so a template or an upgrade can call it freely.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\n/** One package the frame mounts, with the spec the app installs it by (a range, or `file:` for a local checkout). */\nexport interface IdeProfilePackage {\n /** The package name as the overlay's row `name:` writes it. */\n readonly name: string\n /** What the app's `package.json` asks for. */\n readonly spec: string\n /** The Loader row id; defaults to the package name (the row-id rule). */\n readonly row?: string\n /** Leave this DSH row out of the composition (its functionality comes from the frame). */\n readonly disables?: readonly string[]\n /** A row of a package the app already installs (the framework's own subpaths): in the overlay, not in `dependencies`. */\n readonly installed?: boolean\n}\n\n/** Where an app keeps its data, for the plugins that inject `appInstance` (the framework's settings service does): the slice 0 row, kept in the app. */\nexport const APP_INSTANCE_ROW = `// Generated by acrylblends: the app's own folder family, for the plugins that inject appInstance.\nimport { join } from 'node:path'\n\nexport const name = 'app-instance'\n\nexport function apply(ctx) {\n ctx.effect(() => {\n const home = process.env.ACRYLBLENDS_HOME ?? join(process.cwd(), '.acrylblends')\n const instance = ctx.reflect.provide('appInstance', {\n root: process.cwd(),\n home,\n // The engine home ACRYL's plugins read (support serves \\`dshHome/logs\\`); in this host the engine and the app share one home.\n dshHome: home,\n })\n return () => {\n void instance()\n }\n }, 'app-instance')\n}\n`\n\n/** The packages the frame mounts, in row order: the router pair, the host services, the frame's client plugins. */\nexport const FRAME_PACKAGES: readonly IdeProfilePackage[] = [\n { name: 'acrylblends/cordis/router', spec: '', row: 'acryl-router', installed: true },\n { name: 'acrylblends/cordis/router-http', spec: '', row: 'acryl-router-http', installed: true },\n { name: 'acryl-web', spec: '' },\n { name: 'acryl-settings', spec: '' },\n // No `acryl-shortcuts`: its Settings > Shortcuts and the command palette's key are the IDE's; the frame lists the shortcuts in General.\n { name: 'acryl-app-shell', spec: '' },\n { name: 'acryl-versions', spec: '' },\n // The page's root: the Blends frame (docs/design/blends-frame-mockup.html), never the IDE's workbench (`acryl-workspace`).\n { name: 'acryl-blends-frame', spec: '' },\n { name: 'acryl-plugin-admin', spec: '' },\n // The agent's prompt as ACRYL IDE's agent has it: the identity line first, every section a tagged block (pi.dev's shape), empties dropped.\n { name: 'acryl-system-prompt', spec: '' },\n // The app builder's docs, the way ACRYL IDE's agent has its plugin docs: a routed index, verified example plugins and the prompt line that sends\n // the agent to them before it writes a plugin.\n { name: 'acryl-app-context', spec: '' },\n { name: 'acryl-support', spec: '' },\n { name: 'acryl-sign-in', spec: '' },\n {\n name: 'dsh-client-ui-brand-acryl',\n spec: '',\n row: 'ui-brand-acryl',\n disables: ['ui-brand-official'],\n },\n]\n\n/**\n * Where every template's frame row mounts the app (`FRAME_MOUNT_PREFIX` in the template's own source, `/app`). The overlay tells the frame's row, so\n * the frame shows the app's page in its centre; `init-frame` asserts the templates still agree with this constant.\n */\nexport const FRAME_PAGE_PREFIX = '/app'\n\nexport interface IdeProfileInput {\n /** The app's name: its folder name, and the profile's suffix. */\n readonly appName: string\n /** `@deepseek-ai/dsh` exactly, never a range: DSH's APIs are pre-stable and a bump is a decision. */\n readonly dshVersion: string\n /** The framework's range or exact version. */\n readonly frameworkVersion: string\n /** What the frame mounts. Defaults to {@link FRAME_PACKAGES} with `spec` filled from `versions` or `file:`. */\n readonly packages?: readonly IdeProfilePackage[]\n /** Spec for one of the default packages, when it is not published yet. */\n readonly spec?: (name: string) => string | undefined\n /** Further packages the app installs (an unpublished frame package's own dependencies, in a checkout). */\n readonly dependencies?: Readonly<Record<string, string>>\n /** The app's own rows in the overlay (the file that mounts its routes at the frame prefix), in order; none for an app without one. */\n readonly rows?: readonly {\n readonly id: string\n readonly name: string\n readonly isolate?: Readonly<Record<string, true>>\n }[]\n /** Whether the app carries the door at `plugins/acryl-door/src/index.ts`: a row then mounts it, and an app without one simply has no door. */\n readonly door?: boolean\n}\n\n/** What generation writes, as text, so the same plan can be compared before writing. */\nexport interface IdeProfilePlan {\n /** The host's name for this app (one per app; used in the launcher's identity and logs). */\n readonly profile: string\n /** The app's own layer, as committed `cordis.patch.yml`. */\n readonly overlay: string\n /** The app's `package.json`, merged with what the host and the driver rows need. */\n readonly packageJson: string\n /** `cordis.yml`: the empty root entry list the launcher's loader composition starts from. */\n readonly rootConfig: string\n /** `scripts/ide-host.mjs`: composes the driver rows with this app's layer and boots them on the framework's node server. */\n readonly hostScript: string\n}\n\n/**\n * The driver rows the chat and session need, with what they hard-inject, and nothing else of DSH's base or web bundles\n * (`docs/plans/ide-host-inversion.md`, section 4). The composition check proves each one activates; a missing one is a loud failure, never a silent skip.\n */\nexport const DRIVER_ROWS: readonly string[] = [\n 'connection',\n 'typert',\n 'typert-loader',\n 'typert-gateway',\n 'session',\n 'session-persistence-jsonl',\n 'session-query-sqlite',\n 'session-projection',\n 'session-title',\n 'storage',\n 'storage-json',\n 'storage-domain',\n 'agent',\n 'agent-loop',\n 'agent-default-model',\n 'llm',\n 'llm-retry',\n 'llm-deepseek',\n 'llm-deepseek-account',\n // The multi-provider twin, mounted dormant: its OAuth flows (Claude, ChatGPT and the rest of the catalog) are what the\n // Models page's \"Sign in with an account\" rows list, and a sign-in there supplies its provider profiles - the workshop\n // guide tells attendees a subscription works, so the catalog has to be composed, not only the DeepSeek account.\n 'llm-pi-ai',\n 'deepseek-account',\n 'credentials',\n 'authorization',\n 'settings',\n 'config-editor',\n 'attachment-local',\n 'file-reference-local',\n 'session-controller',\n 'settings-controller',\n 'api-remotes',\n 'workspace',\n 'workspace-controller',\n 'tools',\n 'system-prompt',\n // The skills registry the preset's `skill-filesystem` fills from the app's `.agents/skills/` and its `tool-skill` reads (DSH keeps it on the host plane).\n 'skill',\n 'file-upload',\n 'fs-sandbox',\n 'sandbox-policy',\n 'commands',\n // What the agent's tools stand on: the file observation rule, the sandbox and approval policy, the shell and its environment, and background jobs. The\n // tools themselves are the frame's own agent preset below, not host rows.\n 'fs-observation-policy',\n 'sandbox',\n 'bash-sandbox',\n 'pwsh-sandbox',\n 'approval',\n 'permission',\n 'shell-env',\n 'jobs',\n // The chat's auto-save: `workspace-changes` diffs the working tree around every turn and says which files the agent changed (and announces a turn that\n // changed none by saying nothing); it runs git through `subprocess`. The frame's Host saves a version from that, and DSH's changed-files card\n // (`ui-deliverables`) shows the same event under the answer.\n 'subprocess',\n 'workspace-changes',\n // What ACRYL IDE's chat stands on beyond the conversation itself, from DSH's own web composition (the desktop app composes the same): the turn and\n // step stats under the composer (`session-stats`, `session-turn-outline`), Like and Dislike on an answer (`message-feedback`), the background jobs\n // roster (`job-controller`), the session header's \"Open in\" button (`open-in-app`), `/export` (`session-log-download`), session references in `@`\n // (`session-reference`) and the workspace file reads the changed-files card uses (`workspace-files`).\n 'message-feedback',\n 'session-stats',\n 'session-turn-outline',\n 'job-controller',\n 'workspace-files',\n 'open-in-app',\n 'session-log-download',\n 'session-reference',\n // The footer's tokens, cache hit and context % (`ui-chat`'s stats read the `tokenUsage` projection this meter keeps, on the host plane as DSH's web\n // bundle keeps it), and the questions a plan or a tool asks the person (`tool-ask-user` and `ui-user-questions` both stand on this service).\n 'token-meter',\n 'user-questions',\n]\n\n/**\n * The chat's agent preset. DSH's web bundle gives every session a preset (`standard`: every tool DSH has, subagents, workflows, skills and plan mode, each\n * needing host rows of its own), and a session without a loadable preset cannot start. The frame offers the agent what builds an app, and nothing more:\n * the shell, the file tools (read, write, edit, search) and background jobs, with the persona and the app folder's instructions. It is the registry's\n * default, so every chat in the frame uses it. A tool added here needs the host rows it stands on in `DRIVER_ROWS`.\n */\nexport const AGENT_PRESET_ID = 'acryl-blends'\n\n/**\n * The plugins the frame's preset takes from DSH's own `standard` preset, as DSH declares them (config and prompt included), so they follow every DSH\n * bump instead of being restated here: the app's skills (`skill-filesystem` finds `.agents/skills/` in the app folder, the skills `acrylblends` writes\n * there with every app, and `tool-skill` lets the agent load one), plan mode and its `/plan` (`planning`, which ui-plan's plan card and Plan tab show),\n * the To-dos list the composer's dock shows (`tool-todo`), the questions the agent can ask (`tool-ask-user`), and context compaction (`compaction`),\n * whose meter is the footer's context %.\n */\nexport const DSH_STANDARD_PLUGINS: readonly string[] = [\n 'skill-filesystem',\n 'tool-skill',\n 'planning',\n 'compaction',\n 'tool-ask-user',\n 'tool-todo',\n]\n\nexport const AGENT_PRESET_ROWS: readonly {\n readonly id: string\n readonly name: string\n readonly config: Readonly<Record<string, unknown>>\n}[] = [\n {\n id: 'agent-preset-registry',\n name: '@deepseek-ai/dsh-agent-preset-registry',\n config: { default: AGENT_PRESET_ID },\n },\n {\n id: `preset-${AGENT_PRESET_ID}`,\n name: '@deepseek-ai/dsh-agent-preset',\n config: {\n id: AGENT_PRESET_ID,\n order: 1,\n plugins: [\n {\n id: 'persona',\n name: '@deepseek-ai/dsh-persona',\n config: {\n prefix: 'You are a coding agent powered by the {{model}} model.',\n suffix: 'Your working directory is {{cwd}}.',\n },\n },\n {\n id: 'agent-instructions',\n name: '@deepseek-ai/dsh-agent-instructions',\n config: { maxBytes: 65536 },\n },\n // The loader evaluates `__jsExpr` (the `!!js` tag) when it composes the row.\n {\n id: 'tool-bash',\n name: '@deepseek-ai/dsh-tool-bash',\n disabled: { __jsExpr: \"process.platform === 'win32'\" },\n },\n {\n id: 'tool-pwsh',\n name: '@deepseek-ai/dsh-tool-pwsh',\n disabled: { __jsExpr: \"process.platform !== 'win32'\" },\n },\n { id: 'tool-fs', name: '@deepseek-ai/dsh-tool-fs' },\n {\n id: 'tool-fs-search',\n name: '@deepseek-ai/dsh-tool-fs-search',\n config: { sampleOverCapGlobResults: false },\n },\n { id: 'tool-jobs', name: '@deepseek-ai/dsh-tool-jobs' },\n // DSH_STANDARD_PLUGINS are appended here at boot, read from DSH's own `standard` preset (see the host script).\n ],\n },\n },\n]\n\n/**\n * The client rows the frame and the chat load: the client-module serving row plus exactly the client packages the frame's and the chat's\n * `dsh.client` inject lists name, as installed in the app, and the two services those rows wait for (`client-resources`, `ui-input-trigger`).\n * DSH's `ui-layout` client row is deliberately absent: the ACRYL frame registers the `layout` service itself (DSH's row would register it twice,\n * the failure the old overlay avoided by disabling the row); the only layout client row mounted is `acryl-app-shell`, so the document renders\n * the ACRYL frame.\n */\n/** One DSH client row of the frame's profile: a package, its Loader id, and the config it mounts with. */\nexport interface ClientRow {\n readonly id: string\n readonly name: string\n readonly config?: Readonly<Record<string, unknown>>\n}\n\nexport const CLIENT_ROWS: readonly ClientRow[] = [\n { id: 'client-modules', name: '@deepseek-ai/dsh-client-modules' },\n { id: 'client-locale', name: '@deepseek-ai/dsh-client-locale' },\n { id: 'client-shortcuts', name: '@deepseek-ai/dsh-client-shortcuts' },\n { id: 'ui-renderer', name: '@deepseek-ai/dsh-client-ui-renderer' },\n // Dark by default, as the Blends frame is drawn (docs/design/blends-frame-mockup.html); a Settings Appearance change still wins over it.\n { id: 'ui-theme', name: '@deepseek-ai/dsh-client-ui-theme', config: { preference: 'dark' } },\n { id: 'ui-settings', name: '@deepseek-ai/dsh-client-ui-settings' },\n // The settings surface itself (the dialog, its sections list and the trigger). Without this row the page has no Settings trigger or dialog at all:\n // ui-settings and ui-settings-models only contribute pages into it.\n { id: 'ui-settings-general', name: '@deepseek-ai/dsh-client-ui-settings-general' },\n // No `ui-settings-plugins` (DSH's \"Built-in plugins\"): the frame's Settings > Plugins lists everything the app is made of.\n { id: 'ui-settings-models', name: '@deepseek-ai/dsh-client-ui-settings-models' },\n { id: 'ui-session', name: '@deepseek-ai/dsh-client-ui-session' },\n // No `ui-sidebar`: the Blends frame draws the left panel itself (branches and versions), and DSH's left sidebar also claims the\n // `sidebar.settings` seat the frame's top bar renders DSH's Settings in. DSH's right sidebar is mounted as it is (`ui-sidebar-right`, the docking\n // surface the Plan, the changed-files review and a file an answer links to open in); the frame shows it as one of its right panel's tabs.\n { id: 'ui-sidebar-right', name: '@deepseek-ai/dsh-client-ui-sidebar-right' },\n // The right sidebar's document tab: a file an answer names opens in it (Markdown, code, HTML, PDF, plain text).\n { id: 'ui-sidebar-documentpreview', name: '@deepseek-ai/dsh-client-ui-sidebar-documentpreview' },\n { id: 'ui-commands', name: '@deepseek-ai/dsh-client-ui-commands' },\n { id: 'ui-workspace', name: '@deepseek-ai/dsh-client-ui-workspace' },\n { id: 'ui-conversation', name: '@deepseek-ai/dsh-client-ui-conversation' },\n // What the chat view draws: the transcript (`ui-chat`, which opens an answer's links through the `sidebarRight` the frame stands in for), the tool calls\n // inside it, the approval card of a tool that asks, and the files attached to a message. Without them a conversation shows the person's message and no answer.\n { id: 'ui-chat', name: '@deepseek-ai/dsh-client-ui-chat' },\n { id: 'ui-tool', name: '@deepseek-ai/dsh-client-ui-tool' },\n { id: 'ui-approval', name: '@deepseek-ai/dsh-client-ui-approval' },\n { id: 'ui-attachment', name: '@deepseek-ai/dsh-client-ui-attachment' },\n // The rest of ACRYL IDE's chat, the same DSH packages in the order DSH's web composition mounts them: the shortcuts surface, workflow runs, `/` skills,\n // subagents, `@` references, the jobs roster, the goal bar, Like and Dislike, the model and effort selector, the permission picker (Read Only, Workspace\n // Write, Full access), the agent-preset setting, the session log setting, user questions, the Trajectory tab, and the session header's \"Open in\".\n // The changed-files card under an answer and its review tab (`ui-deliverables`), and plan mode's composer control, plan card and Plan tab (`ui-plan`).\n { id: 'ui-shortcuts', name: '@deepseek-ai/dsh-client-ui-shortcuts' },\n { id: 'ui-workflow-run', name: '@deepseek-ai/dsh-client-ui-workflow-run' },\n { id: 'ui-skill', name: '@deepseek-ai/dsh-client-ui-skill' },\n { id: 'ui-subagent', name: '@deepseek-ai/dsh-client-ui-subagent' },\n { id: 'ui-reference', name: '@deepseek-ai/dsh-client-ui-reference' },\n { id: 'ui-jobs', name: '@deepseek-ai/dsh-client-ui-jobs' },\n { id: 'ui-goal', name: '@deepseek-ai/dsh-client-ui-goal' },\n { id: 'ui-message-feedback', name: '@deepseek-ai/dsh-client-ui-message-feedback' },\n { id: 'ui-model-selection', name: '@deepseek-ai/dsh-client-ui-model-selection' },\n { id: 'ui-permission', name: '@deepseek-ai/dsh-client-ui-permission-presets' },\n { id: 'ui-agent-preset', name: '@deepseek-ai/dsh-client-ui-agent-preset' },\n { id: 'ui-settings-session-log', name: '@deepseek-ai/dsh-client-ui-settings-session-log' },\n { id: 'ui-user-questions', name: '@deepseek-ai/dsh-client-ui-user-questions' },\n { id: 'ui-trajectory', name: '@deepseek-ai/dsh-client-ui-trajectory' },\n { id: 'ui-deliverables', name: '@deepseek-ai/dsh-client-ui-deliverables' },\n { id: 'ui-plan', name: '@deepseek-ai/dsh-client-ui-plan' },\n { id: 'ui-open-in-app', name: '@deepseek-ai/dsh-client-ui-open-in-app' },\n { id: 'client-resources', name: '@deepseek-ai/dsh-client-resources' },\n { id: 'ui-input-trigger', name: '@deepseek-ai/dsh-client-ui-input-trigger' },\n]\n\n/** The array literal a host script writes for its rows (`const clientRows = [...];`, JSON at two spaces). */\nconst CLIENT_ROWS_BLOCK = /const clientRows = (\\[[\\s\\S]*?\\n\\])/u\n\n/** The client rows a host script names, or undefined when its array cannot be read. */\nexport function parseHostClientRows(hostText: string): ClientRow[] | undefined {\n const match = CLIENT_ROWS_BLOCK.exec(hostText)\n if (match?.[1] === undefined) return undefined\n try {\n const parsed: unknown = JSON.parse(match[1])\n if (!Array.isArray(parsed)) return undefined\n return parsed.filter(\n (row): row is ClientRow =>\n row !== null &&\n typeof row === 'object' &&\n typeof (row as { id?: unknown }).id === 'string' &&\n typeof (row as { name?: unknown }).name === 'string',\n )\n } catch {\n return undefined\n }\n}\n\n/**\n * Add the client rows the current host script names and the app's host script does not, keeping every row the app has exactly as it is: a person's\n * own row, or one whose config they changed. The app's rows keep their order; the missing current rows follow; the rest of the host script is the\n * current one. An app without a host script (a new app) or with an unreadable array gets the current script as it is.\n * @param currentHost - the host script the CLI generates now.\n * @param appHost - the app's existing host script, when it has one.\n * @returns the text to write and the row names it added.\n */\nexport function mergeHostClientRows(\n currentHost: string,\n appHost: string | undefined,\n): { readonly text: string; readonly added: readonly string[] } {\n if (appHost === undefined) return { text: currentHost, added: [] }\n const current = parseHostClientRows(currentHost)\n const app = parseHostClientRows(appHost)\n if (current === undefined || app === undefined) return { text: currentHost, added: [] }\n const names = new Set(app.map((row) => row.name))\n const missing = current.filter((row) => !names.has(row.name))\n const block = CLIENT_ROWS_BLOCK.exec(currentHost)\n if (block === null) return { text: currentHost, added: [] }\n const merged = [...app, ...missing]\n return {\n text: currentHost.replace(block[0], `const clientRows = ${JSON.stringify(merged, null, 2)}`),\n added: missing.map((row) => row.name),\n }\n}\n\n/**\n * `scripts/ide-host.mjs`: the app's launcher. It boots the framework's node server and the webServer port the driver rows register on, then the frame\n * layer, and mounts DSH's driver rows from the base and web bundles (only the ids in `DRIVER_ROWS`) as ordinary rows of this composition. `--check`\n * audits the composition (every row active) and disposes it, for the gate.\n */\nfunction hostScriptOf(profile: string, frameModule: string): string {\n return [\n \"// Generated by acrylblends: boots this app inside the ACRYL frame on the app's own node server. Regenerate instead of editing.\",\n \"import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\",\n \"import { chmodSync } from 'node:fs'\",\n \"import { spawnSync } from 'node:child_process'\",\n \"import { randomBytes, timingSafeEqual } from 'node:crypto'\",\n \"import { createRequire } from 'node:module'\",\n \"import { dirname, isAbsolute, join, resolve } from 'node:path'\",\n \"import { fileURLToPath, pathToFileURL } from 'node:url'\",\n \"import { DEFAULT_SCHEMA, dump as dumpYaml, Type, load as parseYaml } from 'js-yaml'\",\n \"import { Acryl } from 'acrylblends'\",\n '',\n \"const appRoot = join(dirname(fileURLToPath(import.meta.url)), '..')\",\n `const profileName = '${profile}'`,\n \"const home = process.env.ACRYLBLENDS_HOME ?? join(appRoot, '.acrylblends')\",\n \"// DSH's rows keep their files in this app's own home: set before any driver row is imported.\",\n 'process.env.DSH_HOME = home',\n \"// Development only: the app's code can be loaded again in this host (a version gone back to, a file written by the chat, an agent or an editor). Every file\",\n \"// of the app's src/ then resolves under a generation the frame's Host raises, so nothing but the app's own rows restarts. Before anything of the app loads;\",\n '// a frame package that predates the hook leaves the code as it started.',\n 'try {',\n ` const { installAppGeneration } = await import('${frameModule}/app-generation')`,\n ' installAppGeneration({ appRoot })',\n '} catch (error) {',\n \" if (error?.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED' && error?.code !== 'ERR_MODULE_NOT_FOUND') throw error\",\n '}',\n '// Before any DSH module is imported: its home resolution reads the environment at import time.',\n \"const { boot } = await import('@deepseek-ai/dsh-app-boot')\",\n \"// The frame listens where the app's own server would: the folder's stable port (or the home's, when ACRYLBLENDS_HOME pins one). A restart then\",\n \"// keeps the page's origin, its auth cookie and its tabs; ACRYLBLENDS_PORT still decides, and the gates pass 0 for any free port.\",\n \"const { selectInstance } = await import(new URL('../plugins/instance/index.ts', import.meta.url).href)\",\n 'const port = String(selectInstance({ projectRoot: appRoot, env: process.env }).port.start)',\n \"const check = process.argv.includes('--check')\",\n 'const require = createRequire(import.meta.url)',\n '/** The `!!js` tag the loader evaluates: js-yaml needs the same type the Cordis include plugin declares. */',\n \"const JsExpr = new Type('tag:yaml.org,2002:js', {\",\n \" kind: 'scalar',\",\n ' construct: (data) => ({ __jsExpr: data }),',\n ' represent: (data) => data.__jsExpr,',\n '})',\n 'const load = (text) => parseYaml(text, { schema: DEFAULT_SCHEMA.extend([JsExpr]) })',\n '',\n '/** An insert names its module beside the patch file that named it (the loader rule, and the rule DSH applies when it reads a profile again). The',\n ' * composition is read as the profile bundle, from its own directory, so a row written relative would turn into a bundle-relative path the first time',\n ' * DSH reconciles the profile (any Settings write) and the app rows would stop loading. Anchoring each layer at the directory it was written for',\n ' * keeps one row meaning one file: boot, a Settings write and the frame reload then import the same absolute URL. */',\n 'function anchorRows(patches, baseDir) {',\n ' const visit = (row) => {',\n \" if (typeof row.name === 'string' && (isAbsolute(row.name) || row.name.startsWith('./') || row.name.startsWith('../')))\",\n ' row.name = pathToFileURL(resolve(baseDir, row.name)).href',\n ' if (row.group && Array.isArray(row.config)) row.config.forEach(visit)',\n ' }',\n ' for (const patch of patches) patch.insert?.forEach(visit)',\n ' return patches',\n '}',\n '',\n '/** The driver rows above are selected from the bundles by id; nothing else of DSH is mounted. */',\n `const DRIVER_ROWS = ${JSON.stringify(DRIVER_ROWS, null, 2).replaceAll('\\n', '\\n')}`,\n '',\n \"/** A bundle's patch files, in application order, as the package declares them, anchored beside that package (a relative name in a bundle is its own file). */\",\n 'function bundlePatches(packageName) {',\n \" const dir = dirname(require.resolve(packageName + '/package.json'))\",\n \" const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'))\",\n ' const files = manifest.dsh?.bundle?.patch',\n \" const list = typeof files === 'string' ? [files] : Array.isArray(files) ? files : []\",\n \" return anchorRows(list.flatMap((file) => load(readFileSync(join(dir, file), 'utf8')) ?? []), dir)\",\n '}',\n '',\n '/** Keep only the driver rows: insert lists filtered entry by entry, id-targeted patches only when they hit a driver row. */',\n 'function onlyDriverRows(patches) {',\n ' const kept = []',\n ' for (const patch of patches) {',\n ' if (Array.isArray(patch.insert)) {',\n ' const insert = patch.insert.filter((entry) => DRIVER_ROWS.includes(entry.id))',\n ' if (insert.length > 0) kept.push({ ...patch, insert })',\n \" } else if (typeof patch.id === 'string' && DRIVER_ROWS.includes(patch.id)) kept.push(patch)\",\n ' }',\n ' return kept',\n '}',\n '',\n \"const frameLayer = anchorRows(load(readFileSync(join(appRoot, 'cordis.patch.yml'), 'utf8')) ?? [], appRoot)\",\n \"/** Our host rows first: the framework's node server, then the webServer port the driver rows register on (they are the app's, not DSH's). */\",\n `const clientRows = ${JSON.stringify(CLIENT_ROWS, null, 2).replaceAll('\\\\n', '\\\\n')}`,\n `const agentRows = ${JSON.stringify(AGENT_PRESET_ROWS, null, 2)}`,\n `const DSH_STANDARD_PLUGINS = ${JSON.stringify(DSH_STANDARD_PLUGINS)}`,\n \"/** One plugin row of DSH's `standard` preset, exactly as DSH's web bundle declares it; a missing one fails the boot loudly. */\",\n 'function dshStandardPlugin(id) {',\n \" for (const patch of bundlePatches('@deepseek-ai/dsh-web-app'))\",\n ' for (const entry of patch.insert ?? [])',\n \" if (entry.config?.id === 'standard') {\",\n ' const found = entry.config.plugins?.find((plugin) => plugin.id === id)',\n ' if (found !== undefined) return found',\n ' }',\n \" throw new Error(`DSH's standard preset has no ${id} row`)\",\n '}',\n `agentRows.find((row) => row.id === 'preset-${AGENT_PRESET_ID}').config.plugins.push(...DSH_STANDARD_PLUGINS.map(dshStandardPlugin))`,\n 'const hostLayer = anchorRows([{',\n ' insert: [',\n \" { id: 'node-server', name: './plugins/acryl-node-server/src/index.ts', config: { hostname: '127.0.0.1', port: Number(port), scan: Number(port) === 0 ? 0 : 20 } },\",\n \" { id: 'web-server', name: './plugins/acryl-web-server/src/index.ts' },\",\n \" { id: 'page', name: './plugins/acryl-page/src/index.ts' },\",\n ' ...clientRows,',\n ' ...agentRows,',\n ' ],',\n '}], appRoot)',\n \"// The composition: the driver rows selected from the bundles, the host rows and the app's frame layer, in the order they mount.\",\n 'const composition = [',\n \" ...onlyDriverRows(bundlePatches('@deepseek-ai/dsh-base')),\",\n \" ...onlyDriverRows(bundlePatches('@deepseek-ai/dsh-web-app')),\",\n ' ...hostLayer,',\n ' ...frameLayer,',\n ']',\n '',\n \"// The app's DSH profile, which DSH's Settings, config editor and plugin manager read and write (docs/plans/ide-host-inversion.md, section 20):\",\n '// - package.json: the profile manifest (no DSH bundles; the composition is ours), created once when the app has none.',\n '// - cordis.patch.yml: the composition, regenerated on every boot from the layers above, so an upgrade or a framework bump never leaves a stale copy.',\n '// - settings.patch.yml: the profile patch document ProfileContext.patchPath points at. Only the config editor writes it; the host creates it empty',\n '// when missing and never rewrites it, so the Settings edits of the app survive a boot, an upgrade and a framework bump.',\n 'mkdirSync(home, { recursive: true })',\n \"const manifestPath = join(home, 'package.json')\",\n '// The composition is a profile bundle: bundle layers are applied before the profile patch document, so a Settings edit always wins over a row the',\n '// composition also configures. DSH resolves it from the profile directory like any installed bundle, and the launcher rewrites it on every boot.',\n \"const bundleDir = join(home, 'node_modules', 'acrylblends-frame')\",\n 'mkdirSync(bundleDir, { recursive: true })',\n \"writeFileSync(join(bundleDir, 'package.json'), `${JSON.stringify({ name: 'acrylblends-frame', version: '0.0.0', private: true, dsh: { bundle: { patch: 'cordis.patch.yml' } } }, null, 2)}\\\\n`)\",\n 'const compositionHeader =',\n ' \"# Generated by acrylblends: the host composition of this app, a profile bundle. Rewritten on every boot from the launcher layers; do not edit. Settings live in settings.patch.yml.\\\\n\"',\n \"writeFileSync(join(bundleDir, 'cordis.patch.yml'), compositionHeader + dumpYaml(composition, { schema: DEFAULT_SCHEMA.extend([JsExpr]), noRefs: true }))\",\n \"writeFileSync(manifestPath, `${JSON.stringify({ name: profileName, private: true, dependencies: { 'acrylblends-frame': '0.0.0' }, dsh: { profile: { bundles: ['acrylblends-frame'] } } }, null, 2)}\\\\n`)\",\n \"const settingsPath = join(home, 'settings.patch.yml')\",\n \"if (!existsSync(settingsPath)) writeFileSync(settingsPath, '[]\\\\n')\",\n '',\n '// Boot reads the same list readProfilePatches will: the composition bundle first, then the settings document, which is also the order DSH uses for',\n '// bundles and the profile patch, so the first reconcile is a no-op recomposition and every later one differs only by the edited patch.',\n \"const compositionLayer = load(readFileSync(join(bundleDir, 'cordis.patch.yml'), 'utf8')) ?? []\",\n \"const settingsLayer = anchorRows(load(readFileSync(settingsPath, 'utf8')) ?? [], home)\",\n \"/** The profile context DSH's settings row, config editor and plugin manager read. */\",\n 'const profileContext = {',\n \" name: 'web',\",\n ' dir: home,',\n ' patchPath: settingsPath,',\n \" installAnchor: join(appRoot, 'package.json'),\",\n ' cwd: appRoot,',\n ' home,',\n ' startedBundles: [],',\n ' overlays: [],',\n ' telemetryDisabledEnv: process.env.DSH_TELEMETRY_DISABLED,',\n '}',\n 'const layers = [compositionLayer, settingsLayer]',\n 'let ctx',\n 'try {',\n \" ctx = await boot('\" +\n profile +\n \"', join(appRoot, 'cordis.yml'), layers.flat(), (hostContext) => {\",\n \" hostContext.provide('profileContext', profileContext)\",\n \" }, pathToFileURL(join(appRoot, 'package.json')).href)\",\n '} catch (error) {',\n \" // A row that fails before it has a plugin (a module that is not one, an import that throws) is reported as 'failed to import' with no cause; the boot's\",\n ' // StartupError carries the original failures. Print each one, by row id and module, then fail: a silent half-composed host is never an option.',\n ' const entries = Array.isArray(error?.entries) ? error.entries : []',\n ' const failures = entries.filter(({ outcome }) => outcome?.kind === \"failed\")',\n ' console.error(`acrylblends host: startup failed (${String(failures.length)} row(s))`)',\n ' for (const { entry, outcome } of failures) {',\n ' const cause = outcome.error',\n ' const detail = cause instanceof Error ? cause.stack ?? cause.message : String(cause)',\n ' console.error(`- ${entry.id} (${entry.options?.name ?? \"?\"}): ${detail}`)',\n ' }',\n ' const cause = error?.cause',\n ' if (failures.length === 0 && cause !== undefined) console.error(cause instanceof Error ? cause.stack ?? cause.message : String(cause))',\n ' process.exit(1)',\n '}',\n \"const server = ctx.get('nodeServer')\",\n \"const connection = ctx.get('connection')\",\n '// The frame listens on a stable port; its launch token is made stable the same way: the address of the previous run is read back and the connection',\n '// is given that token, so a restart keeps the same URL and an open page - even one whose cookie was lost - comes back by reloading it. The record is',\n '// the launcher own bookkeeping (the installed framework may predate `acrylblends open`), so it is read and written here: owner-only (0600, and a',\n '// restricted ACL on Windows), a temp file renamed over the old one. The token lives in the connection instance field DSH reads on every check;',\n '// nothing in the pinned package is edited, and a connection without that field keeps the token the process minted.',\n \"const frameRecordPath = join(home, 'frame-url.json')\",\n 'function readFrameRecord() {',\n ' try {',\n ' return JSON.parse(readFileSync(frameRecordPath, \"utf8\"))',\n ' } catch {',\n ' return undefined',\n ' }',\n '}',\n 'function restrictFrameRecord(path) {',\n ' chmodSync(path, 0o600)',\n ' if (process.platform !== \"win32\" || !process.env.USERNAME) return',\n ' spawnSync(\"icacls\", [path, \"/inheritance:r\", \"/grant:r\", `${process.env.USERNAME}:F`], { stdio: \"ignore\", windowsHide: true })',\n '}',\n 'function writeFrameRecord(url) {',\n ' let token = \"\"',\n ' try {',\n ' token = new URL(url).searchParams.get(\"token\") ?? \"\"',\n ' } catch {}',\n ' mkdirSync(home, { recursive: true })',\n ' const temporary = `${frameRecordPath}.${String(process.pid)}.tmp`',\n ' writeFileSync(temporary, JSON.stringify({ version: 1, url, token, pid: process.pid, writtenAt: new Date().toISOString() }, null, 2) + \"\\\\n\", { mode: 0o600 })',\n ' restrictFrameRecord(temporary)',\n ' renameSync(temporary, frameRecordPath)',\n ' restrictFrameRecord(frameRecordPath)',\n '}',\n 'const connectionAuth = connection !== undefined && typeof connection === \"object\" ? connection.browserAuth : undefined',\n 'const previousAddress = readFrameRecord()',\n 'const reusedToken = connectionAuth !== undefined && typeof connectionAuth.launchToken === \"string\" && previousAddress !== undefined && typeof previousAddress.token === \"string\" && previousAddress.token !== \"\"',\n 'if (reusedToken) connectionAuth.launchToken = previousAddress.token',\n \"// The chat's remote RPC is token-authenticated: the printed URL carries the launch token, and the page exchange turns it into a cookie.\",\n 'const url = server === undefined ? undefined : connection === undefined ? server.url : connection.authenticatedUrl(server.url)',\n \"console.log(`acrylblends host: ${url ?? 'no node server'}`)\",\n '// `acrylblends open` prints the record back; a stopped host leaves it behind on purpose, so the command can say the frame is not running instead of',\n '// pretending the app never ran one.',\n 'if (url !== undefined) {',\n ' try {',\n ' writeFrameRecord(url)',\n ' } catch (error) {',\n ' console.error(`acrylblends host: could not write the frame address: ${String(error)}`)',\n ' }',\n ' // `acrylblends open --new` rotates the token in the running host: the stored token presents itself, the connection gets a new one, the new address is',\n ' // written and answered; every other request is refused.',\n ' const frameRouter = ctx.get(\"router\")',\n ' const frameRouterHttp = ctx.get(\"routerHttp\")',\n ' if (frameRouter !== undefined && frameRouterHttp !== undefined && connectionAuth !== undefined && typeof connectionAuth.launchToken === \"string\") {',\n ' const sameToken = (actual, expected) => {',\n ' const a = Buffer.from(String(actual), \"utf8\")',\n ' const b = Buffer.from(String(expected), \"utf8\")',\n ' return a.byteLength === b.byteLength && timingSafeEqual(a, b)',\n ' }',\n ' const rotate = () => {',\n ' connectionAuth.launchToken = randomBytes(32).toString(\"base64url\")',\n ' const next = connection.authenticatedUrl(server.url)',\n ' writeFrameRecord(next)',\n ' return next',\n ' }',\n ' frameRouter.route(\"/api/acryl-frame\", new Acryl().post(\"/token/new\", (c) => {',\n ' const presented = c.req.header(\"x-frame-token\") ?? \"\"',\n ' if (!sameToken(presented, connectionAuth.launchToken)) return c.text(\"forbidden\", 403)',\n ' return c.json({ url: rotate() })',\n ' }))',\n ' frameRouterHttp.mount(\"/api/acryl-frame\")',\n ' }',\n '}',\n 'if (previousAddress !== undefined && !reusedToken && url !== undefined) {',\n ' console.warn(\"acrylblends host: the stored frame address could not be reused on this boot; a new one was minted\")',\n '}',\n 'if (check) {',\n \" const loader = ctx.get('loader')\",\n ' const rows = [...loader.entries()]',\n ' const stalled = []',\n ' for (const row of rows) {',\n ' const state = row.fiber?.state',\n ' if (row.disabled) continue',\n \" if (state !== 2 && state !== 4) stalled.push(`${row.id} (${row.options.name}): ${state === undefined ? 'no fiber' : String(state)}`)\",\n ' }',\n ' const webRows = rows.filter((row) => /(^|\\\\/)(host-webserver|web-app|web-frontend)$/u.test(row.options.name))',\n ' if (stalled.length > 0 || webRows.length > 0) {',\n \" console.error('acrylblends host: composition check failed')\",\n ' for (const line of stalled) console.error(`- not active: ${line}`)',\n ' for (const row of webRows) console.error(`- a DSH web row is mounted: ${row.id} (${row.options.name})`)',\n ' process.exitCode = 1',\n ' } else {',\n ' console.log(`acrylblends host: every row active (${String(rows.length)} rows), no DSH web row mounted`)',\n ' }',\n ' // The Cordis way: a provider removed puts its consumer PENDING, restoring it activates it again; a reload adds nothing twice.',\n ' // The rows live under the include entry (the launcher mounts one config file), so an id is found by its last segment.',\n ' const findRow = (id) => rows.find((row) => row.id === id || row.id.endsWith(`:${id}`))',\n ' const stateOf = (id) => findRow(id)?.fiber?.state',\n \" await loader.update(findRow('credentials').id, { disabled: true })\",\n ' await loader.await()',\n \" const pendingAfterRemoval = stateOf('connection') === 0\",\n \" await loader.update(findRow('credentials').id, { disabled: false })\",\n ' await loader.await()',\n \" const activeAfterRestore = stateOf('connection') === 2\",\n ' for (let round = 0; round < 2; round += 1) {',\n \" await loader.update(findRow('session-title').id, { disabled: true })\",\n \" await loader.update(findRow('session-title').id, { disabled: false })\",\n ' await loader.await()',\n ' }',\n \" const reloaded = stateOf('session-title') === 2\",\n ' if (!pendingAfterRemoval || !activeAfterRestore || !reloaded) {',\n ' console.error(`acrylblends host: lifecycle check failed (pending ${String(pendingAfterRemoval)}, restored ${String(activeAfterRestore)}, reloaded ${String(reloaded)})`)',\n ' process.exitCode = 1',\n ' } else {',\n \" console.log('acrylblends host: PENDING on provider removal, active on restore, two reloads clean')\",\n ' }',\n ' await ctx.fiber.dispose()',\n '} else {',\n \" for (const signal of ['SIGINT', 'SIGTERM']) process.once(signal, () => { void ctx.fiber.dispose() })\",\n '}',\n '',\n ].join('\\n')\n}\n\n/** The row that roots the page (the Blends frame) and the module it is installed as: its source name in a checkout, its published name from npm. */\nexport const BLENDS_FRAME_ROW = 'acryl-blends-frame'\n\nfunction frameModuleOf(packages: readonly IdeProfilePackage[]): string {\n return (\n packages.find((entry) => (entry.row ?? entry.name) === BLENDS_FRAME_ROW)?.name ??\n BLENDS_FRAME_ROW\n )\n}\n\n/** `acrylblends-<app>`, safe for a DSH profile name: lowercase, dashes, nothing else. */\nexport function profileNameFor(appName: string): string {\n const slug = appName\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/gu, '-')\n .replaceAll(/^-+|-+$/gu, '')\n return `acrylblends-${slug === '' ? 'app' : slug}`\n}\n\n/** A package name as a YAML scalar: a scoped name starts with `@`, which YAML reserves at the start of a scalar, so it is quoted. */\nconst yamlName = (name: string): string => (name.startsWith('@') ? `'${name}'` : name)\n\nfunction overlayOf(\n packages: readonly IdeProfilePackage[],\n door = false,\n appRows: readonly {\n readonly id: string\n readonly name: string\n /** The Loader entry option: services this row's whole subtree provides in its own realm instead of the host's. */\n readonly isolate?: Readonly<Record<string, true>>\n }[] = [],\n): string {\n const rows = packages\n const disabled = new Set(packages.flatMap((entry) => entry.disables ?? []))\n const lines = [\n '# Generated by acrylblends: the DSH overlay that mounts the ACRYL frame. Regenerate instead of editing.',\n '- insert:',\n ' - id: app-instance',\n ' name: ./app/runtime.mjs',\n // The frame's index is not the app: the door drives the app's page in the canvas, never the frame around it (an agent's navigate would leave the frame).\n // The canvas prefix goes with it: `act navigate --path /board` reaches the app's /board at the canvas prefix, the same path it has on its own server.\n ...(door\n ? [\n ' - id: acryl-door',\n ' name: ./plugins/acryl-door/src/index.ts',\n ' config:',\n ' hostIndex: false',\n ` appBase: ${FRAME_PAGE_PREFIX}`,\n ]\n : []),\n ...appRows.flatMap((row) => [\n ` - id: ${row.id}`,\n ` name: ${row.name}`,\n ...(row.isolate === undefined ? [] : [` isolate: ${JSON.stringify(row.isolate)}`]),\n ]),\n ]\n for (const entry of rows) {\n lines.push(` - id: ${entry.row ?? entry.name}`, ` name: ${yamlName(entry.name)}`)\n // The app's page, which the frame shows in its centre.\n if ((entry.row ?? entry.name) === BLENDS_FRAME_ROW)\n lines.push(' config:', ` appPage: ${FRAME_PAGE_PREFIX}`)\n }\n // The chat driver's connection registers node routes on the framework's webServer port; it needs no DSH web runtime, so the row's\n // webRuntime inject and its expression are replaced by the one fact it needs here: which hosts the remote may trust.\n lines.push('- id: connection', ' inject: []', ' config:', \" trustedHosts: ['127.0.0.1']\")\n // The chat's first workspace lives in the app's own data home, not the user's Documents: the workspace controller creates the\n // default workspace under this directory, so a first chat is inside the app folder (which the workspace registers as its first\n // project) and a test run never writes into a real home.\n lines.push(\n '- id: workspace-controller',\n ' config:',\n \" documentsDirectory: !!js process.env.ACRYLBLENDS_HOME ?? process.cwd() + '/.acrylblends'\",\n )\n for (const id of [...disabled].sort()) {\n lines.push(`- id: ${id}`, ' disabled: true')\n }\n return `${lines.join('\\n')}\\n`\n}\n\n/**\n * The files a fresh app's profile needs, without touching the disk: the overlay text and the app's `package.json` with the DSH pin, the framework, the\n * frame packages and a `dev` script on the app's own profile. An existing `package.json` keeps its keys; only the generated fields are set.\n */\nexport function ideProfilePlan(input: IdeProfileInput, existing?: string): IdeProfilePlan {\n const profile = profileNameFor(input.appName)\n const packages = (input.packages ?? FRAME_PACKAGES).map((entry) => ({\n ...entry,\n spec: entry.spec === '' ? (input.spec?.(entry.row ?? entry.name) ?? '') : entry.spec,\n }))\n const dependencies: Record<string, string> = {\n // DSH rides as the chat/session driver: the two bundles it is selected from, its boot glue and the generic Cordis loader; pinned exactly.\n // One Cordis for the whole composition: DSH 0.2.0-rc.2's own rows run on 4.0.5, and the framework's plugins accept it (^4.0.2).\n '@deepseek-ai/cordis': '4.0.5',\n '@deepseek-ai/cordis-plugin-loader': '~1.0.6',\n '@deepseek-ai/dsh-app-boot': input.dshVersion,\n '@deepseek-ai/dsh-base': input.dshVersion,\n '@deepseek-ai/dsh-web-app': input.dshVersion,\n acrylblends: input.frameworkVersion,\n 'js-yaml': '^4.2.0',\n ...input.dependencies,\n }\n for (const entry of packages)\n if (entry.installed !== true && entry.spec !== '') dependencies[entry.name] = entry.spec\n const manifest =\n existing === undefined\n ? { name: input.appName, private: true, type: 'module' }\n : JSON.parse(existing)\n return {\n profile,\n overlay: overlayOf(packages, input.door === true, input.rows ?? []),\n packageJson: `${JSON.stringify(mergeManifest(manifest, dependencies), null, 2)}\\n`,\n rootConfig: '[]\\n',\n hostScript: hostScriptOf(profile, frameModuleOf(packages)),\n }\n}\n\n/** The app's manifest with the generated fields set: dependencies merged (generated wins), the dev script on its own profile, keys in a stable order. */\nfunction mergeManifest(\n manifest: Record<string, unknown>,\n dependencies: Record<string, string>,\n): Record<string, unknown> {\n const sorted = Object.fromEntries(\n Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)),\n )\n // `dev` boots the launcher; `start` stays the app's own server (production).\n const scripts = {\n ...(manifest.scripts as Record<string, string> | undefined),\n dev: 'node --import ./plugins/acryl-node-server/src/register.ts scripts/ide-host.mjs',\n }\n return {\n ...manifest,\n dependencies: { ...(manifest.dependencies as Record<string, string> | undefined), ...sorted },\n scripts,\n }\n}\n\n/** What `applyIdeProfile` did: the files it wrote, and the ones already right. */\nexport interface IdeProfileWrite {\n readonly written: readonly string[]\n readonly unchanged: readonly string[]\n}\n\n/**\n * The first line of every file this module generates. `acrylblends upgrade` uses it to tell a file it may rewrite from one the app wrote (a hand-made\n * `cordis.patch.yml` for DSH, say): a file without the marker is refused, never overwritten.\n */\nexport const GENERATED_MARKER = /^(?:#|\\/\\/) Generated by acrylblends:/u\n\n/** The generated files, in the order this module writes them, with the plan field each one's text comes from. */\nconst GENERATED_FILES: readonly {\n readonly file: string\n readonly of: (plan: IdeProfilePlan) => string\n}[] = [\n { file: 'cordis.patch.yml', of: (plan) => plan.overlay },\n { file: 'cordis.yml', of: (plan) => plan.rootConfig },\n { file: 'scripts/ide-host.mjs', of: (plan) => plan.hostScript },\n { file: 'app/runtime.mjs', of: () => APP_INSTANCE_ROW },\n]\n\n/** What writing the profile into an existing app would do to one generated file. `conflict`: the file exists and acrylblends did not write it. */\nexport interface IdeProfileFileWrite {\n readonly file: string\n readonly text: string\n readonly state: 'add' | 'same' | 'update' | 'conflict'\n}\n\n/** The write plan for an app that already exists, computed before anything is touched so a refusal leaves the folder exactly as it was. */\nexport interface IdeProfileWritePlan {\n readonly plan: IdeProfilePlan\n /** The generated files, with what writing would do to each. */\n readonly files: readonly IdeProfileFileWrite[]\n /** Whether `package.json` changes once merged (a merge, never a conflict: the app owns that file). */\n readonly manifestChanged: boolean\n /** Client rows the app's host script did not name yet, which the write adds. */\n readonly clientRowsAdded: readonly string[]\n}\n\n/** The door is app source, not a registry package: the row exists when the app carries it, and nothing fails when it does not. */\nfunction withDoor(root: string, input: IdeProfileInput): IdeProfileInput {\n return { ...input, door: existsSync(join(root, 'plugins', 'acryl-door', 'src', 'index.ts')) }\n}\n\n/**\n * Plan writing the profile into `root`, which may already hold an app: a file this module generated (its first line carries the marker) is an `update`, a\n * file the app wrote is a `conflict` and must be refused by the caller, a missing file is an `add`. The bytes are exactly what `applyIdeProfile` writes.\n */\nexport function planIdeProfileWrite(root: string, input: IdeProfileInput): IdeProfileWritePlan {\n const manifestFile = join(root, 'package.json')\n const existing = existsSync(manifestFile) ? readFileSync(manifestFile, 'utf8') : undefined\n const plan = ideProfilePlan(withDoor(root, input), existing)\n const hostPath = join(root, 'scripts/ide-host.mjs')\n const appHost = existsSync(hostPath) ? readFileSync(hostPath, 'utf8') : undefined\n let clientRowsAdded: readonly string[] = []\n const files = GENERATED_FILES.map(({ file, of }) => {\n let text = of(plan)\n if (file === 'scripts/ide-host.mjs') {\n // The launcher updates as a whole; its rows are a merge, so a person's own row or a row they changed survives and only missing rows are added.\n const merged = mergeHostClientRows(text, appHost)\n text = merged.text\n clientRowsAdded = merged.added\n }\n const path = join(root, file)\n if (!existsSync(path)) return { file, text, state: 'add' as const }\n const before = readFileSync(path, 'utf8')\n if (before === text) return { file, text, state: 'same' as const }\n const firstLine = before.split('\\n', 1)[0] ?? ''\n return {\n file,\n text,\n state: GENERATED_MARKER.test(firstLine) ? ('update' as const) : ('conflict' as const),\n }\n })\n return { plan, files, manifestChanged: existing !== plan.packageJson, clientRowsAdded }\n}\n\n/** Write a plan made by {@link planIdeProfileWrite}: only the files whose bytes differ, and `package.json` only when the merge changes it. */\nexport function writeIdeProfilePlan(root: string, planned: IdeProfileWritePlan): IdeProfileWrite {\n const written: string[] = []\n const unchanged: string[] = []\n for (const file of planned.files) {\n if (file.state === 'same') {\n unchanged.push(file.file)\n continue\n }\n const path = join(root, file.file)\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, file.text)\n written.push(file.file)\n }\n if (planned.manifestChanged) {\n writeFileSync(join(root, 'package.json'), planned.plan.packageJson)\n written.push('package.json')\n } else unchanged.push('package.json')\n return { written, unchanged }\n}\n\n/**\n * Write the profile into `root`: `cordis.patch.yml` and the app's `package.json`. Idempotent: a file whose bytes are already the generated ones is not\n * touched, so a second run reports it under `unchanged` and the git working tree stays clean.\n */\nexport function applyIdeProfile(root: string, input: IdeProfileInput): IdeProfileWrite {\n mkdirSync(root, { recursive: true })\n return writeIdeProfilePlan(root, planIdeProfileWrite(root, input))\n}\n","import { createRequire } from 'node:module'\n\n/** The version of this package. */\nexport const VERSION: string = (\n createRequire(import.meta.url)('../package.json') as { version: string }\n).version\n\n/** Semver order for the release versions (numeric segments; a pre-release sorts before its release). */\nexport function compareVersions(a: string, b: string): number {\n const pa = a.split(/[.-]/u).map((part) => (Number.isNaN(Number(part)) ? part : Number(part)))\n const pb = b.split(/[.-]/u).map((part) => (Number.isNaN(Number(part)) ? part : Number(part)))\n for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {\n const x = pa[i] ?? 0\n const y = pb[i] ?? 0\n if (x < y) return -1\n if (x > y) return 1\n }\n return 0\n}\n","/**\n * Where an app's frame packages come from. npm by default: `frame/packages.json` (bundled into the CLI at build) names each frame package's published\n * name and version and the DSH release they are built against, and `init --desktop` installs those. `ACRYLBLENDS_FRAME_PACKAGES` overrides them: a JSON\n * set of specs for a checkout's unpublished build, or `none` for an app on the framework alone. An app that already carries frame packages upgrades from\n * its own pins.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport release from '../../../frame/packages.json' with { type: 'json' }\nimport { FRAME_PACKAGES, type IdeProfileInput, type IdeProfilePackage } from './ide-profile'\nimport { VERSION } from './version'\n\n/** One frame package as it publishes: its folder under `ide/` (a row package's folder is its source name), its published name, its version. */\nexport interface FrameReleasePackage {\n readonly source: string\n readonly name: string\n readonly version: string\n}\n\n/** The npm scope every frame package publishes under. */\nexport const FRAME_SCOPE: string = release.scope\n\n/** The DSH release the frame's packages pin as peers, so an app's DSH pin is the same one. */\nexport const FRAME_DSH_VERSION: string = release.dshVersion\n\n/** Every package the frame publishes, in the file's order. */\nexport const FRAME_RELEASE: readonly FrameReleasePackage[] = Object.entries(release.packages).map(\n ([source, entry]) => ({ source, name: `${release.scope}/${entry.name}`, version: entry.version }),\n)\n\n/**\n * The specs an app's frame installs: the DSH pin, the framework, and a spec per frame package keyed by its source name. `names` maps a source name to\n * the module it installs as when that differs (a published package); without an entry the module is the source name (a checkout's `file:` spec).\n */\nexport interface FrameProfileSpecs {\n readonly dshVersion: string\n readonly frameworkVersion: string\n readonly packages: Readonly<Record<string, string>>\n readonly names?: Readonly<Record<string, string>>\n}\n\n/** `ACRYLBLENDS_FRAME_PACKAGES=none`: the app runs on the framework alone, whatever the default would be. */\nexport const FRAMEWORK_ONLY = 'framework-only'\n\n/**\n * The frame's specs from npm: every row package by its published name at a caret over its released version, the DSH pin the packages are built against,\n * and this CLI's own release as the framework. A row package the file does not name is an error (the CLI tests catch it), never a silent skip.\n */\nexport function publishedFrameProfile(): FrameProfileSpecs {\n const bySource = new Map(FRAME_RELEASE.map((entry) => [entry.source, entry]))\n const packages: Record<string, string> = {}\n const names: Record<string, string> = {}\n for (const entry of FRAME_PACKAGES) {\n if (entry.installed === true) continue\n const published = bySource.get(entry.name)\n if (published === undefined)\n throw new Error(`frame/packages.json does not name the frame row ${entry.name}`)\n packages[entry.name] = `^${published.version}`\n names[entry.name] = published.name\n }\n return { dshVersion: FRAME_DSH_VERSION, frameworkVersion: `^${VERSION}`, packages, names }\n}\n\n/** `ACRYLBLENDS_FRAME_PACKAGES`: specs (a checkout's build), {@link FRAMEWORK_ONLY} for `none`, undefined when unset or malformed. */\nexport function frameProfileFromEnv(): FrameProfileSpecs | typeof FRAMEWORK_ONLY | undefined {\n const raw = process.env.ACRYLBLENDS_FRAME_PACKAGES\n if (raw === undefined || raw.trim() === '') return undefined\n if (raw.trim() === 'none') return FRAMEWORK_ONLY\n try {\n const parsed = JSON.parse(raw) as {\n dshVersion?: unknown\n frameworkVersion?: unknown\n packages?: unknown\n }\n if (typeof parsed.dshVersion !== 'string' || typeof parsed.frameworkVersion !== 'string')\n return undefined\n const packages: Record<string, string> = {}\n for (const [key, value] of Object.entries((parsed.packages ?? {}) as Record<string, unknown>))\n if (typeof value === 'string') packages[key] = value\n return { dshVersion: parsed.dshVersion, frameworkVersion: parsed.frameworkVersion, packages }\n } catch {\n return undefined\n }\n}\n\n/** The specs a new app's frame installs: the environment's, else npm's for a desktop app; undefined leaves the app on the framework alone. */\nexport function frameProfileForInit(desktop: boolean): FrameProfileSpecs | undefined {\n const fromEnv = frameProfileFromEnv()\n if (fromEnv === FRAMEWORK_ONLY) return undefined\n return fromEnv ?? (desktop ? publishedFrameProfile() : undefined)\n}\n\n/**\n * The specs for an app that may or may not be framed already: the environment first, then the app's own `package.json` (a framed app carries the DSH\n * pin, the framework and the frame packages, by their source or published names), then npm's for a desktop app (one with `plugins/desktop`).\n * Undefined when none applies: the caller then leaves the app on the framework alone and says why.\n */\nexport function frameProfileForApp(root: string): FrameProfileSpecs | undefined {\n const fromEnv = frameProfileFromEnv()\n if (fromEnv === FRAMEWORK_ONLY) return undefined\n if (fromEnv !== undefined) return fromEnv\n const fromApp = frameProfileOfManifest(root)\n if (fromApp !== undefined) return fromApp\n return existsSync(join(root, 'plugins', 'desktop')) ? publishedFrameProfile() : undefined\n}\n\nfunction frameProfileOfManifest(root: string): FrameProfileSpecs | undefined {\n const manifestFile = join(root, 'package.json')\n if (!existsSync(manifestFile)) return undefined\n let manifest: { dependencies?: Record<string, unknown> }\n try {\n manifest = JSON.parse(readFileSync(manifestFile, 'utf8')) as typeof manifest\n } catch {\n return undefined\n }\n const dependencies = manifest.dependencies ?? {}\n const dshVersion = dependencies['@deepseek-ai/dsh-base']\n const frameworkVersion = dependencies.acrylblends\n if (typeof dshVersion !== 'string' || typeof frameworkVersion !== 'string') return undefined\n const publishedOf = new Map(FRAME_RELEASE.map((entry) => [entry.source, entry.name]))\n const packages: Record<string, string> = {}\n const names: Record<string, string> = {}\n for (const entry of FRAME_PACKAGES) {\n const source = dependencies[entry.name]\n const publishedName = publishedOf.get(entry.name)\n const published = publishedName === undefined ? undefined : dependencies[publishedName]\n if (typeof source === 'string') packages[entry.name] = source\n else if (typeof published === 'string' && publishedName !== undefined) {\n packages[entry.name] = published\n names[entry.name] = publishedName\n }\n }\n if (Object.keys(packages).length === 0) return undefined\n return { dshVersion, frameworkVersion, packages, names }\n}\n\n/**\n * The profile input for an app from its specs: every frame row named by the module it installs as, its Loader row id kept as the source name (so a checkout\n * app and a published one have the same rows, and an upgrade between them renames nothing), and the specs as dependencies under the same module names.\n */\nexport function frameProfileInput(\n specs: FrameProfileSpecs,\n appName: string,\n rows: IdeProfileInput['rows'],\n): IdeProfileInput {\n const moduleOf = (source: string) => specs.names?.[source] ?? source\n const packages: IdeProfilePackage[] = FRAME_PACKAGES.map((entry) =>\n entry.installed === true\n ? entry\n : {\n ...entry,\n name: moduleOf(entry.name),\n row: entry.row ?? entry.name,\n spec: specs.packages[entry.name] ?? '',\n },\n )\n return {\n appName,\n dshVersion: specs.dshVersion,\n frameworkVersion: specs.frameworkVersion,\n packages,\n dependencies: Object.fromEntries(\n Object.entries(specs.packages).map(([source, spec]) => [moduleOf(source), spec]),\n ),\n ...(rows === undefined ? {} : { rows }),\n }\n}\n","/**\n * The rows the frame's layer needs for an app's own code, and the generated composition row that runs the app's plugins beside the host.\n *\n * A template with `src/frame.ts` gets the app's routes row. A template whose `src/app.ts` exports `composeInto` also gets `frame/app-plugins.mjs`, one row\n * that composes the app exactly as its own server does (same rows, order and config; `host: true` leaves the host's infrastructure rows out). The services\n * the app's own rows provide are read from their sources, never a hand list, and isolated through the Loader's own entry option, so the host's DSH rows keep\n * their copies of the same names.\n *\n * `init` writes these for a fresh app and `upgrade` writes them for an app made before the frame: one home for the row ids, the isolate rule and the\n * generated file.\n */\nimport { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { FRAME_PAGE_PREFIX } from './ide-profile'\n\n/** Rows the host mounts itself: an app's manifest may name them, but the generated layer does not. */\nexport const HOST_PROVIDED_IDS: ReadonlySet<string> = new Set([\n 'instance',\n 'acryl-router',\n 'acryl-node-server',\n 'acryl-web-server',\n 'acryl-page',\n 'manifest',\n 'save',\n 'acryl-door',\n])\n\n/**\n * Services the app's rows provide that the host's own rows call: the door and the frame reach `pluginShare`, and no host row provides that name. Left out of\n * the isolate list, so they find it; every other service stays in the app's realm.\n */\nconst REACHED_FROM_HOST: ReadonlySet<string> = new Set(['pluginShare'])\n\n/** One row of the generated layer. `isolate` is the Loader's entry option: the services this row's whole subtree provides in its own realm. */\nexport interface FrameRow {\n readonly id: string\n readonly name: string\n readonly isolate?: Readonly<Record<string, true>>\n}\n\n/** The generated `frame/app-plugins.mjs`, by the row that mounts it. */\nexport const APP_PLUGINS_FILE = 'frame/app-plugins.mjs'\n\n/**\n * The generated row source: the app's plugins composed beside the host, with the services they provide isolated (the Loader entry option). Every\n * route they mount lands under the frame's app prefix (`prefix`): a plugin's `ctx.router.route('/board')` answers `/app/board` in the canvas and\n * `/board` on the app's own server, so a plugin never writes the prefix. `@param prefix` is the canvas prefix, {@link FRAME_PAGE_PREFIX}.\n */\nexport function appPluginsRow(prefix: string): string {\n return [\n \"// Generated by acrylblends: the app's own plugins, composed as the app's server composes them (src/app.ts composeInto). The layer row isolates the\",\n \"// services this app provides (the Loader entry option), so the host's driver rows keep their own. Every route these rows mount lands under the\",\n `// frame's app prefix (${prefix}): a plugin's ctx.router.route('/board') answers /${prefix.replace(/^\\/+/u, '')}/board in the canvas and /board on the app's own server.`,\n \"import { composeInto } from '../src/app.ts'\",\n '',\n \"export const name = 'app-plugins'\",\n \"export const inject = ['appInstance', 'router']\",\n '',\n '/** The path a route lands at under the prefix: \"/\" is the prefix itself, a path already under it stays as it is. */',\n 'function prefixedAppRoute(prefix, path) {',\n \" const base = prefix.replace(/\\\\/+$/, '')\",\n \" if (base === '') return path\",\n \" if (path === '' || path === '/') return base\",\n \" return path === base || path.startsWith(base + '/') ? path : base + (path.startsWith('/') ? '' : '/') + path\",\n '}',\n '',\n \"/** The router as the app's rows see it: mounts land under the prefix, and a plugin's own request (its install check) finds them there; every other member is the host router's. */\",\n 'function appRouter(router, prefix) {',\n ' const route = (path, app) => router.route(prefixedAppRoute(prefix, path), app)',\n \" const request = (input, ...rest) => router.request(typeof input === 'string' && input.startsWith('/') ? prefixedAppRoute(prefix, input) : input, ...rest)\",\n ' return new Proxy(router, {',\n ' get(target, property) {',\n \" if (property === 'route') return route\",\n \" if (property === 'request') return request\",\n ' const value = Reflect.get(target, property, target)',\n \" return typeof value === 'function' ? value.bind(target) : value\",\n ' },',\n ' })',\n '}',\n '',\n 'export async function apply(ctx, config) {',\n ` const prefix = typeof config?.appPrefix === 'string' ? config.appPrefix : ${JSON.stringify(prefix)}`,\n \" // The rows read ctx.router as the app's own router: the same paths as on their own server, under the frame's prefix. A getter, read by each plugin\",\n ' // from its own context: it gets the host router traced for that context, so a route it mounts is its own and goes when the plugin is removed',\n \" // (a router taken once here would make every route this row's, served until the whole app reloads).\",\n \" const appScope = ctx.extend({ get router() { return appRouter(this.get('router'), prefix) } })\",\n ' const fibers = await composeInto(appScope, ctx.appInstance, { host: true })',\n \" ctx.effect(() => () => { for (const fiber of [...fibers].reverse()) void fiber.dispose() }, 'app-plugins: dispose')\",\n '}',\n '',\n ].join('\\n')\n}\n\n/** Whether an app's composition root is the one `composeInto` makes: the row can call it only then. */\nexport function carriesComposeInto(appTs: string): boolean {\n return appTs.includes('export async function composeInto')\n}\n\n/** The ids of the app's own rows (blend.yaml), without the ones the host provides. */\nfunction ownRowIds(appDir: string): string[] {\n const blend = join(appDir, 'blend.yaml')\n if (!existsSync(blend)) return []\n const ids: string[] = []\n for (const match of readFileSync(blend, 'utf8').matchAll(/^\\s*- id:\\s*(\\S+)\\s*$/gmu))\n if (!HOST_PROVIDED_IDS.has(match[1] as string)) ids.push(match[1] as string)\n return ids\n}\n\n/** The service names the app's own plugins provide, read from their sources. */\nfunction providedServices(appDir: string): Record<string, true> {\n const provided = new Set<string>()\n const walk = (folder: string): void => {\n if (!existsSync(folder)) return\n for (const entry of readdirSync(folder, { withFileTypes: true })) {\n const path = join(folder, entry.name)\n if (entry.isDirectory()) {\n walk(path)\n continue\n }\n if (!/\\.(ts|tsx|mjs)$/u.test(entry.name)) continue\n const text = readFileSync(path, 'utf8')\n for (const match of text.matchAll(/super\\(\\s*ctx\\s*,\\s*['\"]([\\w.-]+)['\"]/gu))\n provided.add(match[1] as string)\n for (const match of text.matchAll(/reflect\\.provide\\(\\s*['\"]([\\w.-]+)['\"]/gu))\n provided.add(match[1] as string)\n for (const match of text.matchAll(/ctx\\.provide\\(\\s*['\"]([\\w.-]+)['\"]/gu))\n provided.add(match[1] as string)\n }\n }\n for (const row of ownRowIds(appDir)) walk(join(appDir, 'plugins', row))\n return Object.fromEntries(\n [...provided]\n .filter((name) => !REACHED_FROM_HOST.has(name))\n .sort()\n .map((name) => [name, true as const]),\n )\n}\n\nexport interface AppFrameRows {\n /** The layer rows, in order: the app's plugins first, then its routes. Empty for an app with no `src/frame.ts`. */\n readonly rows: readonly FrameRow[]\n /** The generated row file's text, when the app's composition exports `composeInto`; the caller writes it. */\n readonly appPlugins: string | undefined\n}\n\n/**\n * The frame rows for an app: its routes row when it carries `src/frame.ts`, and the composition row when its `src/app.ts` exports `composeInto`.\n * `appTs` and `hasFrame` let a caller plan against the files it is about to write (an upgrade replacing the scaffold) rather than the ones on disk.\n */\nexport function appFrameRows(\n appDir: string,\n options: { appTs?: string; hasFrame?: boolean } = {},\n): AppFrameRows {\n if (options.hasFrame !== true && !existsSync(join(appDir, 'src', 'frame.ts')))\n return { rows: [], appPlugins: undefined }\n const appTs = options.appTs ?? readAppTs(appDir)\n const composes = appTs !== undefined && carriesComposeInto(appTs)\n const rows: FrameRow[] = []\n if (composes) {\n const isolate = providedServices(appDir)\n rows.push({\n id: 'app-plugins',\n name: `./${APP_PLUGINS_FILE}`,\n ...(Object.keys(isolate).length === 0 ? {} : { isolate }),\n })\n }\n rows.push({ id: 'app-routes', name: './src/frame.ts' })\n return { rows, appPlugins: composes ? appPluginsRow(FRAME_PAGE_PREFIX) : undefined }\n}\n\nfunction readAppTs(appDir: string): string | undefined {\n const file = join(appDir, 'src', 'app.ts')\n return existsSync(file) ? readFileSync(file, 'utf8') : undefined\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** The templates, all Cordis apps: a web app with a page and a client component (nodejs), a notes app and a chat app on the db port (notes, chat), a blank app on the router, a todo list on the router and state, and the blank app with the offline agent. */\nexport const TEMPLATES = ['blank', 'nodejs', 'notes', 'chat', 'todo', 'agent'] as const\nexport type Template = (typeof TEMPLATES)[number]\n\n/** One line per template: the single list `acrylblends init` and `create-acrylblends` both show (`create-acrylblends` tests that its own copy equals this one). */\nexport const TEMPLATE_SUMMARY: Record<(typeof TEMPLATES)[number], string> = {\n blank: 'the router, a server and one route: the smallest app',\n nodejs: 'a web app with a page and a client component',\n notes: 'a multi-user notes app on the db port (sign-up, log-in, sessions)',\n chat: 'a chat app that streams an answer over server-sent events',\n todo: 'a to-do list on the router and state',\n agent: 'the blank app with an offline agent that builds plugins into the running app',\n}\n\n/** Names that were templates once and still work: `create-acrylblends --template acryl-app` made the agent app. */\nexport const TEMPLATE_ALIASES: Record<string, Template> = { 'acryl-app': 'agent' }\n\nexport function isTemplate(value: string): value is Template {\n return (TEMPLATES as readonly string[]).includes(value)\n}\n\n/** The template a name means: itself, or what an old name stands for; undefined for anything else. */\nexport function resolveTemplate(value: string): Template | undefined {\n return isTemplate(value) ? value : TEMPLATE_ALIASES[value]\n}\n\n/**\n * The `templates/` folder of this package. The module sits at different depths in source (`src/`) and in the build\n * (`dist/`), so walk up until a `templates` folder holds the `blank` template.\n */\nexport function templatesRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'templates')\n if (existsSync(join(candidate, 'blank'))) return candidate\n const parent = dirname(dir)\n if (parent === dir) throw new Error('acrylblends cannot find its templates folder')\n dir = parent\n }\n}\n\n/** Absolute path of a template folder. */\nexport function templateDir(template: Template): string {\n return join(templatesRoot(), template)\n}\n\n/** The text every generated app's `AGENTS.md` starts with (`shared/`, beside `templates/`). */\nexport function sharedDir(): string {\n return join(dirname(templatesRoot()), 'shared')\n}\n\n/**\n * The framework's docs, as ACRYL wrote them: `docs/framework` of the repository, or the copy `prepack` puts inside the package. The module sits at\n * different depths in source and in the build, so walk up until a `docs/framework` folder holds the README.\n */\nexport function frameworkDocsRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'docs', 'framework')\n if (existsSync(join(candidate, 'README.md'))) return candidate\n const parent = dirname(dir)\n if (parent === dir)\n throw new Error('acrylblends cannot find the framework docs (docs/framework)')\n dir = parent\n }\n}\n","/**\n * What an app knows about the library plugins it carries: `plugins/library.lock.json` records, for every library plugin copied into the app, the version it came from and a hash of each file\n * as it was copied. `acrylblends upgrade` reads it to tell a file nobody touched (safe to replace) from one the app edited (never replaced without `--force`). An app made before the lock existed\n * has none; for it `upgrade` falls back to the hashes of the released versions (`shared/library-history.json`, made by `scripts/build-library-history.mjs`).\n */\nimport { createHash } from 'node:crypto'\nimport { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { sharedDir } from './templates'\nimport { VERSION } from './version'\n\nexport const LOCK_FILE = 'library.lock.json'\n\nexport interface Lock {\n version: number\n plugins: Record<string, { from: string; files: Record<string, string> }>\n}\n\n/** A file's identity: a hash of its text with line endings normalized (a checkout may have changed them), cut to 20 hex digits. */\nexport function digest(content: Buffer | string): string {\n const text = typeof content === 'string' ? content : content.toString('utf8')\n return createHash('sha256').update(text.replace(/\\r\\n/gu, '\\n')).digest('hex').slice(0, 20)\n}\n\n/** Every file below `dir`, as `relative/path` (forward slashes) to its {@link digest}. */\nexport function hashFolder(dir: string): Record<string, string> {\n const files: Record<string, string> = {}\n const walk = (folder: string, inside: string): void => {\n for (const entry of readdirSync(folder).sort()) {\n const path = join(folder, entry)\n const name = inside === '' ? entry : `${inside}/${entry}`\n if (statSync(path).isDirectory()) walk(path, name)\n else files[name] = digest(readFileSync(path))\n }\n }\n if (existsSync(dir)) walk(dir, '')\n return files\n}\n\nexport function readLock(pluginsDir: string): Lock {\n const file = join(pluginsDir, LOCK_FILE)\n if (!existsSync(file)) return { version: 1, plugins: {} }\n try {\n const lock = JSON.parse(readFileSync(file, 'utf8')) as Lock\n return { version: 1, plugins: lock.plugins ?? {} }\n } catch {\n return { version: 1, plugins: {} }\n }\n}\n\n/** Record that `name` was copied from this version of the library, with the hashes of the files as they now are. */\nexport function recordLock(pluginsDir: string, name: string, files: Record<string, string>): void {\n const lock = readLock(pluginsDir)\n lock.plugins[name] = { from: VERSION, files }\n const sorted = Object.fromEntries(\n Object.entries(lock.plugins).sort(([a], [b]) => a.localeCompare(b)),\n )\n writeFileSync(\n join(pluginsDir, LOCK_FILE),\n `${JSON.stringify({ version: 1, plugins: sorted }, null, 2)}\\n`,\n )\n}\n\n/** Plugin to file to the hashes that file has had in released versions. */\nexport type History = Record<string, Record<string, string[]>>\n\nexport function readHistory(): History {\n const file = join(sharedDir(), 'library-history.json')\n if (!existsSync(file)) return {}\n return JSON.parse(readFileSync(file, 'utf8')) as History\n}\n","import { dirname, posix } from 'node:path'\n\n/**\n * The framework plugins import each other by package name (`acryl-router`, `acryl-router/middleware`): inside this repository they are workspace packages. An app has\n * no such packages, only the copied source, so `acrylblends init` and `add` rewrite those specifiers to relative paths when they copy a plugin. The file each name stands\n * for is the `entries` table of the plugin's `plugin.json`.\n */\n\n/** `acryl-router/middleware` to `acryl-router/src/middleware/index.ts`: every importable name of every library plugin. */\nexport type EntryTable = ReadonlyMap<string, string>\n\nexport function entryTable(\n library: ReadonlyMap<string, { readonly entries?: Readonly<Record<string, string>> | undefined }>,\n): EntryTable {\n const table = new Map<string, string>()\n for (const [plugin, { entries }] of library)\n for (const [subpath, file] of Object.entries(entries ?? {}))\n table.set(`${plugin}${subpath}`, `${plugin}/${file}`)\n return table\n}\n\n/** For every character of a TypeScript source: 0 = code, 1 = inside a comment, 2 = inside a string or template literal. */\nfunction classify(source: string): Uint8Array {\n const kind = new Uint8Array(source.length)\n let i = 0\n while (i < source.length) {\n const c = source[i]\n const next = source[i + 1]\n if (c === '/' && next === '/') {\n const end = source.indexOf('\\n', i)\n const stop = end === -1 ? source.length : end\n kind.fill(1, i, stop)\n i = stop\n } else if (c === '/' && next === '*') {\n const end = source.indexOf('*/', i + 2)\n const stop = end === -1 ? source.length : end + 2\n kind.fill(1, i, stop)\n i = stop\n } else if (c === \"'\" || c === '\"' || c === '`') {\n let j = i + 1\n while (j < source.length && source[j] !== c) {\n if (source[j] === '\\\\') j += 1\n else if (c !== '`' && source[j] === '\\n') break\n j += 1\n }\n kind.fill(2, i, Math.min(j + 1, source.length))\n i = j + 1\n } else i += 1\n }\n return kind\n}\n\nconst SPECIFIER =\n /(\\bfrom\\s+|\\bimport\\s*\\(\\s*|\\bimport\\s+)(['\"])(acryl-[A-Za-z0-9-]+(?:\\/[A-Za-z0-9./-]+)?)\\2/gu\n\n/**\n * Rewrite the specifiers that name another library plugin to relative paths. `file` is the path of the file inside the plugins folder (for example\n * `acryl-state/src/service.ts`). Only real import and export statements are touched: a specifier named in a comment or in a string (a documentation example) is text,\n * not a dependency. Any other `acryl-*` specifier in code is an error: a library plugin may depend only on the others and on public packages.\n */\nexport function rewriteSpecifiers(source: string, file: string, entries: EntryTable): string {\n const kind = classify(source)\n return source.replace(\n SPECIFIER,\n (match, lead: string, quote: string, specifier: string, offset: number) => {\n if (kind[offset] !== 0) return match\n const target = entries.get(specifier)\n if (target === undefined)\n throw new Error(`${file}: imports \"${specifier}\", which is not a library plugin`)\n let path = posix.relative(dirname(file), target)\n if (!path.startsWith('.')) path = `./${path}`\n return `${lead}${quote}${path}${quote}`\n },\n )\n}\n","import { cpSync, existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, join, sep } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { hashFolder, recordLock } from './lock'\nimport { type EntryTable, entryTable, rewriteSpecifiers } from './rewrite'\n\n/** One entry of the plugin library: `plugins/<name>/plugin.json` in this repository. */\nexport interface LibraryPlugin {\n readonly name: string\n readonly description: string\n /** Library plugins this one imports; they are copied with it. */\n readonly requires: readonly string[]\n /** npm packages an app needs at run time for this plugin. */\n readonly dependencies: Readonly<Record<string, string>>\n /** npm packages an app needs only to develop or run this plugin's tooling (for example `electron` for the desktop shell). */\n readonly devDependencies?: Readonly<Record<string, string>>\n /** `package.json` scripts the plugin adds; a script the app already has is kept. */\n readonly scripts?: Readonly<Record<string, string>>\n /** Lines the app's `.gitignore` needs for this plugin (added when missing, if the app has a `.gitignore`). */\n readonly gitignore?: readonly string[]\n /** The port's conformance suite, a path inside the plugin: shipped in the package, but copied into an app only by `acrylblends add <plugin> --suite` (it needs vitest). */\n readonly suite?: string\n /** What other library plugins may import from this one: the import name after the plugin's own (`''`, `/middleware`) and the file it stands for. Used to rewrite `acryl-*` imports when a plugin is copied. */\n readonly entries?: Readonly<Record<string, string>>\n /** `package.json` \"imports\" entries the app gets (`#acryl-kv`): a path below the plugins folder. A plugin of your own imports the port by that name, in tests and the type-check as well as when installed live. */\n readonly imports?: Readonly<Record<string, string>>\n /** How to mount it, shown after `acrylblends add`. */\n readonly mount?: string\n /**\n * Paths inside the plugin folder that are not copied into an app (for example a React hook an app without React cannot type-check, or colocated test\n * files). A pattern is an exact path or a glob: `*` matches within one folder name, `**` across folders, `?` one character; `**` followed by a slash also matches no folder.\n */\n readonly omit?: readonly string[]\n}\n\n/** A glob as a regular expression: `*` within a name, `**` across folders, `**` plus a slash may match no folder, `?` one character. */\nfunction globToRegExp(glob: string): RegExp {\n let source = ''\n for (let i = 0; i < glob.length; i += 1) {\n const c = glob[i] as string\n if (c === '*' && glob[i + 1] === '*') {\n if (glob[i + 2] === '/') {\n source += '(?:.*/)?'\n i += 2\n } else {\n source += '.*'\n i += 1\n }\n } else if (c === '*') source += '[^/]*'\n else if (c === '?') source += '[^/]'\n else source += c.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n }\n return new RegExp(`^${source}$`)\n}\n\n/** Whether a path inside a plugin folder (relative, any separator) is named by one of its `omit` patterns. */\nexport function isOmitted(inside: string, patterns: readonly string[] | undefined): boolean {\n if (!patterns || patterns.length === 0) return false\n const path = inside.split(sep).join('/')\n return patterns.some((pattern) =>\n /[*?]/.test(pattern) ? globToRegExp(pattern).test(path) : pattern === path,\n )\n}\n\n/** Files of a library plugin that stay in the repository: its manifest and its tests. */\nconst LIBRARY_ONLY = new Set(['plugin.json', 'test', 'tests'])\n\nexport class LibraryError extends Error {\n override name = 'LibraryError'\n}\n\n/** The library folder (`plugins/` at the repository root): walk up from this module, in source or in the build. */\nexport function libraryRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'plugins')\n if (existsSync(join(candidate, 'instance', 'plugin.json'))) return candidate\n const parent = dirname(dir)\n if (parent === dir)\n throw new LibraryError('acrylblends cannot find its plugin library (plugins/)')\n dir = parent\n }\n}\n\n/** Every plugin in the library, by name. */\nexport function readLibrary(root: string = libraryRoot()): Map<string, LibraryPlugin> {\n const plugins = new Map<string, LibraryPlugin>()\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n const manifest = join(root, entry.name, 'plugin.json')\n if (!entry.isDirectory() || !existsSync(manifest)) continue\n const plugin = JSON.parse(readFileSync(manifest, 'utf8')) as LibraryPlugin\n if (plugin.name !== entry.name)\n throw new LibraryError(`plugins/${entry.name}/plugin.json names \"${plugin.name}\"`)\n plugins.set(plugin.name, {\n ...plugin,\n requires: plugin.requires ?? [],\n dependencies: plugin.dependencies ?? {},\n })\n }\n return plugins\n}\n\n/** The plugins to copy for `names`: each one after everything it requires, no duplicates. */\nexport function closure(\n names: readonly string[],\n library: ReadonlyMap<string, LibraryPlugin>,\n): LibraryPlugin[] {\n const ordered: LibraryPlugin[] = []\n const visiting = new Set<string>()\n const visit = (name: string, trail: string[]) => {\n if (ordered.some((plugin) => plugin.name === name)) return\n const plugin = library.get(name)\n if (!plugin)\n throw new LibraryError(\n `no plugin \"${name}\" in the library${trail.length ? ` (required by ${trail.at(-1)})` : ''}`,\n )\n if (visiting.has(name))\n throw new LibraryError(\n `plugins require each other in a cycle: ${[...trail, name].join(' -> ')}`,\n )\n visiting.add(name)\n for (const required of plugin.requires) visit(required, [...trail, name])\n visiting.delete(name)\n ordered.push(plugin)\n }\n for (const name of names) visit(name, [])\n return ordered\n}\n\n/** The one folder every plugin of an app lives in, library copies and the ones an agent writes. */\nexport const PLUGINS_DIR = 'plugins'\n/** Where apps made before 0.7.8 kept library copies; still understood for one release. */\nexport const LEGACY_PLUGINS_DIR = join('src', 'plugins')\n/** The list of library copies in the plugins folder; the extensions plugin installs every other folder there. */\nexport const LIBRARY_LIST = 'library.json'\n\n/** The plugins folder of an app: `plugins/`, or `src/plugins/` for an app that still has the old layout (and no `plugins/`). */\nexport function pluginsFolder(appDir: string): { dir: string; legacy: boolean } {\n const legacy =\n existsSync(join(appDir, LEGACY_PLUGINS_DIR)) && !existsSync(join(appDir, PLUGINS_DIR))\n return { dir: join(appDir, legacy ? LEGACY_PLUGINS_DIR : PLUGINS_DIR), legacy }\n}\n\n/** The warning for an app with the old layout. */\nexport const LEGACY_WARNING =\n 'warning: this app keeps its plugins in src/plugins/ (and extensions/); plugins now live in one folder, plugins/. Move them with \"git mv src/plugins plugins\", fix the paths in tsconfig.json, package.json and src/, and see docs/framework/agents/index.md. The old layout is read for this release only.'\n\n/** List `name` among the library copies of a plugins folder (`library.json`). */\nexport function recordLibraryCopy(folder: string, name: string): void {\n const file = join(folder, LIBRARY_LIST)\n const names: string[] = existsSync(file)\n ? (JSON.parse(readFileSync(file, 'utf8')) as { plugins: string[] }).plugins\n : []\n if (!names.includes(name)) names.push(name)\n writeFileSync(file, `${JSON.stringify({ plugins: names.sort() }, null, 2)}\\n`)\n}\n\n/** Copy one library plugin's source into `<app>/plugins/<name>/`. Throws when that folder already exists. */\nexport function copyPlugin(\n plugin: LibraryPlugin,\n appDir: string,\n root: string = libraryRoot(),\n): string {\n const folder = pluginsFolder(appDir)\n const target = join(folder.dir, plugin.name)\n if (existsSync(target))\n throw new LibraryError(\n `${relativeName(appDir, target)} already exists; remove it first to copy a fresh one`,\n )\n const source = join(root, plugin.name)\n // A framework package (it has a package.json in the repository) is copied as its source: `src/` and the licence; the package shipped by npm already holds only that. Its\n // `plugin.json` lists `entries`, and the imports of such a plugin are rewritten to relative paths, in the repository and in the installed package alike.\n const isPackage = existsSync(join(source, 'package.json'))\n const entries = plugin.entries === undefined ? undefined : entryTable(readLibrary(root))\n cpSync(source, target, {\n recursive: true,\n filter: (path) => {\n const inside = path\n .slice(source.length + 1)\n .split(sep)\n .join('/')\n if (\n isPackage &&\n inside !== '' &&\n inside !== 'LICENSE' &&\n inside !== 'src' &&\n !inside.startsWith('src/')\n )\n return false\n return (\n !(dirname(path) === source && LIBRARY_ONLY.has(inside)) &&\n !isOmitted(inside, plugin.omit) &&\n inside !== plugin.suite\n )\n },\n })\n if (entries) rewriteTree(join(target, 'src'), plugin.name, 'src', entries)\n if (!folder.legacy) {\n recordLibraryCopy(folder.dir, plugin.name)\n // The hashes of the files as copied: `acrylblends upgrade` tells an untouched file from an edited one by them.\n recordLock(folder.dir, plugin.name, hashFolder(target))\n }\n return target\n}\n\n/** Rewrite the `acryl-*` imports of every TypeScript file below `dir` (the plugin's `src/`) to relative paths. */\nfunction rewriteTree(dir: string, plugin: string, inside: string, entries: EntryTable): void {\n for (const entry of readdirSync(dir)) {\n const path = join(dir, entry)\n if (statSync(path).isDirectory()) rewriteTree(path, plugin, `${inside}/${entry}`, entries)\n else if (/\\.(?:ts|tsx|mts)$/u.test(entry))\n writeFileSync(\n path,\n rewriteSpecifiers(readFileSync(path, 'utf8'), `${plugin}/${inside}/${entry}`, entries),\n )\n }\n}\n\n/** Copy one file of a library plugin (a conformance suite) with its `acryl-*` imports rewritten, as `copyPlugin` does for the rest. */\nexport function copyRewritten(\n from: string,\n to: string,\n plugin: string,\n inside: string,\n root: string = libraryRoot(),\n): void {\n writeFileSync(\n to,\n rewriteSpecifiers(\n readFileSync(from, 'utf8'),\n `${plugin}/${inside}`,\n entryTable(readLibrary(root)),\n ),\n )\n}\n\nconst relativeName = (appDir: string, path: string) =>\n path\n .slice(appDir.length + 1)\n .split(sep)\n .join('/')\n","import {\n cpSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from 'node:fs'\nimport { join } from 'node:path'\nimport { compareVersions, VERSION } from './version'\n\n/** Thrown when the target directory exists and is not empty. Nothing has been written. */\nexport class TargetNotEmptyError extends Error {\n constructor(readonly target: string) {\n super(\n `refusing to write into \"${target}\": the directory is not empty. Choose an empty or new directory.`,\n )\n this.name = 'TargetNotEmptyError'\n }\n}\n\n/** A project name npm accepts: lower case letters, digits and dashes, starting with a letter. */\nexport function isValidName(name: string): boolean {\n return /^[a-z][a-z0-9-]*$/.test(name) && name.length <= 214\n}\n\n/** A valid default project name derived from a directory path. */\nexport function nameFrom(directory: string): string {\n const base = directory.split(/[\\\\/]/).filter(Boolean).pop() ?? ''\n const cleaned = base\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/^[^a-z]+/, '')\n .replace(/-+$/, '')\n return cleaned || 'my-app'\n}\n\n/** Refuse a target that exists and is not an empty directory. A missing target is fine: it is created. */\nexport function assertTargetFree(target: string): void {\n if (!existsSync(target)) return\n if (!statSync(target).isDirectory() || readdirSync(target).length > 0)\n throw new TargetNotEmptyError(target)\n}\n\n/**\n * Copy a template into `target`, replacing `__APP_NAME__` with `name` and `__ACRYLBLENDS_RANGE__` with the range of this release. Checks the target before writing anything.\n *\n * @returns the copied files, relative to `target`\n */\nexport function copyTemplate(source: string, target: string, name: string): string[] {\n assertTargetFree(target)\n mkdirSync(target, { recursive: true })\n const written: string[] = []\n const walk = (from: string, to: string, prefix: string) => {\n for (const entry of readdirSync(from, { withFileTypes: true })) {\n if (\n entry.name === 'node_modules' ||\n (prefix === '' &&\n (entry.name === TEMPLATE_MANIFEST ||\n entry.name === 'upstream.json' ||\n entry.name === 'AGENTS.md'))\n )\n continue\n const sourcePath = join(from, entry.name)\n // npm and git treat a file named .gitignore specially, so templates keep it as `gitignore` (and `dockerignore`).\n const targetName =\n prefix === '' && (entry.name === 'gitignore' || entry.name === 'dockerignore')\n ? `.${entry.name}`\n : entry.name\n const targetPath = join(to, targetName)\n if (entry.isDirectory()) {\n mkdirSync(targetPath, { recursive: true })\n walk(sourcePath, targetPath, `${prefix}${entry.name}/`)\n continue\n }\n const text = readFileSync(sourcePath, 'utf8')\n if (text.includes('__APP_NAME__') || text.includes('__ACRYLBLENDS_RANGE__'))\n writeFileSync(\n targetPath,\n text.replaceAll('__APP_NAME__', name).replaceAll('__ACRYLBLENDS_RANGE__', `^${VERSION}`),\n )\n else cpSync(sourcePath, targetPath)\n written.push(`${prefix}${targetName}`)\n }\n }\n walk(source, target, '')\n return written\n}\n\n/** A template's own manifest, `template.json`: the library plugins it is built from. Never copied (nor is `upstream.json`, the record of what was synced from ACRYL, nor the template's `AGENTS.md`, which `writeAgentFiles` joins to the shared core). */\nexport const TEMPLATE_MANIFEST = 'template.json'\n\n/** The address a template's desktop window opens on, when it is not the root (`template.json`: `desktopPath`). */\nexport function readTemplateDesktopPath(templateDir: string): string | undefined {\n const file = join(templateDir, TEMPLATE_MANIFEST)\n if (!existsSync(file)) return undefined\n return (JSON.parse(readFileSync(file, 'utf8')) as { desktopPath?: string }).desktopPath\n}\n\n/** Record where the desktop window opens in package.json (`acrylblends.desktopPath`), read by `plugins/desktop/main.ts`. */\nexport function writeDesktopPath(appDir: string, path: string): void {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n acrylblends?: Record<string, unknown>\n }\n manifest.acrylblends = { ...manifest.acrylblends, desktopPath: path }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\nexport function readTemplatePlugins(templateDir: string): string[] {\n const file = join(templateDir, TEMPLATE_MANIFEST)\n if (!existsSync(file)) return []\n const { plugins } = JSON.parse(readFileSync(file, 'utf8')) as { plugins?: string[] }\n return plugins ?? []\n}\n\n/** A version a plugin's pin can be compared with: an exact one or a caret or tilde range. A tag, a path or a URL is the app's own choice. */\nfunction comparableVersion(spec: string): string | undefined {\n return /^[\\^~]?\\d+\\.\\d+\\.\\d+(?:-[\\w.]+)?$/u.test(spec) ? spec.replace(/^[\\^~]/u, '') : undefined\n}\n\n/** Whether a plugin's pin asks for more than what the app has: the plugin's code was written against that version. */\nfunction asksForNewer(wanted: string, current: string): boolean {\n const a = comparableVersion(wanted)\n const b = comparableVersion(current)\n return a !== undefined && b !== undefined && compareVersions(a, b) > 0\n}\n\n/**\n * Add run-time dependencies to an app's package.json and sort the keys. Returns the names to install: those added, and those moved because a plugin asks for a newer\n * version than the app lists (the plugin's new code needs it). A version the app lists that is the same or newer stays. A package the app lists as a development\n * dependency too is taken out of that list: npm lets the development entry win, so `npm install --omit=dev` would leave a run-time dependency out.\n */\nexport function mergeDependencies(\n appDir: string,\n dependencies: Readonly<Record<string, string>>,\n): string[] {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n }\n const current = manifest.dependencies ?? {}\n const added = Object.keys(dependencies).filter((name) => !(name in current))\n const moved = Object.entries(dependencies).filter(\n ([name, wanted]) => current[name] !== undefined && asksForNewer(wanted, current[name]),\n )\n const merged = { ...dependencies, ...current, ...Object.fromEntries(moved) }\n manifest.dependencies = Object.fromEntries(\n Object.entries(merged).sort(([a], [b]) => a.localeCompare(b)),\n )\n if (manifest.devDependencies !== undefined)\n manifest.devDependencies = Object.fromEntries(\n Object.entries(manifest.devDependencies).filter(([name]) => !(name in merged)),\n )\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return [...added, ...moved.map(([name]) => name)]\n}\n\n/** Add `package.json` \"imports\" entries (`#acryl-kv`: `./plugins/acryl-kv/src/index.ts`); an entry the app already has is kept. Returns the names added. */\nexport function mergeImports(\n appDir: string,\n imports: Readonly<Record<string, string>>,\n pluginsDir: string,\n): string[] {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as { imports?: Record<string, string> }\n const current = manifest.imports ?? {}\n const added = Object.keys(imports).filter((name) => !(name in current))\n manifest.imports = {\n ...Object.fromEntries(\n Object.entries(imports).map(([name, path]) => [name, `./${pluginsDir}/${path}`]),\n ),\n ...current,\n }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return added\n}\n\n/** Declare a file as an opener in package.json (`\"acryl\": { \"opener\": [...] }`): the one place that may import a database provider. */\nexport function mergeOpener(appDir: string, opener: string): void {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n acryl?: { opener?: string | string[] }\n }\n const current = manifest.acryl?.opener\n const list = typeof current === 'string' ? [current] : (current ?? [])\n if (list.includes(opener)) return\n manifest.acryl = { ...manifest.acryl, opener: [...list, opener] }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\n/**\n * Add a plugin's development dependencies and scripts to an app's package.json: nothing the app already has is replaced, and a package the app already needs at run time\n * (`dependencies`) is not listed again as a development one. Returns the names of the development dependencies and scripts added.\n */\nexport function mergeTooling(\n appDir: string,\n tooling: {\n devDependencies?: Readonly<Record<string, string>> | undefined\n scripts?: Readonly<Record<string, string>> | undefined\n },\n): { devDependencies: string[]; scripts: string[] } {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n scripts?: Record<string, string>\n }\n const dev = manifest.devDependencies ?? {}\n const runtime = manifest.dependencies ?? {}\n const scripts = manifest.scripts ?? {}\n const wanted = Object.entries(tooling.devDependencies ?? {}).filter(\n ([name]) => !(name in runtime),\n )\n const addedDev = wanted.map(([name]) => name).filter((name) => !(name in dev))\n const addedScripts = Object.keys(tooling.scripts ?? {}).filter((name) => !(name in scripts))\n manifest.devDependencies = Object.fromEntries(\n Object.entries({ ...Object.fromEntries(wanted), ...dev })\n .filter(([name]) => !(name in runtime))\n .sort(([a], [b]) => a.localeCompare(b)),\n )\n manifest.scripts = { ...tooling.scripts, ...scripts }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return { devDependencies: addedDev, scripts: addedScripts }\n}\n\n/** What an app runs before `dev`, `start`, `test` and `check`: refuses a Node too old to run it, in plain words, before anything that would fail confusingly (a plain CommonJS file, so any Node can read it). */\nconst CHECK_NODE = `// Refuses a Node that is too old to run this app, before the command that would fail on it. Plain CommonJS so that any Node can read it.\nconst [major, minor] = process.versions.node.split('.').map(Number)\nif (major < 22 || (major === 22 && minor < 19)) {\n console.error(\n \\`Node 22.19+ needed, you have \\${process.versions.node}: install it from https://nodejs.org or with nvm (nvm install 22), then run this again.\\`,\n )\n process.exit(1)\n}\n`\n\n/** Write \\`scripts/check-node.cjs\\` and run it before the app's \\`dev\\`, \\`start\\`, \\`test\\` and \\`check\\` scripts (npm and pnpm run \\`pre<script>\\` first). Existing \\`pre*\\` scripts are kept. */\nexport function addNodePreflight(appDir: string): void {\n mkdirSync(join(appDir, 'scripts'), { recursive: true })\n writeFileSync(join(appDir, 'scripts', 'check-node.cjs'), CHECK_NODE)\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as { scripts?: Record<string, string> }\n const scripts = manifest.scripts ?? {}\n for (const name of ['dev', 'start', 'test', 'check'])\n if (name in scripts && !(`pre${name}` in scripts))\n scripts[`pre${name}`] = 'node scripts/check-node.cjs'\n manifest.scripts = scripts\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\n/** Add the lines a plugin needs to the app's \\`.gitignore\\` when it has one and they are missing. Returns the lines added. */\nexport function appendGitignore(appDir: string, lines: readonly string[] | undefined): string[] {\n const file = join(appDir, '.gitignore')\n if (!lines || lines.length === 0 || !existsSync(file)) return []\n const current = readFileSync(file, 'utf8')\n const present = new Set(current.split('\\n').map((line) => line.trim()))\n const missing = lines.filter((line) => !present.has(line))\n if (missing.length > 0)\n writeFileSync(\n file,\n `${current}${current.endsWith('\\n') || current === '' ? '' : '\\n'}${missing.join('\\n')}\\n`,\n )\n return missing\n}\n\n/**\n * The generated app's guide for a coding agent: the shared core, then the template's own part. `CLAUDE.md` is only an import of it. The skills\n * every app ships (`shared/skills/<name>/SKILL.md`) go to `.claude/skills/` (Claude Code) and `.agents/skills/` (the other agents), the same text in both.\n */\nexport function writeAgentFiles(\n sharedDir: string,\n templateDir: string,\n target: string,\n name: string,\n): string[] {\n const core = readFileSync(join(sharedDir, 'AGENTS.core.md'), 'utf8')\n const own = existsSync(join(templateDir, 'AGENTS.md'))\n ? readFileSync(join(templateDir, 'AGENTS.md'), 'utf8')\n : ''\n const defaults = readFileSync(join(sharedDir, 'DEFAULTS.md'), 'utf8')\n const text = `${core}${defaults}${own}`.replaceAll('__APP_NAME__', name)\n // AGENTS.md and CLAUDE.md are the same text; the skills are written where each kind of agent looks for them.\n writeFileSync(join(target, 'AGENTS.md'), text)\n writeFileSync(join(target, 'CLAUDE.md'), text)\n const written = ['AGENTS.md', 'CLAUDE.md']\n for (const skill of sharedSkills(sharedDir)) {\n const skillText = readFileSync(join(sharedDir, 'skills', skill, 'SKILL.md'), 'utf8').replaceAll(\n '__APP_NAME__',\n name,\n )\n for (const scope of ['.claude/skills', '.agents/skills']) {\n const folder = `${scope}/${skill}`\n mkdirSync(join(target, folder), { recursive: true })\n writeFileSync(join(target, folder, 'SKILL.md'), skillText)\n written.push(`${folder}/SKILL.md`)\n }\n }\n return written\n}\n\n/** The skills every generated app carries: the folder name of each under `shared/skills/`. */\nexport function sharedSkills(sharedDir: string): string[] {\n const root = join(sharedDir, 'skills')\n if (!existsSync(root)) return []\n return readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && existsSync(join(root, entry.name, 'SKILL.md')))\n .map((entry) => entry.name)\n .sort((a, b) => a.localeCompare(b))\n}\n\n/**\n * Copy the framework's docs (a generated copy of ACRYL's `docs/framework`, already checked against its digest) into `<app>/docs/framework/`,\n * so a coding agent working in the app reads the API from the folder it is in. The generated-copy notes and the digest record stay behind.\n *\n * @returns the copied files, relative to `target`\n */\nexport function copyFrameworkDocs(source: string, target: string): string[] {\n const written: string[] = []\n const walk = (from: string, to: string, prefix: string) => {\n for (const entry of readdirSync(from, { withFileTypes: true })) {\n if (entry.name === 'upstream.json' || entry.name === 'UPSTREAM.md') continue\n const sourcePath = join(from, entry.name)\n const targetPath = join(to, entry.name)\n if (entry.isDirectory()) {\n mkdirSync(targetPath, { recursive: true })\n walk(sourcePath, targetPath, `${prefix}${entry.name}/`)\n continue\n }\n cpSync(sourcePath, targetPath)\n written.push(`docs/framework/${prefix}${entry.name}`)\n }\n }\n mkdirSync(join(target, 'docs', 'framework'), { recursive: true })\n walk(source, join(target, 'docs', 'framework'), '')\n return written\n}\n","import { spawnSync } from 'node:child_process'\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { frameProfileForInit, frameProfileInput } from './frame-release'\nimport { APP_PLUGINS_FILE, appFrameRows } from './frame-rows'\nimport { applyIdeProfile } from './ide-profile'\nimport type { Io } from './io'\nimport { closure, copyPlugin, readLibrary } from './library'\nimport {\n addNodePreflight,\n copyFrameworkDocs,\n copyTemplate,\n isValidName,\n mergeDependencies,\n mergeImports,\n mergeTooling,\n nameFrom,\n readTemplateDesktopPath,\n readTemplatePlugins,\n TargetNotEmptyError,\n writeAgentFiles,\n writeDesktopPath,\n} from './project'\nimport {\n frameworkDocsRoot,\n resolveTemplate,\n sharedDir,\n TEMPLATE_SUMMARY,\n TEMPLATES,\n type Template,\n templateDir,\n} from './templates'\n\n/** A command line mistake. The CLI prints it with the help text and exits 2. */\nexport class UsageError extends Error {\n override name = 'UsageError'\n}\n\n/**\n * A new app is a git repository from the start: its `.gitignore` is written with the scaffold, `npm run save` commits it, and the frame's Files and\n * Changes views work on a git worktree (the app's own folder; without one they answer \"file request failed\"). A folder that already is a repository, or\n * a machine without git, is left alone.\n */\nfunction startRepository(directory: string): void {\n if (existsSync(join(directory, '.git'))) return\n const run = (args: string[]) =>\n spawnSync('git', args, { cwd: directory, stdio: 'ignore', windowsHide: true }).status === 0\n if (!run(['init', '-q'])) return\n if (!run(['add', '-A'])) return\n // A machine without a git identity still gets the first commit: this one is the scaffolder's, and later commits use the person's own identity. The `-c`\n // options are git's own and must precede the command.\n const identity = run(['config', 'user.email'])\n ? []\n : ['-c', 'user.name=acrylblends', '-c', 'user.email=scaffold@acrylblends.invalid']\n run([...identity, 'commit', '-q', '-m', 'Initial commit from acrylblends'])\n}\n\n/** What `init` was asked for. Anything left undefined is asked for on a terminal, or defaulted. */\nexport interface InitOptions {\n directory: string | undefined\n name: string | undefined\n template: Template | undefined\n install: boolean | undefined\n /** Also add the desktop shell (`add desktop`): an Electron window on the app's own server. */\n desktop: boolean\n /** Print no \"Next:\" block: `create-acrylblends` runs `init` and prints its own, for the package manager it knows. */\n quietNext: boolean\n yes: boolean\n}\n\n/** Parse `init [dir] [--template t] [--agent|--no-agent] [--name n] [--install] [-y]`. `--agent` means `--template agent`, `--no-agent` means `--template blank`. */\nexport function parseInitOptions(argv: readonly string[]): InitOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n const { values, positionals } = parsed\n if (positionals.length > 1) throw new UsageError('init takes at most one directory')\n if (values.agent && values['no-agent'])\n throw new UsageError('use --agent or --no-agent, not both')\n const named = values.template === undefined ? undefined : resolveTemplate(values.template)\n if (values.template !== undefined && named === undefined)\n throw new UsageError(`unknown template \"${values.template}\" (${TEMPLATES.join(', ')})`)\n const alias: Template | undefined = values.agent\n ? 'agent'\n : values['no-agent']\n ? 'blank'\n : undefined\n if (named !== undefined && alias !== undefined && named !== alias)\n throw new UsageError(\n `--template ${values.template} contradicts --${alias === 'agent' ? 'agent' : 'no-agent'}`,\n )\n return {\n directory: positionals[0],\n name: values.name,\n template: named ?? alias,\n install: values.install,\n desktop: values.desktop === true,\n quietNext: values['no-next'] === true,\n yes: values.yes === true,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n name: { type: 'string' },\n template: { type: 'string' },\n agent: { type: 'boolean' },\n 'no-agent': { type: 'boolean' },\n install: { type: 'boolean' },\n desktop: { type: 'boolean' },\n 'no-next': { type: 'boolean' },\n yes: { type: 'boolean', short: 'y' },\n },\n })\n\nconst yes = (answer: string) => ['y', 'yes'].includes(answer.trim().toLowerCase())\n\nasync function askTemplate(ask: (question: string) => Promise<string>): Promise<Template> {\n const answer = (\n await ask(\n `Templates:\\n${TEMPLATES.map((name) => ` ${name.padEnd(7)} ${TEMPLATE_SUMMARY[name]}`).join('\\n')}\\nTemplate [blank]: `,\n )\n )\n .trim()\n .toLowerCase()\n if (answer === '') return 'blank'\n const chosen = resolveTemplate(answer)\n if (chosen !== undefined) return chosen\n throw new UsageError(`unknown template \"${answer}\" (${TEMPLATES.join(', ')})`)\n}\n\n/** Scaffold a Cordis app. Returns the exit code: 0 done, 1 refused or failed. Never overwrites anything. */\nexport async function init(options: InitOptions, io: Io): Promise<number> {\n const relative = options.directory ?? '.'\n const directory = resolve(io.cwd, relative)\n const ask = options.yes ? undefined : io.ask\n\n let name = options.name ?? nameFrom(directory)\n if (options.name === undefined && ask)\n name = (await ask(`Project name (${name}): `)).trim() || name\n if (!isValidName(name)) {\n throw new UsageError(\n `\"${name}\" is not a valid project name (lower case letters, digits and dashes, starting with a letter)`,\n )\n }\n const template = options.template ?? (ask ? await askTemplate(ask) : 'blank')\n const install =\n options.install ?? (ask ? yes(await ask('Install dependencies with npm now? (y/N): ')) : false)\n\n let files: string[]\n try {\n files = copyTemplate(templateDir(template), directory, name)\n files.push(...writeAgentFiles(sharedDir(), templateDir(template), directory, name))\n files.push(...copyFrameworkDocs(frameworkDocsRoot(), directory))\n } catch (error) {\n if (!(error instanceof TargetNotEmptyError)) throw error\n io.stderr.write(`error: ${error.message}\\n`)\n return 1\n }\n const own = closure(readTemplatePlugins(templateDir(template)), readLibrary())\n const plugins = options.desktop\n ? closure([...own.map((plugin) => plugin.name), 'desktop'], readLibrary())\n : own\n // What only the desktop shell brought (and needs), named apart so the list does not read as if the web app had asked for it.\n const forDesktop = plugins.filter((plugin) => !own.some((owned) => owned.name === plugin.name))\n for (const plugin of plugins) {\n copyPlugin(plugin, directory)\n if (plugin.imports) mergeImports(directory, plugin.imports, 'plugins')\n mergeDependencies(directory, plugin.dependencies)\n mergeTooling(directory, plugin)\n }\n addNodePreflight(directory)\n // The ACRYL frame (docs/plans/ide-port.md, slice 6): a template with a frame row (`src/frame.ts`) gets the app's own DSH profile and overlay, so\n // `npm run dev` opens it inside the frame with its page at /app. A desktop app installs the frame's published packages (frame/packages.json);\n // ACRYLBLENDS_FRAME_PACKAGES overrides them (a checkout's build, or `none`), and an app with neither stays on the framework alone and says so.\n // The ACRYL frame's layer needs two rows for the app itself: its routes row (src/frame.ts), and the one composition row that runs the app's own plugins\n // beside the host through composeInto (frame/app-plugins.mjs), with the services they provide isolated through the Loader's entry option. appFrameRows is\n // the one home for those rows, their isolate rule and the generated file; `upgrade` writes the same ones for an app made before the frame.\n const frame = appFrameRows(directory)\n if (frame.appPlugins !== undefined) {\n mkdirSync(join(directory, 'frame'), { recursive: true })\n writeFileSync(join(directory, APP_PLUGINS_FILE), frame.appPlugins)\n }\n const frameRow = frame.rows\n if (frameRow.length > 0) {\n const specs = frameProfileForInit(options.desktop)\n if (specs === undefined) {\n const why = options.desktop\n ? 'ACRYLBLENDS_FRAME_PACKAGES is none'\n : 'init --desktop puts it inside the ACRYL frame'\n io.stdout.write(`note: this app runs on the framework alone; ${why}\\n`)\n } else {\n const wrote = applyIdeProfile(directory, frameProfileInput(specs, name, frameRow))\n io.stdout.write(`frame: wrote ${wrote.written.join(', ') || 'nothing (already right)'}\\n`)\n }\n }\n const desktopPath = options.desktop ? readTemplateDesktopPath(templateDir(template)) : undefined\n if (desktopPath !== undefined) writeDesktopPath(directory, desktopPath)\n io.stdout.write(\n `created ${name} (${template}) in ${directory}: ${files.length} files, plugins ${own.map((plugin) => plugin.name).join(', ')}${forDesktop.length > 0 ? `; the desktop shell adds ${forDesktop.map((plugin) => plugin.name).join(', ')}, which it needs` : ''}\\n`,\n )\n\n // The scaffold is the repository's first commit, so the frame's Changes view starts from what the person (or their agent) changed afterwards.\n startRepository(directory)\n if (install && (await io.install(directory)) !== 0) {\n io.stderr.write('error: npm install failed; run it yourself in the new directory\\n')\n return 1\n }\n const steps = [\n relative === '.' ? '' : ` cd ${relative}\\n`,\n install ? '' : ' npm install\\n',\n ' npm run dev\\n npm test\\n',\n ]\n if (!options.quietNext) io.stdout.write(`\\nNext:\\n${steps.join('')}`)\n return 0\n}\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\n/**\n * Plugins that `acrylblends add` mounts itself: a row in `blend.yaml` (after the row it needs, before the `routes` row) and an entry in the `registry` of `src/app.ts`.\n * `needs` are the rows the plugin cannot run without; an app that lacks them is refused before anything is copied.\n */\nexport interface Mounted {\n readonly name: string\n /** The identifier of the registry import, and the plugin names (the `name:` of a row) it needs. */\n readonly local: string\n readonly needs: readonly string[]\n /** The row this one goes after (it goes before `routes` otherwise). */\n readonly after?: string\n /** The `id:` of its row, when it is not the plugin's name (`db` for acryl-db-sqlite). */\n readonly row?: string\n /** Its entry file inside the plugin's folder (`index.ts`, or `src/index.ts` for the plugins that keep their source in `src/`). */\n readonly entry?: string\n}\n\nexport const MOUNTED: readonly Mounted[] = [\n { name: 'agent-panel', local: 'panel', needs: ['agent', 'extensions'] },\n { name: 'driver-pty', local: 'driverPty', needs: ['agent-panel'], after: 'agent-panel' },\n { name: 'driver-acp', local: 'driverAcp', needs: ['agent-panel'], after: 'agent-panel' },\n { name: 'feedback', local: 'feedback', needs: ['acryl-router'] },\n { name: 'showcase', local: 'showcase', needs: ['plugin-share'], after: 'plugin-share' },\n]\n\n/** A row, in either line ending: a file edited on Windows (or checked out with core.autocrlf) is still a Blend. */\nconst ROW = /^( *)- id: ?(\\S+)\\r?\\n\\1 {2}name: ?(\\S+)\\r?\\n/gmu\n\n/** The line ending the text already uses, so an edit keeps the file's own style. */\nconst eolOf = (text: string): string => (text.includes('\\r\\n') ? '\\r\\n' : '\\n')\n\n/** The `name:` of every row of the app's `blend.yaml`, or `undefined` when it has none. */\nexport function rowNames(appDir: string): string[] | undefined {\n const file = join(appDir, 'blend.yaml')\n if (!existsSync(file)) return undefined\n return [...readFileSync(file, 'utf8').matchAll(ROW)].map((match) => match[3] as string)\n}\n\n/** What a plugin to be mounted is missing: the rows it needs that neither the app nor this same command brings. */\nexport function missingRows(\n appDir: string,\n plugin: Mounted,\n alsoAdded: readonly string[],\n): string[] {\n const have = rowNames(appDir) ?? []\n return plugin.needs.filter((need) => !have.includes(need) && !alsoAdded.includes(need))\n}\n\nexport interface MountResult {\n readonly done: boolean\n /** What was edited, or why it was not (the person finishes by hand). */\n readonly note: string\n}\n\n/** Add the row to `blend.yaml` and the entry to `src/app.ts`; each only when it is not there yet and its place is where the template puts it. */\nexport function mount(appDir: string, plugin: Mounted): MountResult {\n const done: string[] = []\n const failed: string[] = []\n const manifestFile = join(appDir, 'blend.yaml')\n if (existsSync(manifestFile)) {\n let text = readFileSync(manifestFile, 'utf8')\n if ([...text.matchAll(ROW)].some((match) => match[3] === plugin.name))\n done.push('blend.yaml (already has the row)')\n else {\n const eol = eolOf(text)\n const anchor = plugin.after\n ? new RegExp(\n `^( *)- id: ?\\\\S+\\\\r?\\\\n\\\\1 {2}name: ?${plugin.after}\\\\r?\\\\n(?:\\\\1 {2}config:\\\\r?\\\\n(?:\\\\1 {4}.*\\\\r?\\\\n|\\\\1 {2}\\\\s*\\\\r?\\\\n)*)?`,\n 'mu',\n )\n : /^( *)- id: ?routes\\r?\\n/mu\n const found = anchor.exec(text)\n if (found) {\n const indent = found[1] as string\n const row = `${indent}- id: ${plugin.row ?? plugin.name}${eol}${indent} name: ${plugin.name}${eol}`\n const at = plugin.after ? found.index + found[0].length : found.index\n text = text.slice(0, at) + row + text.slice(at)\n writeFileSync(manifestFile, text)\n done.push(`blend.yaml (row ${plugin.name})`)\n } else\n failed.push(\n `blend.yaml: add a row \\`- id: ${plugin.name}\\` / \\`name: ${plugin.name}\\` ${plugin.after ? `after the ${plugin.after} row` : 'before the `routes` row'}`,\n )\n }\n }\n const appFile = join(appDir, 'src', 'app.ts')\n if (existsSync(appFile)) {\n let text = readFileSync(appFile, 'utf8')\n const entry = `'${plugin.name}': ${plugin.local}`\n if (text.includes(entry)) done.push('src/app.ts (already registered)')\n else {\n const eol = eolOf(text)\n const imports = [...text.matchAll(/^import .* from '\\.\\.\\/plugins\\/[^']+'\\r?\\n/gmu)]\n const last = imports.at(-1)\n // The registry as the templates write it: one entry per line, or all on one line (`{ 'acryl-router': router, routes }`).\n const table = /^export const registry = \\{(\\r?\\n)/mu.exec(text)\n if (last && table) {\n const file = plugin.entry ?? 'index.ts'\n const line = `import * as ${plugin.local} from '../plugins/${plugin.name}/${file}'${eol}`\n const at = table.index + table[0].length\n text =\n text.slice(0, at) +\n (table[1] === '\\n' || table[1] === '\\r\\n' ? ` ${entry},${eol}` : `${entry}, `) +\n text.slice(at)\n const lastEnd = last.index + last[0].length\n text = text.slice(0, lastEnd) + line + text.slice(lastEnd)\n writeFileSync(appFile, text)\n done.push(`src/app.ts (registered as ${plugin.local})`)\n } else\n failed.push(\n `src/app.ts: import it as \\`${plugin.local}\\` from '../plugins/${plugin.name}/${plugin.entry ?? 'index.ts'}' and add \\`${entry}\\` to the registry`,\n )\n }\n } else failed.push(`src/app.ts: not found; register ${entry(plugin)} in the app's registry`)\n return {\n done: failed.length === 0,\n note: [\n done.length > 0 ? `mounted in ${done.join(' and ')}` : '',\n ...failed.map((line) => `could not edit ${line}`),\n ]\n .filter(Boolean)\n .join('\\n'),\n }\n}\n\nconst entry = (plugin: Mounted) => `'${plugin.name}': ${plugin.local}`\n","import { cpSync, existsSync, mkdirSync, readFileSync, statSync } from 'node:fs'\nimport { basename, dirname, join, relative, resolve, sep } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { UsageError } from './init'\nimport type { Io } from './io'\nimport {\n closure,\n copyPlugin,\n copyRewritten,\n LEGACY_WARNING,\n type LibraryPlugin,\n libraryRoot,\n PLUGINS_DIR,\n pluginsFolder,\n readLibrary,\n} from './library'\nimport { MOUNTED, type Mounted, missingRows, mount, rowNames } from './mount'\nimport {\n appendGitignore,\n mergeDependencies,\n mergeImports,\n mergeOpener,\n mergeTooling,\n} from './project'\n\nexport interface AddOptions {\n readonly names: readonly string[]\n readonly directory: string | undefined\n /** Also copy the port's conformance suite (`src/conformance.ts`) and the `vitest` it runs on: what a provider of your own must pass. */\n readonly suite: boolean\n}\n\n/** Parse `add <plugin...> [--dir <app>] [--suite]`. */\nexport function parseAddOptions(argv: readonly string[]): AddOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n if (parsed.positionals.length === 0)\n throw new UsageError('add needs at least one plugin name (see \"acrylblends plugins\")')\n return {\n names: parsed.positionals,\n directory: parsed.values.dir,\n suite: parsed.values.suite === true,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: { dir: { type: 'string' }, suite: { type: 'boolean' } },\n })\n\nconst present = (appDir: string, name: string) => existsSync(join(pluginsFolder(appDir).dir, name))\n\n/**\n * Copy library plugins into an existing app, with the plugins they require, and add their npm packages to its package.json.\n * Refuses a plugin the app already has. Returns the exit code.\n */\nexport async function add(options: AddOptions, io: Io): Promise<number> {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n if (!existsSync(join(appDir, 'package.json')) || !existsSync(join(appDir, 'src'))) {\n io.stderr.write(\n `error: ${appDir} is not an app (no package.json and src/); run \"acrylblends init\" first\\n`,\n )\n return 1\n }\n if (pluginsFolder(appDir).legacy) io.stderr.write(`${LEGACY_WARNING}\\n`)\n // A path is an external plugin folder; anything else is a library plugin name.\n const external = options.names.filter((name) => /[\\\\/]/.test(name) || name.startsWith('.'))\n for (const folder of external) {\n const code = addExternal(resolve(io.cwd, folder), appDir, io)\n if (code !== 0) return code\n }\n // The desktop shell has two forms: the Blend app (it needs the `instance` plugin) and a plain Acryl app (`export default app`); `desktop` means the one that fits.\n const plain = !existsSync(join(pluginsFolder(appDir).dir, 'instance'))\n if (plain && options.names.includes('db')) {\n io.stderr.write(\n 'error: \"db\" is the Cordis plugin for a Blend app. A plain Acryl app opens its database with openDb from \"acrylblends/db/sqlite\" (see docs/framework/guides/data.md)\\n',\n )\n return 1\n }\n if (plain && options.names.includes('kv')) {\n io.stderr.write(\n 'error: \"kv\" is the Cordis plugin for a Blend app. A plain Acryl app opens its store with openKv from \"acrylblends/kv/json\" (see docs/framework/guides/data.md#a-tiny-app-kv)\\n',\n )\n return 1\n }\n const names = options.names\n .filter((name) => !external.includes(name))\n // `db` is the SQLite provider of the db port (it brings the port); a plain app opens its database with `acrylblends/db/sqlite` instead.\n .map((name) => (name === 'db' ? 'acryl-db-sqlite' : name))\n // `kv` is the JSON-file provider of the kv port (it brings the port); `acryl-kv-db` is the one over db.\n .map((name) => (name === 'kv' ? 'acryl-kv-json' : name))\n .map((name) => (name === 'desktop' && plain ? 'desktop-web' : name))\n if (names.length === 0) return 0\n const already = options.suite ? [] : names.filter((name) => present(appDir, name))\n if (already.length > 0) {\n io.stderr.write(\n `error: the app already has ${already\n .map(\n (name) =>\n `${relative(appDir, join(pluginsFolder(appDir).dir, name))\n .split(sep)\n .join('/')}`,\n )\n .join(', ')}\\n`,\n )\n return 1\n }\n // The agent panel and the PTY driver cannot run without the rows of the agent template: refuse before anything is copied, and say exactly what is missing.\n const mounting = MOUNTED.filter((plugin) => names.includes(plugin.name))\n for (const plugin of mounting) {\n const missing = missingRows(appDir, plugin, names)\n if (missing.length > 0) {\n io.stderr.write(\n `error: ${plugin.name} needs the ${missing.map((row) => `\\`${row}\\``).join(' and ')} ${missing.length === 1 ? 'row' : 'rows'} in blend.yaml, and this app has ${describeRows(appDir)}. ` +\n `${plugin.name === 'driver-pty' ? 'Add agent-panel first (acrylblends add agent-panel driver-pty), which needs the agent template. ' : ''}` +\n `The rows come with the agent template: start from it (npm create acrylblends@latest my-app agent) and add ${plugin.name} there. Nothing was changed.\\n`,\n )\n return 1\n }\n }\n const toCopy: LibraryPlugin[] = closure(names, readLibrary()).filter(\n (plugin) => !present(appDir, plugin.name),\n )\n const addedDependencies: string[] = []\n for (const plugin of toCopy) {\n copyPlugin(plugin, appDir)\n if (plugin.imports)\n mergeImports(\n appDir,\n plugin.imports,\n relative(appDir, pluginsFolder(appDir).dir).split(sep).join('/'),\n )\n addedDependencies.push(...mergeDependencies(appDir, plugin.dependencies))\n const tooling = mergeTooling(appDir, plugin)\n appendGitignore(appDir, plugin.gitignore)\n // The provider is mounted from src/app.ts, which is therefore the one file that may import it (`acrylblends check-deps`).\n if (plugin.name === 'acryl-db-sqlite') mergeOpener(appDir, 'src/app.ts')\n addedDependencies.push(...tooling.devDependencies)\n if (tooling.scripts.length > 0)\n io.stdout.write(\n `new scripts: ${tooling.scripts.map((name) => `npm run ${name}`).join(', ')}\\n`,\n )\n }\n if (options.suite) {\n const refused = copySuites(names, appDir, addedDependencies, io)\n if (refused !== 0) return refused\n }\n io.stdout.write(`added ${toCopy.map((plugin) => plugin.name).join(', ')} to ${appDir}\\n`)\n if (addedDependencies.length > 0)\n io.stdout.write(`dependencies to install: ${addedDependencies.join(', ')} (run npm install)\\n`)\n for (const plugin of toCopy.filter((plugin) => names.includes(plugin.name))) {\n const mounted = MOUNTED.find((candidate) => candidate.name === plugin.name)\n const result = mounted ? mount(appDir, mounted) : undefined\n io.stdout.write(\n `\\n${plugin.name}: ${result?.done ? `${result.note}${extraNote(mounted)}` : `${result ? `${result.note}\\n` : ''}${plugin.mount ?? `import it from '../plugins/${plugin.name}/index.ts' and mount it in src/app.ts`}`}\\n`,\n )\n }\n return 0\n}\n\n/** What an app has to say about its rows, for an error: the `name:` of each, or that it has no blend.yaml. */\nfunction describeRows(appDir: string): string {\n const rows = rowNames(appDir)\n return rows === undefined ? 'no blend.yaml' : `the rows ${rows.join(', ') || '(none)'}`\n}\n\n/** What to do after `npm install` for a plugin that needs more than the install: node-pty compiles a native module in an install script, which npm 11 and pnpm skip until approved. */\nfunction extraNote(plugin: Mounted | undefined): string {\n return plugin?.name === 'driver-pty'\n ? \"\\nnpm 11 and pnpm warn that node-pty's install script was not run, because they ask you to approve dependency install scripts. node-pty ships prebuilt binaries for macOS, Windows and Linux (x64, arm64), so you can usually ignore the warning. If the panel lists no `pty:` terminal (the app prints why at start), approve it and rebuild: `npm approve-scripts node-pty`, then `npm rebuild node-pty` (pnpm: `pnpm approve-builds`).\"\n : ''\n}\n\n/**\n * The vitest the suites run on. 3.2.x, not the newest: npm 10 (the npm of Node 22) crashes on the dependency tree of vitest 4 and 5 (\"Cannot read properties of null (reading\n * 'edgesOut')\"), and the suites use only `describe`, `it` and `expect`, which are the same in all of them. An app on a newer npm may raise it.\n */\nconst SUITE_RUNNER = { vitest: '3.2.4' }\n\n/**\n * Copy the conformance suite of each named port next to its source (`plugins/<name>/src/conformance.ts`), and add `vitest` to the app's development\n * dependencies. The suite is left out of a normal copy because an app that only uses a port has no use for it. Returns the exit code: 1 when a plugin has none.\n */\nfunction copySuites(names: readonly string[], appDir: string, added: string[], io: Io): number {\n const copies: Array<{ from: string; to: string }> = []\n // The suite belongs to the port: `add kv --suite` (acryl-kv-json, or acryl-kv-db) brings the suite of acryl-kv.\n const ports = [\n ...new Set(names.map((name) => name.replace(/^(acryl-(?:kv|db))(?:-.*)?$/u, '$1'))),\n ]\n const library = readLibrary()\n for (const name of ports) {\n const suite = library.get(name)?.suite\n const from = suite === undefined ? '' : join(libraryRoot(), name, suite)\n if (suite === undefined || !existsSync(from)) {\n io.stderr.write(`error: ${name} has no conformance suite (the ports db and kv do)\\n`)\n return 1\n }\n copies.push({ from, to: join(pluginsFolder(appDir).dir, name, suite) })\n }\n for (const { from, to } of copies) {\n mkdirSync(dirname(to), { recursive: true })\n copyRewritten(from, to, pluginOf(to), suiteOf(to))\n io.stdout.write(`added the conformance suite ${relative(appDir, to).split(sep).join('/')}\\n`)\n }\n added.push(...mergeTooling(appDir, { devDependencies: SUITE_RUNNER }).devDependencies)\n return 0\n}\n\n/** The plugin folder name and the file inside it, from a path below the plugins folder (`.../plugins/acryl-kv/src/conformance.ts`). */\nconst pluginOf = (path: string): string => path.split(sep).slice(-3, -2)[0] ?? ''\nconst suiteOf = (path: string): string => path.split(sep).slice(-2).join('/')\n\nconst PLUGIN_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/\n\n/**\n * Bring an external plugin folder (a package made with the builder's package_plugin, or any folder with an index.ts that exports a\n * Cordis plugin) into the app's `plugins/`, where the extensions plugin installs it like any other: checked, staged, started,\n * verified. Its runtime dependencies are not installed here; they are reported so the user adds them deliberately.\n */\nfunction addExternal(folder: string, appDir: string, io: Io): number {\n if (!existsSync(folder) || !statSync(folder).isDirectory()) {\n io.stderr.write(`error: ${folder} is not a folder\\n`)\n return 1\n }\n if (!['index.ts', 'index.js', 'index.mjs'].some((entry) => existsSync(join(folder, entry)))) {\n io.stderr.write(`error: ${folder} has no index.ts that exports a Cordis plugin\\n`)\n return 1\n }\n const manifestFile = join(folder, 'package.json')\n const manifest = existsSync(manifestFile)\n ? (JSON.parse(readFileSync(manifestFile, 'utf8')) as {\n name?: string\n dependencies?: Record<string, string>\n })\n : {}\n const name = (manifest.name ?? basename(folder)).replace(/^@[^/]+\\//, '')\n if (!PLUGIN_NAME.test(name)) {\n io.stderr.write(\n `error: \"${name}\" is not a plugin name (lowercase letters, digits and dashes)\\n`,\n )\n return 1\n }\n const target = join(\n pluginsFolder(appDir).legacy ? join(appDir, 'extensions') : join(appDir, PLUGINS_DIR),\n name,\n )\n if (existsSync(target)) {\n io.stderr.write(`error: the app already has ${relative(appDir, target).split(sep).join('/')}\\n`)\n return 1\n }\n cpSync(folder, target, {\n recursive: true,\n filter: (path) => !/[\\\\/](node_modules|\\.git)([\\\\/]|$)/.test(path.slice(folder.length)),\n })\n io.stdout.write(\n `added external plugin ${name} to ${dirname(target)}; it installs at the next start\\n`,\n )\n const dependencies = Object.keys(manifest.dependencies ?? {})\n if (dependencies.length > 0)\n io.stdout.write(\n `it uses ${dependencies.join(', ')}: add them to the app with npm install first\\n`,\n )\n return 0\n}\n\n/** `acrylblends plugins`: the library, one line per plugin. */\nexport function listPlugins(io: Io): number {\n for (const plugin of readLibrary().values()) {\n const requires = plugin.requires.length > 0 ? ` (requires ${plugin.requires.join(', ')})` : ''\n io.stdout.write(`${plugin.name.padEnd(12)} ${plugin.description}${requires}\\n`)\n }\n return 0\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { relative, resolve } from 'node:path'\nimport { UsageError } from './init'\nimport type { Io } from './io'\n\n/** Parse `build-client [root] [outDir]`. */\nexport function parseBuildClientOptions(argv: readonly string[]): { root: string; outDir: string } {\n const args = argv.filter((a) => !a.startsWith('-'))\n if (args.length !== argv.length)\n throw new UsageError('build-client takes [root] [outDir] and no options')\n if (args.length > 2)\n throw new UsageError('build-client takes at most two arguments: [root] [outDir]')\n return { root: args[0] ?? 'src/client', outDir: args[1] ?? 'dist/client' }\n}\n\n/**\n * Compile the app's client modules once (`buildClientModules` of the framework), for a production install that has no compiler at run time.\n * The framework is the one installed in the app (so the compiled files match the server that serves them), else this package's own.\n */\nexport async function buildClient(\n options: { root: string; outDir: string },\n io: Io,\n): Promise<number> {\n const root = resolve(io.cwd, options.root)\n if (!existsSync(root)) {\n io.stderr.write(\n `error: ${root} does not exist; the client modules live in src/client by default\\n`,\n )\n return 1\n }\n const outDir = resolve(io.cwd, options.outDir)\n const here = createRequire(resolve(io.cwd, 'package.json'))\n let specifier = 'acrylblends/node-server'\n try {\n here.resolve(specifier)\n } catch {\n specifier = new URL('../lib/node-server.js', import.meta.url).href\n }\n const mod = (await import(\n specifier.startsWith('file:') ? specifier : here.resolve(specifier)\n )) as typeof import('../lib/node-server')\n const written = mod.buildClientModules({ root, outDir })\n for (const file of written) io.stdout.write(`${relative(io.cwd, resolve(outDir, file))}\\n`)\n io.stdout.write(`built ${written.length} files in ${relative(io.cwd, outDir) || '.'}\\n`)\n return 0\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { join, relative, resolve, sep } from 'node:path'\nimport type { Io } from './io'\n\n/**\n * The dependency rule (spec 049): app code imports a port, never a provider. A port is `acrylblends/db` (the `Db` type); a provider is a\n * driver or the one function that opens a database. Only the opener, a file the app names once in package.json, may import a provider.\n */\n\n/** A package that is a database driver. An import of it, or of anything below it, is a provider import. */\nconst DRIVERS = [\n 'node:sqlite',\n 'sqlite',\n 'sqlite3',\n 'better-sqlite3',\n '@libsql/client',\n 'libsql',\n 'pg',\n 'pg-pool',\n 'pg-native',\n 'postgres',\n '@electric-sql/pglite',\n '@neondatabase/serverless',\n '@vercel/postgres',\n 'mysql',\n 'mysql2',\n]\n/** The openers a provider ships: importing one of them is opening a database. */\nconst OPENERS = ['acrylblends/db/sqlite', 'acryl-db-sqlite']\n/** The desktop shell is where Electron lives (`acrylblends add desktop` copies it). */\nconst ELECTRON = 'electron'\nconst ELECTRON_HOMES = [\n 'plugins/desktop/',\n 'plugins/desktop-web/',\n 'src/plugins/desktop/',\n 'src/plugins/desktop-web/',\n]\n/** The provider plugins themselves may import what they wrap. */\nconst PROVIDER_HOMES = ['plugins/acryl-db-sqlite/', 'src/plugins/acryl-db-sqlite/']\n\nconst SOURCE = /\\.(?:[cm]?[jt]s|[jt]sx)$/u\nconst SKIP_DIRS = new Set(['node_modules', 'dist', 'lib', 'build', 'coverage'])\nconst ROOTS = ['src', 'test', 'tests', 'plugins', 'extensions']\n\nexport interface Violation {\n readonly file: string\n readonly line: number\n readonly specifier: string\n readonly kind: 'driver' | 'opener' | 'electron'\n}\n\nconst matches = (specifier: string, name: string): boolean =>\n specifier === name || specifier.startsWith(`${name}/`)\n\n/** What a specifier is to the rule, or undefined when it is not a provider. */\nexport function classifySpecifier(specifier: string): Violation['kind'] | undefined {\n if (DRIVERS.some((name) => matches(specifier, name))) return 'driver'\n if (OPENERS.some((name) => matches(specifier, name))) return 'opener'\n if (matches(specifier, ELECTRON)) return 'electron'\n return undefined\n}\n\n/** The source with every comment replaced by spaces (newlines kept), so a line number still means what it did and a commented import is not an import. */\nexport function blankComments(source: string): string {\n let out = ''\n let i = 0\n while (i < source.length) {\n const c = source[i] as string\n const next = source[i + 1]\n if (c === '/' && next === '/') {\n const end = source.indexOf('\\n', i)\n const stop = end === -1 ? source.length : end\n out += ' '.repeat(stop - i)\n i = stop\n } else if (c === '/' && next === '*') {\n const end = source.indexOf('*/', i + 2)\n const stop = end === -1 ? source.length : end + 2\n out += source.slice(i, stop).replace(/[^\\n]/gu, ' ')\n i = stop\n } else if (c === \"'\" || c === '\"' || c === '`') {\n let j = i + 1\n while (j < source.length && source[j] !== c) {\n if (source[j] === '\\\\') j += 1\n else if (c !== '`' && source[j] === '\\n') break\n j += 1\n }\n out += source.slice(i, Math.min(j + 1, source.length))\n i = j + 1\n } else {\n out += c\n i += 1\n }\n }\n return out\n}\n\n/** `import x from 'a'`, `export * from 'a'`, `import 'a'`, `import('a')` and `require('a')`, with the line of each. */\nconst IMPORT =\n /(?:^|[\\s;{}()])(?:import\\s*(?:type\\s+)?(?:[\\w*${},\\s]*?\\s*from\\s*)?|export\\s*(?:type\\s+)?[\\w*${},\\s]*?\\s*from\\s*|import\\s*\\(\\s*|require\\s*\\(\\s*)(['\"])([^'\"\\n]+)\\1/gu\n\nexport function importsOf(source: string): { specifier: string; line: number }[] {\n const code = blankComments(source)\n const found: { specifier: string; line: number }[] = []\n for (const match of code.matchAll(IMPORT)) {\n const specifier = match[2] as string\n const at = (match.index ?? 0) + match[0].length - specifier.length - 2\n found.push({ specifier, line: code.slice(0, at).split('\\n').length })\n }\n return found\n}\n\nfunction listSources(dir: string, out: string[] = []): string[] {\n if (!existsSync(dir)) return out\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name.startsWith('.') || SKIP_DIRS.has(entry.name)) continue\n const path = join(dir, entry.name)\n if (entry.isDirectory()) listSources(path, out)\n else if (SOURCE.test(entry.name) && !entry.name.endsWith('.d.ts')) out.push(path)\n }\n return out\n}\n\n/** The opener files the app declares: `\"acryl\": { \"opener\": \"src/db.ts\" }` (or a list) in package.json. */\nexport function openersOf(appDir: string): string[] {\n const file = join(appDir, 'package.json')\n if (!existsSync(file)) return []\n const declared = (JSON.parse(readFileSync(file, 'utf8')) as { acryl?: { opener?: unknown } })\n .acryl?.opener\n const list = typeof declared === 'string' ? [declared] : Array.isArray(declared) ? declared : []\n return list.filter((entry): entry is string => typeof entry === 'string')\n}\n\n/** Every provider import outside the declared opener (and the homes that may hold one), in file and line order. */\nexport function findViolations(appDir: string): Violation[] {\n const openers = new Set(openersOf(appDir).map((entry) => entry.split(sep).join('/')))\n const violations: Violation[] = []\n for (const root of ROOTS) {\n for (const path of listSources(join(appDir, root)).sort()) {\n const file = relative(appDir, path).split(sep).join('/')\n if (openers.has(file)) continue\n for (const { specifier, line } of importsOf(readFileSync(path, 'utf8'))) {\n const kind = classifySpecifier(specifier)\n if (kind === undefined) continue\n if (kind === 'electron' && ELECTRON_HOMES.some((home) => file.startsWith(home))) continue\n if (kind !== 'electron' && PROVIDER_HOMES.some((home) => file.startsWith(home))) continue\n violations.push({ file, line, specifier, kind })\n }\n }\n }\n return violations\n}\n\nconst GUIDE = 'docs/framework/guides/data.md#why-the-database-is-opened-in-one-place'\n\nfunction advice(violation: Violation, openers: readonly string[]): string {\n if (violation.kind === 'electron')\n return `\"${violation.specifier}\" is the desktop shell's: only plugins/desktop (added with \"acrylblends add desktop\") imports electron`\n const where =\n openers.length > 0\n ? openers.join(', ')\n : 'a file named in package.json \"acryl\": { \"opener\": \"...\" }'\n return violation.kind === 'driver'\n ? `\"${violation.specifier}\" is a database driver; take a Db (import type { Db } from \"acrylblends/db\") as an argument and let ${where} open the database`\n : `\"${violation.specifier}\" opens a database, and only ${where} may; take a Db (import type { Db } from \"acrylblends/db\") as an argument`\n}\n\n/** Parse `check-deps [dir]`. */\nexport function parseCheckDepsOptions(argv: readonly string[]): { directory: string | undefined } {\n const args = argv.filter((a) => !a.startsWith('-'))\n if (args.length !== argv.length || args.length > 1)\n throw new Error('check-deps takes at most one argument: the app folder')\n return { directory: args[0] }\n}\n\n/** Report each provider import outside the opener, as `file:line: why`. Returns the exit code: 1 when there is one. */\nexport function checkDeps(options: { directory: string | undefined }, io: Io): number {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n const violations = findViolations(appDir)\n if (violations.length === 0) {\n io.stdout.write('check-deps: no app code imports a database provider outside the opener\\n')\n return 0\n }\n const openers = openersOf(appDir)\n for (const violation of violations)\n io.stderr.write(`${violation.file}:${violation.line}: ${advice(violation, openers)}\\n`)\n io.stderr.write(`See ${GUIDE}\\n`)\n return 1\n}\n","/**\n * The running frame's address, kept where the app's own data lives.\n *\n * The generated launcher prints the frame's URL with the launch token in it, and that token is the only way into a frame whose browser session ended.\n * So the address is written where the app keeps its data - `.acrylblends/`, which every scaffold's `.gitignore` already covers - at mint time,\n * owner-only (0600 here; a restricted ACL on Windows), and atomically (a temp file in the same folder renamed over it, so a reader never sees half a\n * record). On every later boot the launcher reuses the stored token instead of minting a new one, so the address - and an open page - survives a\n * restart; `acrylblends open` prints it, and `acrylblends open --new` rotates it (the running host replaces the token in place through\n * `/api/acryl-frame/token/new`), invalidating the old one immediately.\n *\n * DSH's connection package mints its launch token as a random value with no configuration seam (BrowserAuth's constructor calls\n * `processLaunchToken`, cached per process owner). The one seam it offers is the instance field the class keeps: `browserAuth.launchToken`, which\n * `authorizeIndex` and `authenticatedUrl` read on every use. The launcher sets that field from the stored record (guarded: a DSH package whose shape\n * lacks it simply keeps its fresh token), and sets it again on a rotation. Nothing inside the pinned package is edited.\n */\nimport { spawnSync } from 'node:child_process'\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from 'node:fs'\nimport { connect } from 'node:net'\nimport { join, resolve } from 'node:path'\nimport type { Io } from './io'\n\n/** The record's file name inside the app home. */\nexport const FRAME_URL_FILE = 'frame-url.json'\n\n/** Where a running frame rotates its token: a route the launcher mounts on the frame's own router. */\nexport const FRAME_TOKEN_PATH = '/api/acryl-frame/token/new'\n\n/** What the launcher writes: the address, the token it carries, the process that wrote it, and when. */\nexport interface FrameUrlRecord {\n readonly version: 1\n readonly url: string\n readonly token: string\n readonly pid: number\n readonly writtenAt: string\n}\n\n/** The app home the frame uses: `ACRYLBLENDS_HOME` when set, else `<app>/.acrylblends` (the launcher's own rule). */\nexport function frameHome(\n appRoot: string,\n env: Record<string, string | undefined> = process.env,\n): string {\n const pinned = env.ACRYLBLENDS_HOME?.trim()\n return pinned === undefined || pinned === ''\n ? join(resolve(appRoot), '.acrylblends')\n : resolve(pinned)\n}\n\nexport function frameUrlPath(home: string): string {\n return join(home, FRAME_URL_FILE)\n}\n\n/** Windows has no mode bits; the equivalent is an ACL with inheritance removed and only this account allowed. Best effort: a failure leaves the\n * inherited ACL, which is still the user's folder on a normal install. */\nfunction restrictToOwner(path: string): void {\n chmodSync(path, 0o600)\n if (process.platform !== 'win32') return\n const user = process.env.USERNAME\n if (user === undefined || user === '') return\n spawnSync('icacls', [path, '/inheritance:r', '/grant:r', `${user}:F`], {\n stdio: 'ignore',\n windowsHide: true,\n })\n}\n\n/** The token a frame address carries, if it carries one. */\nexport function tokenOfFrameUrl(url: string): string | undefined {\n try {\n const token = new URL(url).searchParams.get('token')\n return token === null || token === '' ? undefined : token\n } catch {\n return undefined\n }\n}\n\n/** Write the frame's address for `home`, owner-only and atomically. Throws when the folder or file cannot be written; the caller decides whether\n * that is fatal (the launcher prints why and keeps serving the app). */\nexport function writeFrameUrl(home: string, url: string, pid: number = process.pid): void {\n mkdirSync(home, { recursive: true })\n const record: FrameUrlRecord = {\n version: 1,\n url,\n token: tokenOfFrameUrl(url) ?? '',\n pid,\n writtenAt: new Date().toISOString(),\n }\n const target = frameUrlPath(home)\n const temporary = join(home, `${FRAME_URL_FILE}.${String(pid)}.tmp`)\n writeFileSync(temporary, `${JSON.stringify(record, null, 2)}\\n`, { mode: 0o600 })\n restrictToOwner(temporary)\n renameSync(temporary, target)\n restrictToOwner(target)\n}\n\n/** Remove the record, but only the one `pid` wrote: a host that is stopping must not erase the address of a newer one that already started. */\nexport function removeFrameUrl(home: string, pid: number = process.pid): void {\n const record = readFrameUrl(home)\n if (record === undefined || record.pid !== pid) return\n rmSync(frameUrlPath(home), { force: true })\n}\n\n/** The record, or undefined when there is none, it is not ours, or it is malformed. */\nexport function readFrameUrl(home: string): FrameUrlRecord | undefined {\n const path = frameUrlPath(home)\n if (!existsSync(path)) return undefined\n let parsed: unknown\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'))\n } catch {\n return undefined\n }\n if (typeof parsed !== 'object' || parsed === null) return undefined\n const record = parsed as Record<string, unknown>\n if (record.version !== 1) return undefined\n if (typeof record.url !== 'string' || record.url === '') return undefined\n if (typeof record.pid !== 'number' || !Number.isInteger(record.pid)) return undefined\n if (typeof record.writtenAt !== 'string') return undefined\n const token =\n typeof record.token === 'string' ? record.token : (tokenOfFrameUrl(record.url) ?? '')\n return { version: 1, url: record.url, token, pid: record.pid, writtenAt: record.writtenAt }\n}\n\n/** Whether something accepts a TCP connection at the record's address (the host process is alive and listening). */\nexport function frameIsListening(url: string, timeoutMs = 500): Promise<boolean> {\n let target: URL\n try {\n target = new URL(url)\n } catch {\n return Promise.resolve(false)\n }\n const port = Number(target.port === '' ? (target.protocol === 'https:' ? 443 : 80) : target.port)\n return new Promise((done) => {\n const socket = connect({ host: target.hostname, port })\n const finish = (listening: boolean) => {\n socket.destroy()\n done(listening)\n }\n socket.setTimeout(timeoutMs)\n socket.once('connect', () => finish(true))\n socket.once('timeout', () => finish(false))\n socket.once('error', () => finish(false))\n })\n}\n\n/** What `acrylblends open` found. */\nexport type FrameUrlLookup =\n | { readonly kind: 'running'; readonly record: FrameUrlRecord }\n | { readonly kind: 'stopped'; readonly record: FrameUrlRecord }\n | { readonly kind: 'none'; readonly home: string }\n\n/** Read the record and say whether the frame it names is still listening. */\nexport async function lookUpFrameUrl(\n appRoot: string,\n env: Record<string, string | undefined> = process.env,\n): Promise<FrameUrlLookup> {\n const home = frameHome(appRoot, env)\n const record = readFrameUrl(home)\n if (record === undefined) return { kind: 'none', home }\n return (await frameIsListening(record.url))\n ? { kind: 'running', record }\n : { kind: 'stopped', record }\n}\n\n/** Ask the running host to rotate its token, presenting the current one. Returns the new address, or a reason it failed. */\nasync function rotateFrameToken(\n record: FrameUrlRecord,\n): Promise<{ url: string } | { reason: string }> {\n let origin: string\n try {\n origin = new URL(record.url).origin\n } catch {\n return { reason: 'the stored address is not a URL' }\n }\n let response: Response\n try {\n response = await fetch(`${origin}${FRAME_TOKEN_PATH}`, {\n method: 'POST',\n headers: { 'x-frame-token': record.token, accept: 'application/json' },\n })\n } catch (cause) {\n return { reason: cause instanceof Error ? cause.message : String(cause) }\n }\n if (!response.ok) return { reason: `the host answered ${String(response.status)}` }\n const body = (await response.json().catch(() => undefined)) as { url?: unknown } | undefined\n if (typeof body?.url !== 'string' || body.url === '')\n return { reason: 'the host answered without an address' }\n return { url: body.url }\n}\n\n/**\n * `acrylblends open [dir] [--new]`: print the address of the frame running the app in this folder, so a page whose session ended (or the person, after\n * a restart) can open it again without restarting the host. Only a listening host's address is printed, and only the address goes to stdout, for copy\n * and paste. `--new` rotates the token in the running host first: the old address stops working at once and the new one is printed; with no host\n * running, the stored address is simply forgotten and the next start mints a fresh one.\n */\nexport async function openFrame(args: readonly string[], io: Io): Promise<number> {\n const fresh = args.includes('--new')\n const rest = args.filter((arg) => arg !== '--new')\n if (rest.length > 1) {\n io.stderr.write('usage: acrylblends open [dir] [--new]\\n')\n return 2\n }\n const directory = resolve(io.cwd, rest[0] ?? '.')\n const found = await lookUpFrameUrl(directory)\n if (found.kind === 'none') {\n io.stderr.write(`no frame has run in ${directory} yet; start it with npm run dev\\n`)\n return 1\n }\n if (!fresh) {\n if (found.kind === 'running') {\n io.stdout.write(`${found.record.url}\\n`)\n return 0\n }\n io.stderr.write(\n `the frame of ${directory} is not running (its last address was minted ${found.record.writtenAt}); run npm run dev, then this command again\\n`,\n )\n return 1\n }\n if (found.kind === 'stopped') {\n rmSync(frameUrlPath(frameHome(directory)), { force: true })\n io.stderr.write(\n `no frame is running in ${directory}; its stored address was forgotten and the next npm run dev mints a new one\\n`,\n )\n return 0\n }\n const rotated = await rotateFrameToken(found.record)\n if ('reason' in rotated) {\n io.stderr.write(\n `the running frame did not rotate its address (${rotated.reason}); run npm run dev to start it with a fresh one\\n`,\n )\n return 1\n }\n writeFrameUrl(frameHome(directory), rotated.url)\n io.stdout.write(`${rotated.url}\\n`)\n return 0\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nexport interface Invocation {\n file: string\n args: string[]\n}\n\n/**\n * How to start a package manager without the shell option (Node warns about passing args to a shell, DEP0190, and a `.cmd` shim cannot be spawned directly).\n * Off Windows the command runs as it is. On Windows npm runs as `node npm-cli.js` (the one npm is running from, else the one beside `node`); any other\n * manager goes through `cmd.exe /d /s /c <name>.cmd`, whose arguments stay an array.\n */\nexport function packageManagerInvocation(\n command: string,\n args: readonly string[],\n platform: NodeJS.Platform = process.platform,\n env: NodeJS.ProcessEnv = process.env,\n exists: (path: string) => boolean = existsSync,\n execPath: string = process.execPath,\n): Invocation {\n if (platform !== 'win32') return { file: command, args: [...args] }\n if (command === 'npm') {\n const running = env.npm_execpath\n const cli =\n running !== undefined && /npm-cli\\.c?js$/.test(running)\n ? running\n : join(dirname(execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js')\n if (exists(cli)) return { file: execPath, args: [cli, ...args] }\n }\n return { file: env.ComSpec ?? 'cmd.exe', args: ['/d', '/s', '/c', `${command}.cmd`, ...args] }\n}\n","import { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline/promises'\nimport { packageManagerInvocation } from './spawn'\n\n/** Everything the CLI touches outside the filesystem. Tests pass their own. */\nexport interface Io {\n stdout: { write(text: string): unknown }\n stderr: { write(text: string): unknown }\n cwd: string\n /** Ask one question; `undefined` when there is no terminal to ask on. */\n ask: ((question: string) => Promise<string>) | undefined\n /** Install dependencies in a directory; resolves to the exit code. */\n install(directory: string): Promise<number>\n}\n\n/** The real terminal and `npm install`. */\nexport function processIo(): Io {\n const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY)\n return {\n stdout: process.stdout,\n stderr: process.stderr,\n cwd: process.cwd(),\n ask: interactive\n ? async (question) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n try {\n return await rl.question(question)\n } finally {\n rl.close()\n }\n }\n : undefined,\n install: (directory) =>\n new Promise((done) => {\n const { file, args } = packageManagerInvocation('npm', ['install'])\n const child = spawn(file, args, { cwd: directory, stdio: 'inherit' })\n child.on('error', () => done(1))\n child.on('exit', (code) => done(code ?? 1))\n }),\n }\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { resolve } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { UsageError } from './init'\nimport type { Io } from './io'\n\nexport interface KvImportOptions {\n readonly from: string\n readonly to: string\n readonly overwrite: boolean\n readonly table: string | undefined\n readonly migrations: string | undefined\n}\n\n/** Parse `kv import <kv.json> <database.sqlite> [--overwrite] [--table name] [--migrations dir]`. */\nexport function parseKvOptions(argv: readonly string[]): KvImportOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n const [action, from, to, ...rest] = parsed.positionals\n if (action !== 'import')\n throw new UsageError('kv takes one subcommand: kv import <kv.json> <database.sqlite>')\n if (from === undefined || to === undefined || rest.length > 0)\n throw new UsageError('kv import takes two arguments: the JSON file and the SQLite file')\n return {\n from,\n to,\n overwrite: parsed.values.overwrite ?? false,\n table: parsed.values.table,\n migrations: parsed.values.migrations,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n overwrite: { type: 'boolean' },\n table: { type: 'string' },\n migrations: { type: 'string' },\n },\n })\n\n/** The framework as the app has it installed (so the code that reads the file is the code the app runs), else this package's own. */\nasync function framework<T>(io: Io, subpath: string, own: string): Promise<T> {\n const here = createRequire(resolve(io.cwd, 'package.json'))\n try {\n return (await import(here.resolve(`acrylblends/${subpath}`))) as T\n } catch {\n return (await import(new URL(`../lib/${own}`, import.meta.url).href)) as T\n }\n}\n\n/**\n * Import a JSON-file kv store into the `kv` table of an app's SQLite database: the move from `acryl-kv-json` to SQL. The file is not changed.\n * The database is opened with the app's `migrations/` folder when it has one (a database whose schema is newer than the files given is refused),\n * and the table is created when missing. Refuses to overwrite a key the table already has unless `--overwrite`.\n */\nexport async function kvImport(options: KvImportOptions, io: Io): Promise<number> {\n const from = resolve(io.cwd, options.from)\n const to = resolve(io.cwd, options.to)\n if (!existsSync(from)) {\n io.stderr.write(`error: ${from} does not exist\\n`)\n return 1\n }\n const kvCore = await framework<typeof import('../lib/kv')>(io, 'kv', 'kv.js')\n const json = await framework<typeof import('../lib/kv/json')>(io, 'kv/json', 'kv/json.js')\n const onDb = await framework<typeof import('../lib/kv/db')>(io, 'kv/db', 'kv/db.js')\n const sqlite = await framework<typeof import('../lib/db/sqlite')>(io, 'db/sqlite', 'db/sqlite.js')\n const migrations =\n options.migrations !== undefined\n ? resolve(io.cwd, options.migrations)\n : existsSync(resolve(io.cwd, 'migrations'))\n ? resolve(io.cwd, 'migrations')\n : undefined\n const source = await json.openKv({ path: from })\n try {\n const database = await sqlite.openDb({\n path: to,\n ...(migrations === undefined ? {} : { migrations }),\n })\n try {\n const target = onDb.kvOnDb(\n database.db,\n options.table === undefined ? {} : { table: options.table },\n )\n const result = await kvCore.copyKv(source.kv, target, { overwrite: options.overwrite })\n io.stdout.write(\n `imported ${result.copied} ${result.copied === 1 ? 'key' : 'keys'} from ${options.from} into ${options.to}` +\n `${options.table === undefined ? '' : ` (table ${options.table})`}` +\n `${result.overwritten > 0 ? `, ${result.overwritten} overwritten` : ''}\\n`,\n )\n return 0\n } finally {\n await database.close()\n }\n } finally {\n await source.close()\n }\n}\n","import { spawn, spawnSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { createServer } from 'node:net'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport type { Io } from './io'\nimport { packageManagerInvocation } from './spawn'\n\n/**\n * `acrylblends preflight`: what a workshop machine needs, checked before the first command, with one line per check. PASS names what was\n * found; FIX is the exact command or address that fixes it; NOTE is something the first blocks do without. Exit 1 while anything needs a fix.\n * Every probe of the machine goes through `Probes`, so each check's text is tested for every platform on any platform.\n */\n\nexport type Status = 'PASS' | 'FIX' | 'NOTE'\n\nexport interface Check {\n name: string\n status: Status\n detail: string\n}\n\n/** A program's run: `code` is null when it could not be started (not installed). */\nexport interface Run {\n code: number | null\n stdout: string\n}\n\nexport interface Probes {\n platform: NodeJS.Platform\n nodeVersion: string\n env: NodeJS.ProcessEnv\n home: string\n run(file: string, args: readonly string[]): Promise<Run>\n onPath(program: string): boolean\n readFile(path: string): string | undefined\n /** The first port in [from, to] that 127.0.0.1 can listen on, or the error code that stopped every one. */\n freePort(from: number, to: number): Promise<number | string>\n}\n\n/** The Node the framework needs (the bin refuses an older one before loading anything). */\nexport const NODE_MINIMUM = [22, 19] as const\n/** The range an app's port is chosen in (plugins/instance: PORT_BASE and PORT_SPAN). */\nexport const APP_PORTS = [3100, 3999] as const\n\nconst AGENTS = ['claude', 'codex', 'pi'] as const\nconst AGENT_INSTALL =\n 'install one coding agent: npm i -g @openai/codex then codex login, or Claude Code (https://claude.com/claude-code), or npm i -g @earendil-works/pi-coding-agent with DEEPSEEK_API_KEY set'\n/** Off by default on Windows; a full workshop ran without it, so it is a NOTE: what to do if a path is ever too long. */\nconst LONG_PATHS_NOTE =\n 'off; if an install fails with ENAMETOOLONG or \"path too long\", in PowerShell as administrator: New-ItemProperty -Path HKLM:\\\\SYSTEM\\\\CurrentControlSet\\\\Control\\\\FileSystem -Name LongPathsEnabled -Value 1 -PropertyType DWORD -Force'\n\nconst firstLine = (text: string) => text.trim().split(/\\r?\\n/)[0]?.trim() ?? ''\n\nexport function checkNode(version: string): Check {\n const [major = 0, minor = 0] = version.split('.').map(Number)\n const [needMajor, needMinor] = NODE_MINIMUM\n const ok = major > needMajor || (major === needMajor && minor >= needMinor)\n return ok\n ? { name: 'node', status: 'PASS', detail: version }\n : {\n name: 'node',\n status: 'FIX',\n detail: `${version} is too old: install Node ${needMajor}.${needMinor} or newer (the LTS) from https://nodejs.org`,\n }\n}\n\nexport async function checkNpm(probes: Probes): Promise<Check> {\n const npm = packageManagerInvocation('npm', ['--version'], probes.platform, probes.env)\n const result = await probes.run(npm.file, npm.args)\n return result.code === 0\n ? { name: 'npm', status: 'PASS', detail: firstLine(result.stdout) }\n : {\n name: 'npm',\n status: 'FIX',\n detail: 'npm did not run: it comes with Node, reinstall Node from https://nodejs.org',\n }\n}\n\nexport async function checkGit(probes: Probes): Promise<Check> {\n const result = await probes.run('git', ['--version'])\n return result.code === 0\n ? { name: 'git', status: 'PASS', detail: firstLine(result.stdout).replace(/^git version /, '') }\n : { name: 'git', status: 'FIX', detail: 'install git from https://git-scm.com' }\n}\n\n/** Whether pi can reach DeepSeek: the key in the environment, or the one pi stored when its owner logged in (the panel's rule). */\nexport function piHasKey(probes: Pick<Probes, 'env' | 'home' | 'readFile'>): boolean {\n if (probes.env.DEEPSEEK_API_KEY) return true\n const stored = probes.readFile(\n join(probes.env.PI_CODING_AGENT_DIR ?? join(probes.home, '.pi', 'agent'), 'auth.json'),\n )\n if (stored === undefined) return false\n try {\n const key = (JSON.parse(stored) as Record<string, { key?: unknown } | undefined>).deepseek?.key\n return typeof key === 'string' && key !== ''\n } catch {\n return false\n }\n}\n\n/**\n * The coding agents the agent panel would offer: on the PATH, and pi only with a DeepSeek key. On Windows the panel also reads an npm shim to find\n * its script (plugins/agent-panel/launch.ts); that is plugin code an app carries, not something the command imports, so the PATH is checked here.\n */\nconst HINT: Record<(typeof AGENTS)[number], string> = {\n claude: 'be logged in: run claude once',\n codex: 'be logged in: codex login',\n pi: 'DeepSeek key found',\n}\n\nexport function checkAgent(probes: Probes): Check {\n const installed = AGENTS.filter((agent) => probes.onPath(agent))\n const usable = installed.filter((agent) => agent !== 'pi' || piHasKey(probes))\n if (usable.length > 0)\n return {\n name: 'agent',\n status: 'PASS',\n // Each agent says what it needs, so a line never asks for a login pi does not use: claude and codex use their own login, pi uses the DeepSeek key.\n detail: usable.map((agent) => `${agent} (${HINT[agent]})`).join(', '),\n }\n if (installed.includes('pi'))\n return {\n name: 'agent',\n status: 'FIX',\n detail:\n 'pi has no DeepSeek key: set DEEPSEEK_API_KEY, or log in to pi once, or install another agent',\n }\n return { name: 'agent', status: 'FIX', detail: AGENT_INSTALL }\n}\n\nexport async function checkPort(probes: Probes): Promise<Check> {\n const [from, to] = APP_PORTS\n const found = await probes.freePort(from, to)\n return typeof found === 'number'\n ? {\n name: 'port',\n status: 'PASS',\n detail: `127.0.0.1:${found} is free (apps use ${from}-${to})`,\n }\n : {\n name: 'port',\n status: 'FIX',\n detail: `nothing can listen on 127.0.0.1 in ${from}-${to} (${found}): allow Node in the firewall or security software, or free a port`,\n }\n}\n\n/** Only block 5 needs gh, so it is a NOTE, never a FIX. */\nexport async function checkGh(probes: Probes): Promise<Check> {\n if (!probes.onPath('gh'))\n return {\n name: 'gh',\n status: 'NOTE',\n detail:\n 'not installed; block 5 publishes with it: https://cli.github.com, then gh auth login',\n }\n const status = await probes.run('gh', ['auth', 'status'])\n return status.code === 0\n ? { name: 'gh', status: 'PASS', detail: 'logged in' }\n : { name: 'gh', status: 'NOTE', detail: 'not logged in; block 5 needs it: gh auth login' }\n}\n\nexport async function checkPowerShell(probes: Probes): Promise<Check> {\n const pwsh = probes.onPath('pwsh')\n const result = await probes.run('powershell.exe', [\n '-NoProfile',\n '-Command',\n '$PSVersionTable.PSVersion.ToString()',\n ])\n const version = result.code === 0 ? firstLine(result.stdout) : undefined\n if (pwsh)\n return {\n name: 'powershell',\n status: 'PASS',\n detail: `pwsh 7 found${version ? `, Windows PowerShell ${version}` : ''}`,\n }\n if (version !== undefined)\n return {\n name: 'powershell',\n status: 'PASS',\n detail: `Windows PowerShell ${version}: use the guide's PowerShell lines where they differ (&& and NODE_ENV=... need PowerShell 7)`,\n }\n return {\n name: 'powershell',\n status: 'NOTE',\n detail:\n 'powershell.exe did not run; use cmd.exe lines or install PowerShell 7 (winget install Microsoft.PowerShell)',\n }\n}\n\nexport async function checkLongPaths(probes: Probes): Promise<Check> {\n const result = await probes.run('reg', [\n 'query',\n 'HKLM\\\\SYSTEM\\\\CurrentControlSet\\\\Control\\\\FileSystem',\n '/v',\n 'LongPathsEnabled',\n ])\n const value = /LongPathsEnabled\\s+REG_DWORD\\s+0x([0-9a-f]+)/i.exec(result.stdout)?.[1]\n return value !== undefined && Number.parseInt(value, 16) === 1\n ? { name: 'long paths', status: 'PASS', detail: 'enabled' }\n : { name: 'long paths', status: 'NOTE', detail: LONG_PATHS_NOTE }\n}\n\n/** Git Bash rewrites a slash-led argument into a Windows path (`/board` becomes `C:/Program Files/Git/board`); the door undoes it from 1.0.7. */\nexport function checkGitBash(probes: Pick<Probes, 'env'>): Check | undefined {\n if (!probes.env.MSYSTEM) return undefined\n return {\n name: 'git bash',\n status: 'NOTE',\n detail:\n 'this shell rewrites /paths: the door handles it from acrylblends 1.0.7; on an older app run .acryl/bin/acryl from PowerShell, or npx acrylblends@latest upgrade',\n }\n}\n\nexport async function preflight(probes: Probes): Promise<Check[]> {\n const checks = [\n checkNode(probes.nodeVersion),\n await checkNpm(probes),\n await checkGit(probes),\n checkAgent(probes),\n await checkPort(probes),\n await checkGh(probes),\n ]\n if (probes.platform === 'win32') {\n checks.push(await checkPowerShell(probes), await checkLongPaths(probes))\n const gitBash = checkGitBash(probes)\n if (gitBash) checks.push(gitBash)\n }\n return checks\n}\n\nexport function formatChecks(checks: readonly Check[]): string {\n const width = Math.max(...checks.map((check) => check.name.length))\n const lines = checks.map(\n (check) => `${check.status.padEnd(4)} ${check.name.padEnd(width)} ${check.detail}`,\n )\n const fixes = checks.filter((check) => check.status === 'FIX').length\n lines.push(\n fixes === 0\n ? 'Ready for the workshop.'\n : `${fixes} to fix before the workshop: run each FIX line, then npx acrylblends@latest preflight again.`,\n )\n return `${lines.join('\\n')}\\n`\n}\n\n/** The real machine. A program that cannot start resolves with code null; nothing here throws. */\nexport function machineProbes(): Probes {\n return {\n platform: process.platform,\n nodeVersion: process.versions.node,\n env: process.env,\n home: homedir(),\n run: (file, args) =>\n new Promise((done) => {\n let stdout = ''\n const child = spawn(file, args, { stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true })\n const timer = setTimeout(() => child.kill(), 15_000)\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString()\n })\n child.on('error', () => {\n clearTimeout(timer)\n done({ code: null, stdout })\n })\n child.on('close', (code) => {\n clearTimeout(timer)\n done({ code, stdout })\n })\n }),\n onPath: (program) =>\n spawnSync(process.platform === 'win32' ? 'where' : 'which', [program], { windowsHide: true })\n .status === 0,\n readFile: (path) => {\n try {\n return readFileSync(path, 'utf8')\n } catch {\n return undefined\n }\n },\n freePort: async (from, to) => {\n let last = 'no port tried'\n for (let port = from; port <= to; port++) {\n const result = await new Promise<true | string>((done) => {\n const server = createServer()\n server.once('error', (error: NodeJS.ErrnoException) => done(error.code ?? error.message))\n server.listen({ port, host: '127.0.0.1', exclusive: true }, () =>\n server.close(() => done(true)),\n )\n })\n if (result === true) return port\n last = result\n // A refusal that is not \"in use\" (EACCES from a firewall or policy) holds for every port: stop at once.\n if (last !== 'EADDRINUSE') return last\n }\n return last\n },\n }\n}\n\nexport async function runPreflight(\n io: Pick<Io, 'stdout'>,\n probes: Probes = machineProbes(),\n): Promise<number> {\n const checks = await preflight(probes)\n io.stdout.write(formatChecks(checks))\n return checks.some((check) => check.status === 'FIX') ? 1 : 0\n}\n","/**\n * The two `src/app.ts` edits `acrylblends add` makes for a plugin it mounts (`mount.ts`): an import line after the last plugin import, and a\n * registry entry after `export const registry = {`. An app's own copy of these lines is not the owner's change; the upgrade recognises them,\n * keeps them on the new file it writes, and reports only the owner's other lines.\n */\n\nimport { MOUNTED, type Mounted } from './mount.ts'\n\n/** One registration line the framework wrote into `src/app.ts`. */\nexport interface AppRegistration {\n readonly plugin: Mounted\n readonly kind: 'import' | 'registry'\n /** The line without its line ending. */\n readonly line: string\n}\n\nconst eolOf = (text: string): string => (text.includes('\\r\\n') ? '\\r\\n' : '\\n')\n\n/** The import line `mount` writes for one plugin. */\nexport function registrationImport(plugin: Mounted): string {\n return `import * as ${plugin.local} from '../plugins/${plugin.name}/${plugin.entry ?? 'index.ts'}'`\n}\n\n/** The registry entry line `mount` writes for one plugin (its indent and comma included). */\nexport function registrationEntry(plugin: Mounted): string {\n return ` '${plugin.name}': ${plugin.local},`\n}\n\n/** Every registration line at the start of `text`, the line ending kept; a line is one registration. */\nfunction registrations(text: string, mounted: readonly Mounted[]): AppRegistration[] {\n const found: AppRegistration[] = []\n for (const raw of text.split('\\n')) {\n const line = raw.endsWith('\\r') ? raw.slice(0, -1) : raw\n const importMatch = /^import \\* as (\\w+) from '\\.\\.\\/plugins\\/([^/]+)\\/([^']+)'$/u.exec(line)\n if (importMatch !== null) {\n const plugin = mounted.find(\n (candidate) => candidate.name === importMatch[2] && candidate.local === importMatch[1],\n )\n if (plugin !== undefined) found.push({ plugin, kind: 'import', line })\n continue\n }\n const entryMatch = /^ {2}'([^']+)': (\\w+),$/u.exec(line)\n if (entryMatch !== null) {\n const plugin = mounted.find(\n (candidate) => candidate.name === entryMatch[1] && candidate.local === entryMatch[2],\n )\n if (plugin !== undefined) found.push({ plugin, kind: 'registry', line })\n }\n }\n return found\n}\n\n/** The text without the framework's registration lines, and those lines in file order. A line the released template already has is the\n * template's own (agent-panel's import ships with the agent template), never the add command's edit. */\nexport function stripRegistrations(\n text: string,\n mounted: readonly Mounted[] = MOUNTED,\n template?: string,\n): { readonly text: string; readonly edits: readonly AppRegistration[] } {\n const inTemplate = new Set(\n (template ?? '').split('\\n').map((raw) => (raw.endsWith('\\r') ? raw.slice(0, -1) : raw)),\n )\n const edits = registrations(text, mounted).filter((edit) => !inTemplate.has(edit.line))\n if (edits.length === 0) return { text, edits }\n const wanted = new Set(edits.map((edit) => edit.line))\n const kept = text.split('\\n').filter((raw) => {\n const line = raw.endsWith('\\r') ? raw.slice(0, -1) : raw\n return !wanted.has(line)\n })\n return { text: kept.join('\\n'), edits }\n}\n\n/**\n * Put the framework's registration lines back into a template, at the same places `mount` inserts them: every import after the last\n * `../plugins/` import, every registry entry after the `export const registry = {` line. Lines already present are left alone.\n */\nexport function withRegistrations(text: string, edits: readonly AppRegistration[]): string {\n const eol = eolOf(text)\n let next = text\n const imports = edits.filter(\n (edit) =>\n edit.kind === 'import' &&\n !next.split('\\n').some((raw) => (raw.endsWith('\\r') ? raw.slice(0, -1) : raw) === edit.line),\n )\n if (imports.length > 0) {\n const last = [...next.matchAll(/^import .* from '\\.\\.\\/plugins\\/[^']+'\\r?\\n/gmu)].at(-1)\n if (last !== undefined) {\n const at = last.index + last[0].length\n next =\n next.slice(0, at) + imports.map((edit) => `${edit.line}${eol}`).join('') + next.slice(at)\n }\n }\n const entries = edits.filter(\n (edit) =>\n edit.kind === 'registry' &&\n !next.split('\\n').some((raw) => (raw.endsWith('\\r') ? raw.slice(0, -1) : raw) === edit.line),\n )\n if (entries.length > 0) {\n const table = /^export const registry = \\{(\\r?\\n)/mu.exec(next)\n if (table !== null) {\n const at = table.index + table[0].length\n next =\n next.slice(0, at) + entries.map((edit) => `${edit.line}${eol}`).join('') + next.slice(at)\n }\n }\n return next\n}\n\n/** One line of the app that is its own, not the framework's registration. */\nexport interface ChangedLine {\n /** 1-based. */\n readonly line: number\n readonly text: string\n}\n\n/** One line of the app that is its own, not the framework's registration. */\nexport interface ChangedLine {\n /** 1-based. */\n readonly line: number\n readonly text: string\n}\n\n/**\n * The app's lines that the expected file does not have, in order: the owner's real changes. A line-level longest common subsequence, cut to\n * `limit` lines, so a change reads as the lines it is, not as a whole file.\n */\nexport function ownerChangeLines(actual: string, expected: string, limit = 5): ChangedLine[] {\n const lineOf = (raw: string | undefined): string =>\n raw === undefined ? '' : raw.endsWith('\\r') ? raw.slice(0, -1) : raw\n const left = actual.split('\\n')\n const right = expected.split('\\n')\n // One table of the longest common subsequence lengths, then the walk reports the app's lines it cannot match.\n const lengths: number[][] = Array.from({ length: left.length + 1 }, () =>\n new Array<number>(right.length + 1).fill(0),\n )\n for (let i = left.length - 1; i >= 0; i -= 1) {\n for (let j = right.length - 1; j >= 0; j -= 1) {\n lengths[i]![j] =\n lineOf(left[i]) === lineOf(right[j])\n ? (lengths[i + 1]![j + 1] ?? 0) + 1\n : Math.max(lengths[i + 1]![j] ?? 0, lengths[i]![j + 1] ?? 0)\n }\n }\n const changed: ChangedLine[] = []\n let i = 0\n let j = 0\n while (i < left.length && changed.length < limit) {\n if (j < right.length && lineOf(left[i]) === lineOf(right[j])) {\n i += 1\n j += 1\n continue\n }\n if (j < right.length && (lengths[i + 1]![j] ?? 0) < (lengths[i]![j + 1] ?? 0)) {\n // The expected file has a line here the app does not: a template line, not the owner's.\n j += 1\n continue\n }\n changed.push({ line: i + 1, text: lineOf(left[i]) })\n i += 1\n }\n return changed\n}\n","/** A unified diff of two texts (3 lines of context), for showing what an upgrade changes. Very large pairs are summarized instead. */\n\nexport interface DiffStat {\n readonly added: number\n readonly removed: number\n}\n\ntype Op = { kind: ' ' | '+' | '-'; line: string }\n\nfunction ops(a: string[], b: string[]): Op[] | undefined {\n if (a.length * b.length > 6_000_000) return undefined\n // Longest common subsequence by dynamic programming over the suffixes.\n const width = b.length + 1\n const table = new Uint32Array((a.length + 1) * width)\n for (let i = a.length - 1; i >= 0; i -= 1)\n for (let j = b.length - 1; j >= 0; j -= 1)\n table[i * width + j] =\n a[i] === b[j]\n ? (table[(i + 1) * width + j + 1] as number) + 1\n : Math.max(table[(i + 1) * width + j] as number, table[i * width + j + 1] as number)\n const out: Op[] = []\n let i = 0\n let j = 0\n while (i < a.length && j < b.length) {\n if (a[i] === b[j]) {\n out.push({ kind: ' ', line: a[i] as string })\n i += 1\n j += 1\n } else if ((table[(i + 1) * width + j] as number) >= (table[i * width + j + 1] as number)) {\n out.push({ kind: '-', line: a[i] as string })\n i += 1\n } else {\n out.push({ kind: '+', line: b[j] as string })\n j += 1\n }\n }\n for (; i < a.length; i += 1) out.push({ kind: '-', line: a[i] as string })\n for (; j < b.length; j += 1) out.push({ kind: '+', line: b[j] as string })\n return out\n}\n\nconst lines = (text: string): string[] =>\n text === '' ? [] : text.replace(/\\r\\n/gu, '\\n').replace(/\\n$/u, '').split('\\n')\n\n/** How many lines a change adds and removes; undefined counts when the pair is too large to compare. */\nexport function diffStat(from: string, to: string): DiffStat {\n const all = ops(lines(from), lines(to))\n if (all === undefined) return { added: lines(to).length, removed: lines(from).length }\n return {\n added: all.filter((op) => op.kind === '+').length,\n removed: all.filter((op) => op.kind === '-').length,\n }\n}\n\n/** `--- a/<name>`, `+++ b/<name>` and the hunks; empty when the texts are equal. */\nexport function unifiedDiff(name: string, from: string, to: string, context = 3): string {\n const a = lines(from)\n const b = lines(to)\n const all = ops(a, b)\n if (all === undefined)\n return `--- a/${name}\\n+++ b/${name}\\n(too large to compare line by line: ${a.length} lines became ${b.length})\\n`\n if (!all.some((op) => op.kind !== ' ')) return ''\n const out = [`--- a/${name}`, `+++ b/${name}`]\n let at = 0\n let aLine = 1\n let bLine = 1\n const positions = all.map((op) => {\n const here = { a: aLine, b: bLine }\n if (op.kind !== '+') aLine += 1\n if (op.kind !== '-') bLine += 1\n return here\n })\n while (at < all.length) {\n while (at < all.length && all[at]?.kind === ' ') at += 1\n if (at >= all.length) break\n const start = Math.max(0, at - context)\n let end = at\n for (let k = at; k < all.length; k += 1) {\n if (all[k]?.kind !== ' ') end = k\n else if (k - end > context * 2) break\n }\n const stop = Math.min(all.length, end + context + 1)\n const slice = all.slice(start, stop)\n const first = positions[start] as { a: number; b: number }\n out.push(\n `@@ -${first.a},${slice.filter((op) => op.kind !== '+').length} +${first.b},${slice.filter((op) => op.kind !== '-').length} @@`,\n )\n for (const op of slice) out.push(`${op.kind}${op.line}`)\n at = stop\n }\n return `${out.join('\\n')}\\n`\n}\n","/**\n * The scaffolds published acrylblends releases shipped, as `acrylblends upgrade` needs them: the template's route module and, where the composition root\n * exports `composeInto` today, `src/app.ts`. Fixtures are verbatim from each release (deduplicated by content hash under `legacy/<template>/<hash>/`, built by\n * `scripts/build-legacy-layouts.mjs` from the release tags). An app's file matches when it equals the released bytes with the app name substituted, or the\n * current template's file (already upgraded); anything else is the app's own and is never replaced.\n */\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { basename, dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { TEMPLATES, type Template, templateDir } from './templates'\nimport { compareVersions } from './version'\n\n/** The app name init substituted for `__APP_NAME__` in the template's files. */\nconst PLACEHOLDER = '__APP_NAME__'\n\n/** The frame row module's path inside an app: a template with it is framed by the host. */\nexport const FRAME_FILE = 'src/frame.ts'\n\n/** One distinct scaffold a release shipped for a template: the content hash the build script gave it, and its files (released bytes, placeholders left). */\nexport interface LegacyVariant {\n readonly id: string\n readonly files: ReadonlyMap<string, string>\n}\n\n/** What the upgrade's read of one scaffold file found. */\nexport type ScaffoldState = 'same' | 'update' | 'add' | 'conflict'\n\nexport interface ScaffoldFile {\n /** Path inside the app, with forward slashes. */\n readonly file: string\n /** The current template's text, which an upgrade writes for `update` and `add`. */\n readonly text: string\n readonly state: ScaffoldState\n}\n\n/** The upgrade's read of an app: its template and what the frame layout would do to its scaffold. */\nexport interface LegacyRead {\n readonly template: Template\n /** `src/frame.ts` first (it has no released predecessor), then the template's own files the frame layout replaces. */\n readonly files: readonly ScaffoldFile[]\n}\n\n/** The `legacy/` fixtures: beside `dist/` in the build and `src/` in a checkout, found by walking up like the templates. */\nexport function legacyRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'legacy')\n if (existsSync(join(candidate, 'layouts.json'))) return candidate\n const parent = dirname(dir)\n if (parent === dir) throw new Error('acrylblends cannot find its legacy layout fixtures')\n dir = parent\n }\n}\n\n/** Whether a template carries the frame row (only the framed templates can be upgraded onto it). `dir` overrides the template folder for tests. */\nexport function carriesFrame(template: Template, dir: string = templateDir(template)): boolean {\n return existsSync(join(dir, FRAME_FILE))\n}\n\n/** A template's file as a scaffolded app of `name` has it, or undefined when the current template has no such file. */\nfunction templateText(template: Template, file: string, name: string): string | undefined {\n const path = join(templateDir(template), file)\n return existsSync(path) ? readFileSync(path, 'utf8').replaceAll(PLACEHOLDER, name) : undefined\n}\n\nfunction read(path: string): string | undefined {\n return existsSync(path) ? readFileSync(path, 'utf8') : undefined\n}\n\nfunction walk(dir: string, base: string, into: Map<string, string>): void {\n for (const entry of readdirSync(dir)) {\n const path = join(dir, entry)\n if (statSync(path).isDirectory()) walk(path, `${base}${entry}/`, into)\n else into.set(`${base}${entry}`, readFileSync(path, 'utf8'))\n }\n}\n\n/** Every distinct scaffold the releases shipped for a template, sorted by hash. Empty when no release shipped this template. */\nexport function legacyVariants(template: Template): LegacyVariant[] {\n const dir = join(legacyRoot(), template)\n if (!existsSync(dir)) return []\n return readdirSync(dir)\n .filter((entry) => statSync(join(dir, entry)).isDirectory())\n .sort()\n .map((id) => {\n const files = new Map<string, string>()\n walk(join(dir, id), '', files)\n return { id, files }\n })\n}\n\n/** The app's name as `init` wrote it into the scaffold (package.json, then the folder). */\nexport function appNameOf(appDir: string): string {\n try {\n const manifest = JSON.parse(readFileSync(join(appDir, 'package.json'), 'utf8')) as {\n name?: unknown\n }\n if (typeof manifest.name === 'string' && manifest.name !== '') return manifest.name\n } catch {\n // The caller already refused an app without a readable package.json; fall through to the folder.\n }\n return basename(appDir)\n}\n\n/** The scaffold files the releases recorded for a template (the union across variants): what a fixture app starts from. */\nexport function legacyScaffold(template: Template): readonly string[] {\n const paths = new Set<string>()\n for (const variant of legacyVariants(template))\n for (const file of variant.files.keys()) paths.add(file)\n return [...paths].sort()\n}\n\n/**\n * The app's entry module when a release before 0.7.8 wrote it: those releases shipped `src/main.ts` importing `./plugins/...` where the app keeps its\n * plugins one folder up (`../plugins/...` since 0.7.8), so the app cannot start as scaffolded and the frame layout cannot be applied on top of it.\n */\nexport function brokenEntry(appDir: string): string | undefined {\n const file = join(appDir, 'src', 'main.ts')\n const text = read(file)\n return text !== undefined && /from '\\.\\/plugins\\//u.test(text) ? 'src/main.ts' : undefined\n}\n\n/** The first release whose scaffold an upgrade can carry forward: 0.7.8 fixed the entry module's plugin imports. */\nconst FIRST_FIXED = '0.7.8'\n\n/**\n * Whether every release that shipped a layout variant is before 0.7.8. Such an app's `src/main.ts` imports `./plugins/...` and cannot start (see\n * {@link brokenEntry}); the upgrade reports it and leaves the frame alone. Exposed for the fixture builders (the gate and the tests).\n */\nexport function variantFromBrokenEra(template: Template, id: string): boolean {\n const versions = releasedVersions()\n const index = JSON.parse(readFileSync(join(legacyRoot(), 'layouts.json'), 'utf8')) as {\n releases?: Array<{ version?: unknown; layouts?: Record<string, unknown> }>\n }\n const users = (index.releases ?? []).filter((release) => release.layouts?.[template] === id)\n if (users.length === 0) return false\n return users.every((release) => {\n const version = typeof release.version === 'string' ? release.version : ''\n return version !== '' && versions.includes(version) && compareVersions(version, FIRST_FIXED) < 0\n })\n}\n\n/** The releases the fixtures cover, oldest first, from the index the build script wrote (for the CLI's message). */\nexport function releasedVersions(): string[] {\n try {\n const index = JSON.parse(readFileSync(join(legacyRoot(), 'layouts.json'), 'utf8')) as {\n releases?: Array<{ version?: unknown }>\n }\n return (index.releases ?? [])\n .map((release) => release.version)\n .filter((version): version is string => typeof version === 'string')\n } catch {\n return []\n }\n}\n\n/** The scaffold a named release shipped for a template, from the index (undefined when that release had no such template, or the index lacks it). */\nexport function releasedVariant(template: Template, version: string): LegacyVariant | undefined {\n try {\n const index = JSON.parse(readFileSync(join(legacyRoot(), 'layouts.json'), 'utf8')) as {\n releases?: Array<{ version?: unknown; layouts?: Record<string, unknown> }>\n }\n const release = (index.releases ?? []).find((entry) => entry.version === version)\n const id = release?.layouts?.[template]\n return typeof id === 'string'\n ? legacyVariants(template).find((variant) => variant.id === id)\n : undefined\n } catch {\n return undefined\n }\n}\n\n/** The release range the fixtures cover, for a sentence: `0.3.0 through 1.1.0`, or `the published releases` when the index cannot be read. */\nexport function releasedRange(): string {\n const versions = releasedVersions()\n const first = versions[0]\n const last = versions.at(-1)\n if (first === undefined || last === undefined) return 'the published releases'\n return first === last ? first : `${first} through ${last}`\n}\n\n/**\n * Which template made this app: the template whose released (or current) scaffold files the app matches most. Undefined when none matches: the app's\n * scaffold was edited, or a release that shipped no template made it, and the upgrade leaves it alone.\n */\nexport function identifyTemplate(appDir: string, name: string): Template | undefined {\n let best: { template: Template; score: number } | undefined\n for (const template of TEMPLATES) {\n const variants = legacyVariants(template)\n if (variants.length === 0) continue\n let score = 0\n for (const variant of variants) {\n let matches = 0\n for (const [file, text] of variant.files)\n if (read(join(appDir, file)) === text.replaceAll(PLACEHOLDER, name)) matches += 1\n score = Math.max(score, matches)\n }\n // An app already upgraded (or scaffolded by this version) matches the current template instead.\n let current = 0\n for (const file of legacyScaffold(template)) {\n const text = templateText(template, file, name)\n if (text !== undefined && read(join(appDir, file)) === text) current += 1\n }\n score = Math.max(score, current)\n if (score > 0 && (best === undefined || score > best.score)) best = { template, score }\n }\n return best?.template\n}\n\n/**\n * What the frame layout would do to the app's scaffold of `template`: `same` when the app already has the current template's file, `update` when it has a\n * released one, `add` when it is missing, `conflict` when the app edited it (the caller refuses without `--force`).\n */\nexport function readLegacy(appDir: string, template: Template, name: string): LegacyRead {\n const variants = legacyVariants(template)\n const files: ScaffoldFile[] = []\n const frame = read(join(appDir, FRAME_FILE))\n if (carriesFrame(template)) {\n const text = templateText(template, FRAME_FILE, name)\n if (text !== undefined)\n files.push({\n file: FRAME_FILE,\n text,\n state: frame === undefined ? 'add' : frame === text ? 'same' : 'conflict',\n })\n }\n for (const file of legacyScaffold(template)) {\n const text = templateText(template, file, name)\n // A file the current template no longer has is left to the app: the upgrade has nothing to write for it.\n if (text === undefined) continue\n const app = read(join(appDir, file))\n const released = variants\n .map((variant) => variant.files.get(file))\n .filter((value): value is string => value !== undefined)\n .map((value) => value.replaceAll(PLACEHOLDER, name))\n files.push({\n file,\n text,\n state:\n app === undefined\n ? 'add'\n : app === text\n ? 'same'\n : released.includes(app)\n ? 'update'\n : 'conflict',\n })\n }\n return { template, files }\n}\n","import {\n cpSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs'\nimport { dirname, join, relative, resolve, sep } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport {\n type ChangedLine,\n ownerChangeLines,\n stripRegistrations,\n withRegistrations,\n} from './app-registration.ts'\nimport { diffStat, unifiedDiff } from './diff'\nimport { frameProfileForApp, frameProfileInput } from './frame-release'\nimport { APP_PLUGINS_FILE, appFrameRows } from './frame-rows'\nimport {\n GENERATED_MARKER,\n type IdeProfileInput,\n type IdeProfileWritePlan,\n planIdeProfileWrite,\n writeIdeProfilePlan,\n} from './ide-profile'\nimport { UsageError } from './init'\nimport type { Io } from './io'\nimport {\n appNameOf,\n brokenEntry,\n carriesFrame,\n identifyTemplate,\n readLegacy,\n releasedRange,\n} from './legacy'\nimport {\n closure,\n copyPlugin,\n LEGACY_WARNING,\n LIBRARY_LIST,\n type LibraryPlugin,\n pluginsFolder,\n readLibrary,\n recordLibraryCopy,\n} from './library'\nimport { hashFolder, readHistory, readLock, recordLock } from './lock'\nimport { appendGitignore, mergeDependencies, mergeImports, mergeTooling } from './project'\nimport { type Template, templateDir } from './templates'\nimport { VERSION } from './version'\n\nexport interface UpgradeOptions {\n /** Library plugins to upgrade; none means every library plugin the app carries. */\n readonly names: readonly string[]\n readonly directory: string | undefined\n /** Show what would change and stop. */\n readonly dryRun: boolean\n /** Replace a file the app edited too (the edited version is saved first). */\n readonly force: boolean\n /** Print the diff of every changed file, not only of the edited ones. */\n readonly diff: boolean\n}\n\n/** Parse `upgrade [plugin...] [--dir <app>] [--dry-run] [--force] [--diff]`. */\nexport function parseUpgradeOptions(argv: readonly string[]): UpgradeOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n return {\n names: parsed.positionals,\n directory: parsed.values.dir,\n dryRun: parsed.values['dry-run'] === true,\n force: parsed.values.force === true,\n diff: parsed.values.diff === true,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n dir: { type: 'string' },\n 'dry-run': { type: 'boolean' },\n force: { type: 'boolean' },\n diff: { type: 'boolean' },\n },\n })\n\ntype FileState = 'same' | 'add' | 'update' | 'conflict' | 'remove' | 'extra'\n\ninterface PluginPlan {\n readonly plugin: LibraryPlugin\n /** The app does not have this plugin at all (a new requirement of one that it has). */\n readonly isNew: boolean\n /** Path inside the plugin folder to what happens to it. */\n readonly files: Map<string, FileState>\n /** The hashes of the files the current library would copy. */\n readonly fresh: Record<string, string>\n}\n\nconst posix = (path: string): string => path.split(sep).join('/')\n\nfunction readText(path: string): string {\n return readFileSync(path, 'utf8')\n}\n\n/** The library copies an app carries: the plugins listed in `plugins/library.json`, or when there is none the folders of `plugins/` that the library knows. */\nfunction carriedCopies(dir: string): string[] {\n const listFile = join(dir, LIBRARY_LIST)\n const listed = existsSync(listFile)\n ? ((JSON.parse(readFileSync(listFile, 'utf8')) as { plugins?: string[] }).plugins ?? [])\n : readdirSync(dir).filter((name) => statSync(join(dir, name)).isDirectory())\n return listed.filter((name) => existsSync(join(dir, name)))\n}\n\n/** The plugins the frame's host layer mounts by path: any framed app carries them, and an app made before the frame gets them here. */\nconst HOST_PLUGINS = ['acryl-web-server', 'acryl-page'] as const\n\n/** A library plugin a scaffold file imports: `from '../plugins/<name>/...'`. */\nconst PLUGIN_IMPORT = /from '\\.\\.\\/plugins\\/([^/']+)\\//gu\n\n/** The profile script the DSH-profile layout used; the host launcher replaced it, so an app upgraded from that layout drops it. */\nconst OLD_PROFILE_SCRIPT = 'scripts/ide-profile.mjs'\n\n/** One file the frame step writes: a template scaffold file, the frame row, or the generated composition row. */\ninterface FrameFileWrite {\n readonly file: string\n readonly text: string\n readonly state: 'add' | 'same' | 'update' | 'conflict'\n}\n\n/**\n * Scaffold files the app owns from its first edit: the release's copy is only the starting point, so an edited (or removed) one is never written, refused\n * over or backed up. A copy the app never edited is still the release's and is updated like any other scaffold file.\n */\nexport const KEPT_BY_THE_APP = new Set(['src/routes.ts'])\n\n/** What the frame row of every template imports from `src/routes.ts`: an app-owned copy without it cannot be shown in the frame. */\nconst FRAME_PREFIX_EXPORT = /\\bexport const FRAME_MOUNT_PREFIX\\b/u\n\n/** What `upgrade` does about the frame for one app: nothing (and why), or a plan computed before anything is written. */\nexport type FrameStep =\n | { readonly kind: 'unidentified' }\n | { readonly kind: 'broken-scaffold'; readonly file: string }\n | { readonly kind: 'no-frame-template'; readonly template: Template }\n | { readonly kind: 'unavailable'; readonly template: Template }\n | {\n readonly kind: 'planned'\n readonly template: Template\n /** What the overlay's rows and the package.json merge are planned from. */\n readonly input: IdeProfileInput\n readonly profile: IdeProfileWritePlan\n /** The app's scaffold, its frame row and the generated composition row. */\n readonly files: readonly FrameFileWrite[]\n /** App-owned scaffold files left exactly as the app has them (edited or removed), each with whether the frame can mount it. */\n readonly kept: readonly { readonly file: string; readonly mountable: boolean }[]\n /**\n * Library plugins the app does not carry yet and gets: the frame's host plugins, and every plugin the scaffold files written here import (with what\n * they require), so a release whose template mounts a new plugin (the agent template's `home`) leaves an app that starts.\n */\n readonly addedPlugins: readonly string[]\n /** The DSH-profile script of the older layout, removed when it is acrylblends' own. */\n readonly removeProfileScript: boolean\n /** Paths the app edited; refused without `--force`. */\n readonly conflicts: readonly string[]\n /** For a scaffold file with the owner's own lines: the lines, so the refusal names them. */\n readonly conflictLines?: Readonly<Record<string, readonly ChangedLine[]>>\n readonly changed: boolean\n }\n\n/** The state of one generated file that already exists: our marker makes it ours to update, anything else is the app's. */\nfunction generatedState(existing: string | undefined, text: string): FrameFileWrite['state'] {\n if (existing === undefined) return 'add'\n if (existing === text) return 'same'\n return GENERATED_MARKER.test(existing.split('\\n', 1)[0] ?? '') ? 'update' : 'conflict'\n}\n\n/**\n * Plan the frame half of an upgrade: an app made by the previous published release gets the frame-hosted layout (`scripts/ide-host.mjs`, the overlay with\n * the app's rows, `frame/app-plugins.mjs` with its entry isolate, the mount config in its composition root, the host plugins), and the template's own files\n * are replaced only where the app still has the released ones. An app whose scaffold was edited is refused by the caller unless it passes --force; an app\n * of a template without a frame row yet, or one whose scaffold is not this release's, stays standalone and is told so.\n */\nexport function planFrameStep(\n appDir: string,\n library: ReadonlyMap<string, LibraryPlugin>,\n templateDirFor: (template: Template) => string = templateDir,\n): FrameStep {\n const name = appNameOf(appDir)\n // The releases before 0.7.8 wrote an entry module that cannot run (see brokenEntry): nothing the upgrade writes can make it boot.\n const broken = brokenEntry(appDir)\n if (broken !== undefined) return { kind: 'broken-scaffold', file: broken }\n const template = identifyTemplate(appDir, name)\n if (template === undefined) return { kind: 'unidentified' }\n if (!carriesFrame(template, templateDirFor(template)))\n return { kind: 'no-frame-template', template }\n const specs = frameProfileForApp(appDir)\n if (specs === undefined) return { kind: 'unavailable', template }\n const released = readLegacy(appDir, template, name)\n // src/routes.ts is the app's own from its first edit: an edited or removed one is never taken as the template's, never refused over, and never\n // overwritten with --force (the backup list takes its files from this list too). An untouched released copy is updated: the frame row needs the new one.\n const ownedByTheApp = (file: { file: string; state: FrameFileWrite['state'] }) =>\n KEPT_BY_THE_APP.has(file.file) && (file.state === 'conflict' || file.state === 'add')\n const kept = released.files.filter(ownedByTheApp).map((file) => {\n const path = join(appDir, file.file)\n return {\n file: file.file,\n mountable: existsSync(path) && FRAME_PREFIX_EXPORT.test(readText(path)),\n }\n })\n const files: FrameFileWrite[] = released.files\n .filter((file) => !ownedByTheApp(file))\n .map((file) => ({\n file: file.file,\n text: file.text,\n state: file.state,\n }))\n // The framework's own `add` registration lines are not the owner's edits: keep them on the app.ts the upgrade writes, and report only the rest.\n const appIndex = files.findIndex((file) => file.file === 'src/app.ts')\n const appFile = appIndex < 0 ? undefined : files[appIndex]\n let conflictLines: Record<string, readonly ChangedLine[]> | undefined\n if (appFile !== undefined && appFile.state === 'conflict') {\n const appTsPath = join(appDir, 'src', 'app.ts')\n const current = existsSync(appTsPath) ? readText(appTsPath) : undefined\n if (current !== undefined) {\n const stripped = stripRegistrations(current, undefined, appFile.text)\n const target = withRegistrations(appFile.text, stripped.edits)\n if (stripped.text === appFile.text) {\n files[appIndex] = {\n file: 'src/app.ts',\n text: target,\n state: target === current ? 'same' : 'update',\n }\n } else {\n conflictLines = { 'src/app.ts': ownerChangeLines(current, target) }\n }\n }\n }\n // The composition root as it will be once the scaffold is written: the plan may be about to replace it.\n const plannedAppTs = files.find((file) => file.file === 'src/app.ts')?.text\n const appTs =\n plannedAppTs ??\n (existsSync(join(appDir, 'src', 'app.ts'))\n ? readText(join(appDir, 'src', 'app.ts'))\n : undefined)\n const rows = appFrameRows(appDir, { ...(appTs === undefined ? {} : { appTs }), hasFrame: true })\n if (rows.appPlugins !== undefined) {\n const path = join(appDir, APP_PLUGINS_FILE)\n files.push({\n file: APP_PLUGINS_FILE,\n text: rows.appPlugins,\n state: generatedState(existsSync(path) ? readText(path) : undefined, rows.appPlugins),\n })\n }\n const imported = files\n .filter((file) => file.file.endsWith('.ts'))\n .flatMap((file) => [...file.text.matchAll(PLUGIN_IMPORT)].map((match) => match[1] as string))\n const addedPlugins = closure(\n [...new Set([...HOST_PLUGINS, ...imported])].filter((plugin) => library.has(plugin)),\n library,\n )\n .map((plugin) => plugin.name)\n .filter((plugin) => !existsSync(join(appDir, 'plugins', plugin)))\n const oldScript = join(appDir, OLD_PROFILE_SCRIPT)\n const removeProfileScript =\n existsSync(oldScript) && GENERATED_MARKER.test(readText(oldScript).split('\\n', 1)[0] ?? '')\n const input: IdeProfileInput = frameProfileInput(specs, name, rows.rows)\n const profile = planIdeProfileWrite(appDir, input)\n const conflicts = [\n ...files.filter((file) => file.state === 'conflict').map((file) => file.file),\n ...profile.files.filter((file) => file.state === 'conflict').map((file) => file.file),\n ]\n const changed =\n files.some((file) => file.state !== 'same') ||\n profile.manifestChanged ||\n profile.files.some((file) => file.state !== 'same') ||\n addedPlugins.length > 0 ||\n removeProfileScript\n return {\n kind: 'planned',\n template,\n input,\n profile,\n files,\n kept,\n addedPlugins,\n removeProfileScript,\n conflicts,\n ...(conflictLines === undefined ? {} : { conflictLines }),\n changed,\n }\n}\n\n/** The `frame` line `upgrade` prints: every file it changes, or why it leaves the app alone. */\nexport function frameReport(frame: FrameStep): string {\n if (frame.kind === 'broken-scaffold')\n return `${frame.file} imports './plugins/...', as acrylblends 0.3.0 through 0.7.7 wrote it (the plugins are one folder up; 0.7.8 fixed the paths), so the app cannot start: change those imports to '../plugins/...' and run again`\n if (frame.kind === 'unidentified')\n return `not the scaffold of any published acrylblends release (${releasedRange()}), so it was edited or another version made it: left on the framework alone`\n if (frame.kind === 'no-frame-template')\n return `the ${frame.template} template has no src/frame.ts yet: left standalone`\n if (frame.kind === 'unavailable')\n return 'left on the framework alone: not a desktop app (`acrylblends add desktop` makes it one), or ACRYLBLENDS_FRAME_PACKAGES is none'\n const parts: string[] = []\n for (const file of frame.files)\n if (file.state !== 'same')\n parts.push(`${file.file} ${file.state === 'update' ? 'updated' : 'added'}`)\n for (const file of frame.profile.files)\n if (file.state !== 'same')\n parts.push(`${file.file} ${file.state === 'update' ? 'updated' : 'added'}`)\n if (frame.profile.clientRowsAdded.length > 0)\n parts.push(\n `scripts/ide-host.mjs client rows added (${frame.profile.clientRowsAdded.join(', ')})`,\n )\n for (const plugin of frame.addedPlugins) parts.push(`plugins/${plugin} added`)\n if (frame.removeProfileScript) parts.push(`${OLD_PROFILE_SCRIPT} removed`)\n if (frame.profile.manifestChanged)\n parts.push('package.json merged (dev runs scripts/ide-host.mjs)')\n for (const { file, mountable } of frame.kept)\n if (!mountable)\n parts.push(\n `${file} kept as the app's (edited): it does not export FRAME_MOUNT_PREFIX, so the frame cannot show the app until it does (a fresh app's ${file} shows how)`,\n )\n return parts.length === 0 ? 'up to date' : parts.join(', ')\n}\n\n/**\n * `acrylblends upgrade`: bring the library plugins an app carries (the router, the server, the door, `extensions`, ...) up to the version of this package. Each file is compared with what\n * the app was given: an untouched file is replaced, a file the app edited stops the upgrade (it is shown as a diff and nothing changes) unless `--force`, which saves the edited version\n * under `.acrylblends/upgrade-backup/` first. Returns the exit code: 0 done or nothing to do, 1 refused or failed.\n */\nexport async function upgrade(options: UpgradeOptions, io: Io): Promise<number> {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n if (!existsSync(join(appDir, 'package.json')) || !existsSync(join(appDir, 'src'))) {\n io.stderr.write(\n `error: ${appDir} is not an app (no package.json and src/); run \"acrylblends init\" first\\n`,\n )\n return 1\n }\n const folder = pluginsFolder(appDir)\n if (folder.legacy) {\n io.stderr.write(\n `${LEGACY_WARNING}\\nerror: upgrade needs the one-folder layout (plugins/); move the plugins first\\n`,\n )\n return 1\n }\n const library = readLibrary()\n const carried = carriedCopies(folder.dir)\n const unknown = carried.filter((name) => !library.has(name))\n for (const name of options.names)\n if (!carried.includes(name)) {\n io.stderr.write(\n `error: the app carries no library plugin \"${name}\" (it carries: ${carried.filter((n) => library.has(n)).join(', ') || 'none'})\\n`,\n )\n return 1\n }\n const selected = (options.names.length > 0 ? [...options.names] : carried).filter((name) =>\n library.has(name),\n )\n // The frame half of the upgrade, planned before anything is written so a refusal leaves the folder exactly as it was.\n const frame = planFrameStep(appDir, library)\n if (frame.kind === 'planned' && frame.conflicts.length > 0 && !options.force) {\n const own = frame.conflictLines?.['src/app.ts'] ?? []\n const detail =\n own.length === 0\n ? ''\n : `\\n src/app.ts keeps the framework's plugin registrations; the app's own lines are ${own.map((line) => `line ${String(line.line)} (${JSON.stringify(line.text)})`).join(', ')}`\n io.stderr.write(\n `error: ${frame.conflicts.join(', ')} in ${appDir} ${frame.conflicts.length === 1 ? 'is' : 'are'} not the previous release's scaffold and acrylblends did not write ${frame.conflicts.length === 1 ? 'it' : 'them'}; nothing was changed.${detail} Move the app's version aside (or merge it into the template's) and run again, or run again with --force (the app's versions are saved under .acrylblends/upgrade-backup/ first)\\n`,\n )\n return 1\n }\n const frameChanged = frame.kind === 'planned' && frame.changed\n if (selected.length === 0 && !frameChanged) {\n io.stdout.write(\n frame.kind === 'planned'\n ? 'nothing to upgrade: the library plugins and the frame are up to date\\n'\n : `nothing to upgrade: the app carries no library plugins\\n ${'frame'.padEnd(22)} ${frameReport(frame)}\\n`,\n )\n return 0\n }\n\n // What the current library would copy, into a scratch folder inside the app (`.acrylblends/` is ignored by git).\n const scratch = join(appDir, '.acrylblends', `upgrade-${process.pid}-${Date.now()}`)\n const lock = readLock(folder.dir)\n const history = readHistory()\n const plans: PluginPlan[] = []\n try {\n mkdirSync(join(scratch, 'plugins'), { recursive: true })\n const wanted = closure(selected, library)\n for (const plugin of wanted) copyPlugin(plugin, scratch)\n for (const plugin of wanted) {\n const here = join(folder.dir, plugin.name)\n const fresh = hashFolder(join(scratch, 'plugins', plugin.name))\n const files = new Map<string, FileState>()\n const isNew = !existsSync(here)\n const mine = isNew ? {} : hashFolder(here)\n const recorded = lock.plugins[plugin.name]?.files ?? {}\n const pristine = (file: string, hash: string): boolean =>\n recorded[file] === hash || (history[plugin.name]?.[file]?.includes(hash) ?? false)\n for (const [file, hash] of Object.entries(fresh)) {\n const have = mine[file]\n files.set(\n file,\n have === undefined\n ? 'add'\n : have === hash\n ? 'same'\n : pristine(file, have)\n ? 'update'\n : 'conflict',\n )\n }\n for (const [file, hash] of Object.entries(mine))\n if (!(file in fresh)) files.set(file, pristine(file, hash) ? 'remove' : 'extra')\n plans.push({ plugin, isNew, files, fresh })\n }\n\n // The report.\n const count = (plan: PluginPlan, state: FileState): string[] =>\n [...plan.files].filter(([, s]) => s === state).map(([file]) => file)\n io.stdout.write(`upgrade: the library plugins of ${appDir} to acrylblends ${VERSION}\\n`)\n let conflicts = 0\n let changes = 0\n for (const plan of plans) {\n const [updated, added, removed, edited, extra] = (\n ['update', 'add', 'remove', 'conflict', 'extra'] as const\n ).map((state) => count(plan, state))\n const label = plan.plugin.name.padEnd(22)\n conflicts += (edited as string[]).length\n if (plan.isNew) {\n changes += 1\n io.stdout.write(` ${label} new (required by a plugin of the app)\\n`)\n continue\n }\n if (\n (updated as string[]).length +\n (added as string[]).length +\n (removed as string[]).length +\n (edited as string[]).length ===\n 0\n ) {\n io.stdout.write(\n ` ${label} up to date${(extra as string[]).length > 0 ? ` (keeps ${(extra as string[]).length} file(s) of its own)` : ''}\\n`,\n )\n continue\n }\n changes += 1\n let plus = 0\n let minus = 0\n for (const file of [...(updated as string[]), ...(edited as string[])]) {\n const stat = diffStat(\n readText(join(folder.dir, plan.plugin.name, file)),\n readText(join(scratch, 'plugins', plan.plugin.name, file)),\n )\n plus += stat.added\n minus += stat.removed\n }\n const parts = [\n (updated as string[]).length > 0\n ? `${(updated as string[]).length} updated (+${plus} -${minus})`\n : '',\n (added as string[]).length > 0 ? `${(added as string[]).length} added` : '',\n (removed as string[]).length > 0 ? `${(removed as string[]).length} removed` : '',\n (edited as string[]).length > 0 ? `${(edited as string[]).length} EDITED HERE` : '',\n (extra as string[]).length > 0 ? `${(extra as string[]).length} of its own kept` : '',\n ].filter(Boolean)\n io.stdout.write(` ${label} ${parts.join(', ')}\\n`)\n }\n for (const name of unknown)\n io.stdout.write(` ${name.padEnd(22)} not in this library: left alone\\n`)\n io.stdout.write(` ${'frame'.padEnd(22)} ${frameReport(frame)}\\n`)\n\n // Diffs: always of the edited files (what would be lost), of every changed file with --diff.\n for (const plan of plans) {\n for (const [file, state] of plan.files) {\n if (\n !(\n state === 'conflict' ||\n (options.diff && (state === 'update' || state === 'add' || state === 'remove'))\n )\n )\n continue\n const name = `plugins/${plan.plugin.name}/${file}`\n const before = state === 'add' ? '' : readText(join(folder.dir, plan.plugin.name, file))\n const after =\n state === 'remove' ? '' : readText(join(scratch, 'plugins', plan.plugin.name, file))\n io.stdout.write(\n `\\n${state === 'conflict' ? 'EDITED HERE, the upgrade would replace it:\\n' : ''}${unifiedDiff(name, before, after)}`,\n )\n }\n }\n if (conflicts > 0 && !options.force) {\n io.stderr.write(\n `\\nerror: ${conflicts} file(s) were edited in this app and the upgrade would replace them; nothing was changed. Review the diffs above, move your changes out of the plugin (a plugin of your own in plugins/<name>/), or run again with --force (the edited versions are saved under .acrylblends/upgrade-backup/)\\n`,\n )\n return 1\n }\n if (options.dryRun) {\n io.stdout.write(\n `\\n${changes === 0 && !frameChanged ? 'everything is up to date' : 'dry run: nothing was changed'}\\n`,\n )\n return 0\n }\n if (changes === 0 && !frameChanged) {\n io.stdout.write('\\neverything is up to date\\n')\n return 0\n }\n\n // Apply.\n const backup = join(\n appDir,\n '.acrylblends',\n 'upgrade-backup',\n new Date().toISOString().replace(/[:.]/gu, '-'),\n )\n const addedDependencies: string[] = []\n for (const plan of plans) {\n const here = join(folder.dir, plan.plugin.name)\n if (plan.isNew) {\n // Copied the way `add` copies it: the plugin, and what it needs in package.json.\n mkdirSync(here, { recursive: true })\n cpSync(join(scratch, 'plugins', plan.plugin.name), here, { recursive: true })\n recordLibraryCopy(folder.dir, plan.plugin.name)\n } else {\n for (const [file, state] of plan.files) {\n const target = join(here, file)\n if (state === 'conflict') {\n mkdirSync(dirname(join(backup, plan.plugin.name, file)), { recursive: true })\n cpSync(target, join(backup, plan.plugin.name, file))\n }\n if (state === 'add' || state === 'update' || state === 'conflict') {\n mkdirSync(dirname(target), { recursive: true })\n cpSync(join(scratch, 'plugins', plan.plugin.name, file), target)\n } else if (state === 'remove') rmSync(target, { force: true })\n }\n recordLibraryCopy(folder.dir, plan.plugin.name)\n }\n recordLock(folder.dir, plan.plugin.name, plan.fresh)\n const plugin = plan.plugin\n if (plugin.imports) mergeImports(appDir, plugin.imports, posix(relative(appDir, folder.dir)))\n addedDependencies.push(...mergeDependencies(appDir, plugin.dependencies))\n addedDependencies.push(...mergeTooling(appDir, plugin).devDependencies)\n appendGitignore(appDir, plugin.gitignore)\n }\n // The package the app depends on moves with its plugins.\n const manifestFile = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(manifestFile, 'utf8')) as {\n dependencies?: Record<string, string>\n }\n if (\n manifest.dependencies?.acrylblends !== undefined &&\n manifest.dependencies.acrylblends !== `^${VERSION}`\n ) {\n manifest.dependencies.acrylblends = `^${VERSION}`\n writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\\n`)\n }\n // The frame last: the app's scaffold, the frame row, the generated composition row, the host plugins and the launcher/overlay write as `init` writes\n // them for a fresh app. A file of the app's own (no marker, or not the released template's) was refused above unless --force, which backs it up here.\n let frameBackups = 0\n if (frame.kind === 'planned' && frameChanged) {\n for (const file of [...frame.files, ...frame.profile.files]) {\n if (file.state !== 'conflict') continue\n mkdirSync(dirname(join(backup, file.file)), { recursive: true })\n cpSync(join(appDir, file.file), join(backup, file.file))\n frameBackups += 1\n }\n for (const plugin of frame.addedPlugins) {\n const known = library.get(plugin)\n if (known === undefined) continue\n copyPlugin(known, appDir)\n if (known.imports) mergeImports(appDir, known.imports, 'plugins')\n addedDependencies.push(...mergeDependencies(appDir, known.dependencies))\n addedDependencies.push(...mergeTooling(appDir, known).devDependencies)\n appendGitignore(appDir, known.gitignore)\n recordLibraryCopy(folder.dir, plugin)\n }\n for (const file of frame.files) {\n if (file.state === 'same') continue\n const target = join(appDir, file.file)\n mkdirSync(dirname(target), { recursive: true })\n writeFileSync(target, file.text)\n }\n // Re-planned against the manifest the plugin step and the host plugins left.\n writeIdeProfilePlan(appDir, planIdeProfileWrite(appDir, frame.input))\n if (frame.removeProfileScript) rmSync(join(appDir, OLD_PROFILE_SCRIPT), { force: true })\n }\n const upgradedPlugins = plans\n .filter((p) => [...p.files.values()].some((s) => s !== 'same' && s !== 'extra') || p.isNew)\n .map((p) => p.plugin.name)\n io.stdout.write(\n `\\nupgraded ${[...upgradedPlugins, ...(frameChanged ? ['the frame'] : [])].join(', ')}\\n`,\n )\n if (conflicts + frameBackups > 0)\n io.stdout.write(\n `the ${conflicts + frameBackups} edited file(s) replaced by --force are saved in ${posix(relative(appDir, backup))}/\\n`,\n )\n io.stdout.write(\n `acrylblends is now ^${VERSION} in package.json${addedDependencies.length > 0 ? `; dependencies to install: ${[...new Set(addedDependencies)].join(', ')}` : ''}: run npm install, then npm run check and npm test\\n`,\n )\n return 0\n } finally {\n rmSync(scratch, { recursive: true, force: true })\n }\n}\n","import { add, listPlugins, parseAddOptions } from './add'\nimport { buildClient, parseBuildClientOptions } from './build-client'\nimport { checkDeps, parseCheckDepsOptions } from './check-deps'\nimport { openFrame } from './frame-url'\nimport { init, parseInitOptions, UsageError } from './init'\nimport { type Io, processIo } from './io'\nimport { kvImport, parseKvOptions } from './kv-import'\nimport { runPreflight } from './preflight'\nimport { TEMPLATE_SUMMARY, TEMPLATES } from './templates'\nimport { parseUpgradeOptions, upgrade } from './upgrade'\nimport { VERSION } from './version'\n\nexport { VERSION }\n\nexport const HELP = `acrylblends ${VERSION} - scaffold Cordis apps\n\nUsage:\n acrylblends init [dir] [options] Create a Cordis app in an empty or new directory\n acrylblends add <plugin...> [--dir d] [--suite] Copy library plugins (and what they require) into an app;\n a folder path brings an external plugin into its plugins/;\n --suite also copies the port's conformance suite and vitest (add kv --suite)\n acrylblends upgrade [plugin...] [--dir d] [--dry-run] [--force] [--diff]\n Bring the library plugins an app carries (the router, the server, the door, ...) up to this\n version: untouched files are replaced, an edited file stops it (diff shown) unless --force\n acrylblends plugins List the plugin library\n acrylblends open [dir] Print the address of the frame running this app (the URL npm run dev printed),\n for a page that has to be opened again after its session ended\n acrylblends preflight Check this machine for the workshop: Node, npm, git, a coding agent, a free port\n (and on Windows PowerShell and long paths); one line per check, PASS or the fix\n acrylblends build-client [root] [out] Compile the client modules once for production (src/client to dist/client)\n acrylblends kv import <kv.json> <db> Move a JSON-file kv store into the kv table of a SQLite database [--overwrite] [--table t] [--migrations dir]\n acrylblends check-deps [dir] Fail when app code imports a database driver or opener outside the file named in\n package.json \"acryl\": { \"opener\": \"src/db.ts\" } (a port is imported, a provider is not)\n\nOptions for init:\n --template <name> one of the templates below (asked when not given); --agent means --template agent,\n --no-agent means --template blank\n --name <name> the project name (default: the directory name)\n --desktop also add the desktop shell: npm run desktop opens a window on the app\n --install run npm install after copying\n -y, --yes ask nothing; use defaults (the blank template, no install)\n\n -h, --help show this help\n -v, --version show the version\n\nTemplates (the same list as npm create acrylblends):\n${TEMPLATES.map((name) => ` ${name.padEnd(7)} ${TEMPLATE_SUMMARY[name]}`).join('\\n')}\n`\n\n/**\n * Run the CLI and return the exit code: 0 done, 1 failed or refused, 2 a usage error. Never calls `process.exit`.\n */\nexport async function runCli(argv: readonly string[], io: Io = processIo()): Promise<number> {\n const [command, ...args] = argv\n try {\n if (command === undefined || command === 'help' || command === '-h' || command === '--help') {\n io.stdout.write(HELP)\n return command === undefined ? 2 : 0\n }\n if (command === 'version' || command === '-v' || command === '--version') {\n io.stdout.write(`${VERSION}\\n`)\n return 0\n }\n if (command === 'init') return await init(parseInitOptions(args), io)\n if (command === 'add') return await add(parseAddOptions(args), io)\n if (command === 'upgrade') return await upgrade(parseUpgradeOptions(args), io)\n if (command === 'build-client') return await buildClient(parseBuildClientOptions(args), io)\n if (command === 'kv') return await kvImport(parseKv(args), io)\n if (command === 'check-deps') return checkDeps(parseCheckDeps(args), io)\n if (command === 'plugins') return listPlugins(io)\n if (command === 'open') return await openFrame(args, io)\n if (command === 'preflight') return await runPreflight(io)\n throw new UsageError(`unknown command \"${command}\"`)\n } catch (error) {\n if (error instanceof UsageError) {\n io.stderr.write(`error: ${error.message}\\n\\n${HELP}`)\n return 2\n }\n io.stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n }\n}\n\nfunction parseCheckDeps(args: readonly string[]) {\n try {\n return parseCheckDepsOptions(args)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n}\n\nfunction parseKv(args: readonly string[]) {\n return parseKvOptions(args)\n}\n"],"mappings":"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AO,CAAC;CAAE;CAC1D,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,OAAO;CAClC,OAAO;EACL,MAAM,YAAY,QAAQ,MAAM,IAAI,sBAAsB,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG;EAC3F,OAAO,QAAQ,KAAK,QAAQ,IAAI,IAAI;CACtC;AACF;;;;;;AAOA,SAAS,aAAa,SAAiB,aAA6B;CAClE,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,wBAAwB,QAAQ;EAChC;EACA;EACA;EACA;EACA;EACA;EACA;EACA,oDAAoD,YAAY;EAChE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,uBAAuB,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC,CAAC,WAAW,MAAM,IAAI;EACjF;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,sBAAsB,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC,CAAC,WAAW,OAAO,KAAK;EAClF,qBAAqB,KAAK,UAAU,mBAAmB,MAAM,CAAC;EAC9D,gCAAgC,KAAK,UAAU,oBAAoB;EACnE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,8CAA8C,gBAAgB;EAC9D;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,yBACE,UACA;EACF;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI;AACb;AAKA,SAAS,cAAc,UAAgD;CACrE,OACE,SAAS,MAAM,WAAW,MAAM,OAAO,MAAM,UAAA,oBAA0B,CAAC,EAAE,QAAA;AAG9E;;AAGA,SAAgB,eAAe,SAAyB;CACtD,MAAM,OAAO,QACV,YAAY,CAAC,CACb,WAAW,gBAAgB,GAAG,CAAC,CAC/B,WAAW,aAAa,EAAE;CAC7B,OAAO,eAAe,SAAS,KAAK,QAAQ;AAC9C;;AAGA,MAAM,YAAY,SAA0B,KAAK,WAAW,GAAG,IAAI,IAAI,KAAK,KAAK;AAEjF,SAAS,UACP,UACA,OAAO,OACP,UAKM,CAAC,GACC;CACR,MAAM,OAAO;CACb,MAAM,WAAW,IAAI,IAAI,SAAS,SAAS,UAAU,MAAM,YAAY,CAAC,CAAC,CAAC;CAC1E,MAAM,QAAQ;EACZ;EACA;EACA;EACA;EAGA,GAAI,OACA;GACE;GACA;GACA;GA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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["release.scope","release.dshVersion","release.packages","parse","eolOf","parse","parse","posix"],"sources":["../../../frame/packages.json","../src/pnpm-settings.ts","../src/ide-profile.ts","../src/version.ts","../src/frame-release.ts","../src/frame-rows.ts","../src/templates.ts","../src/lock.ts","../src/rewrite.ts","../src/library.ts","../src/project.ts","../src/init.ts","../src/mount.ts","../src/add.ts","../src/build-client.ts","../src/check-deps.ts","../src/frame-url.ts","../src/spawn.ts","../src/io.ts","../src/kv-import.ts","../src/preflight.ts","../src/app-registration.ts","../src/diff.ts","../src/legacy.ts","../src/upgrade.ts","../src/cli.ts"],"sourcesContent":["","/**\n * What an app in the ACRYL frame needs from pnpm to install and start as it does with npm, kept in the app's `pnpm-workspace.yaml` (npm never reads it).\n *\n * - `nodeLinker: hoisted`: the frame's host composes about a hundred DSH rows by package name from the app's own folder, which finds them only in a flat\n * `node_modules` (npm's layout). pnpm's isolated layout hides them, and the host stops with \"failed to import\" on every row.\n * - `allowBuilds`: pnpm 11 and later refuse to finish an install while a dependency's build script is neither allowed nor denied. The frame's packages\n * whose scripts run under npm (frame/packages.json `pnpm.allowBuilds`) are allowed, so pnpm installs what npm installs.\n *\n * The file is the person's too (pnpm itself writes `minimumReleaseAgeExclude` there), so it is edited by lines: a key they set is never changed, and\n * their comments and layout stay.\n */\nimport release from '../../../frame/packages.json' with { type: 'json' }\n\n/** The frame's dependencies whose build scripts pnpm must run. */\nexport const FRAME_ALLOW_BUILDS: readonly string[] = release.pnpm.allowBuilds\n\nexport const PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml'\n\n/** A top-level key line (`name:` at column 0), with or without a value after it. */\nfunction topKey(line: string): string | undefined {\n return /^([A-Za-z][\\w-]*):/u.exec(line)?.[1]\n}\n\n/** A YAML map key, quoted when it is not a plain word (a scoped package name). */\nfunction yamlKey(name: string): string {\n return /^[\\w.-]+$/u.test(name) ? name : `'${name}'`\n}\n\n/** The package names a top-level `allowBuilds:` block already decides, allowed or denied. */\nfunction decided(lines: readonly string[], start: number): { names: Set<string>; end: number } {\n const names = new Set<string>()\n // Right after the block's last entry: blank lines and comments after it belong to what follows.\n let end = start + 1\n for (let index = start + 1; index < lines.length; index += 1) {\n const line = lines[index] ?? ''\n if (line.trim() === '' || line.trimStart().startsWith('#')) continue\n if (!/^\\s/u.test(line)) break\n const key = /^\\s+(['\"]?)(.+?)\\1\\s*:/u.exec(line)?.[2]\n if (key !== undefined) names.add(key)\n end = index + 1\n }\n return { names, end }\n}\n\n/**\n * `pnpm-workspace.yaml` with the frame's settings added: the whole file when there is none, else only what it lacks. `builds` defaults to the frame's\n * list. Returns the text unchanged when nothing is missing.\n */\nexport function withFramePnpmSettings(\n text: string | undefined,\n builds: readonly string[] = FRAME_ALLOW_BUILDS,\n): string {\n const lines = text === undefined || text === '' ? [] : text.replace(/\\n$/u, '').split('\\n')\n const keys = new Set(lines.map(topKey).filter((key) => key !== undefined))\n if (!keys.has('nodeLinker')) {\n lines.push(\n '# The ACRYL frame finds its packages in a flat node_modules, as npm lays it out (acrylblends writes this; npm ignores the file).',\n 'nodeLinker: hoisted',\n )\n }\n const at = lines.findIndex((line) => topKey(line) === 'allowBuilds')\n if (at === -1) {\n if (builds.length > 0) {\n lines.push(\n \"# The frame's packages whose install scripts npm runs too; pnpm stops an install while a build is neither allowed nor denied.\",\n 'allowBuilds:',\n ...builds.map((name) => ` ${yamlKey(name)}: true`),\n )\n }\n } else {\n const { names, end } = decided(lines, at)\n const missing = builds.filter((name) => !names.has(name))\n // An inline value (`allowBuilds: {}`) is the person's to change: only a block is extended.\n if (missing.length > 0 && /^allowBuilds:\\s*(#.*)?$/u.test(lines[at] ?? ''))\n lines.splice(end, 0, ...missing.map((name) => ` ${yamlKey(name)}: true`))\n }\n const next = lines.length === 0 ? '' : `${lines.join('\\n')}\\n`\n return text !== undefined && next === text ? text : next\n}\n","// biome-ignore-all lint/suspicious/noTemplateCurlyInString: the launcher's source is written out as lines of text, and its template literals are meant\n/**\n * The host an app runs on inside the ACRYL frame: the app's own node server and loader composition, with DSH riding as the chat/session driver\n * (`docs/plans/ide-host-inversion.md`). The overlay (`cordis.patch.yml`) is the app's own layer: the frame's rows, the app's rows and the switches.\n * `scripts/ide-host.mjs` is the launcher: it composes the driver rows from DSH's base and web bundles with this app's layer and boots them on the\n * framework's node server. No DSH profile, no `dsh` launcher, no `dsh plugin add`. Generation is deterministic and idempotent: asking twice writes\n * the same bytes and the second call reports nothing changed, so a template or an upgrade can call it freely.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { PNPM_WORKSPACE_FILE, withFramePnpmSettings } from './pnpm-settings.ts'\n\n/** One package the frame mounts, with the spec the app installs it by (a range, or `file:` for a local checkout). */\nexport interface IdeProfilePackage {\n /** The package name as the overlay's row `name:` writes it. */\n readonly name: string\n /** What the app's `package.json` asks for. */\n readonly spec: string\n /** The Loader row id; defaults to the package name (the row-id rule). */\n readonly row?: string\n /** Leave this DSH row out of the composition (its functionality comes from the frame). */\n readonly disables?: readonly string[]\n /** A row of a package the app already installs (the framework's own subpaths): in the overlay, not in `dependencies`. */\n readonly installed?: boolean\n}\n\n/** Where an app keeps its data, for the plugins that inject `appInstance` (the framework's settings service does): the slice 0 row, kept in the app. */\nexport const APP_INSTANCE_ROW = `// Generated by acrylblends: the app's own folder family, for the plugins that inject appInstance.\nimport { fileURLToPath } from 'node:url'\nimport { selectInstance } from '../plugins/instance/index.ts'\n\nexport const name = 'app-instance'\n\nexport function apply(ctx) {\n ctx.effect(() => {\n // Chosen by the app's own family, as the host chose it: this folder, or the source a desktop product names. The engine home ACRYL's plugins read\n // (support serves \\`dshHome/logs\\`) is the app's home.\n const chosen = selectInstance({ projectRoot: fileURLToPath(new URL('..', import.meta.url)), env: process.env })\n const instance = ctx.reflect.provide('appInstance', chosen)\n return () => {\n void instance()\n }\n }, 'app-instance')\n}\n`\n\n/** The packages the frame mounts, in row order: the router pair, the host services, the frame's client plugins. */\nexport const FRAME_PACKAGES: readonly IdeProfilePackage[] = [\n { name: 'acrylblends/cordis/router', spec: '', row: 'acryl-router', installed: true },\n { name: 'acrylblends/cordis/router-http', spec: '', row: 'acryl-router-http', installed: true },\n { name: 'acryl-web', spec: '' },\n { name: 'acryl-settings', spec: '' },\n // No `acryl-shortcuts`: its Settings > Shortcuts and the command palette's key are the IDE's; the frame lists the shortcuts in General.\n { name: 'acryl-app-shell', spec: '' },\n { name: 'acryl-versions', spec: '' },\n // The page's root: the Blends frame (docs/design/blends-frame-mockup.html), never the IDE's workbench (`acryl-workspace`).\n { name: 'acryl-blends-frame', spec: '' },\n { name: 'acryl-plugin-admin', spec: '' },\n // The agent's prompt as ACRYL IDE's agent has it: the identity line first, every section a tagged block (pi.dev's shape), empties dropped.\n { name: 'acryl-system-prompt', spec: '' },\n // The app builder's docs, the way ACRYL IDE's agent has its plugin docs: a routed index, verified example plugins and the prompt line that sends\n // the agent to them before it writes a plugin.\n { name: 'acryl-app-context', spec: '' },\n { name: 'acryl-support', spec: '' },\n { name: 'acryl-sign-in', spec: '' },\n {\n name: 'dsh-client-ui-brand-acryl',\n spec: '',\n row: 'ui-brand-acryl',\n disables: ['ui-brand-official'],\n },\n]\n\n/**\n * Where every template's frame row mounts the app (`FRAME_MOUNT_PREFIX` in the template's own source, `/app`). The overlay tells the frame's row, so\n * the frame shows the app's page in its centre; `init-frame` asserts the templates still agree with this constant.\n */\nexport const FRAME_PAGE_PREFIX = '/app'\n\nexport interface IdeProfileInput {\n /** The app's name: its folder name, and the profile's suffix. */\n readonly appName: string\n /** `@deepseek-ai/dsh` exactly, never a range: DSH's APIs are pre-stable and a bump is a decision. */\n readonly dshVersion: string\n /** The framework's range or exact version. */\n readonly frameworkVersion: string\n /** What the frame mounts. Defaults to {@link FRAME_PACKAGES} with `spec` filled from `versions` or `file:`. */\n readonly packages?: readonly IdeProfilePackage[]\n /** Spec for one of the default packages, when it is not published yet. */\n readonly spec?: (name: string) => string | undefined\n /** Further packages the app installs (an unpublished frame package's own dependencies, in a checkout). */\n readonly dependencies?: Readonly<Record<string, string>>\n /** The app's own rows in the overlay (the file that mounts its routes at the frame prefix), in order; none for an app without one. */\n readonly rows?: readonly {\n readonly id: string\n readonly name: string\n readonly isolate?: Readonly<Record<string, true>>\n }[]\n /** Whether the app carries the door at `plugins/acryl-door/src/index.ts`: a row then mounts it, and an app without one simply has no door. */\n readonly door?: boolean\n}\n\n/** What generation writes, as text, so the same plan can be compared before writing. */\nexport interface IdeProfilePlan {\n /** The host's name for this app (one per app; used in the launcher's identity and logs). */\n readonly profile: string\n /** The app's own layer, as committed `cordis.patch.yml`. */\n readonly overlay: string\n /** The app's `package.json`, merged with what the host and the driver rows need. */\n readonly packageJson: string\n /** `cordis.yml`: the empty root entry list the launcher's loader composition starts from. */\n readonly rootConfig: string\n /** `scripts/ide-host.mjs`: composes the driver rows with this app's layer and boots them on the framework's node server. */\n readonly hostScript: string\n}\n\n/**\n * The driver rows the chat and session need, with what they hard-inject, and nothing else of DSH's base or web bundles\n * (`docs/plans/ide-host-inversion.md`, section 4). The composition check proves each one activates; a missing one is a loud failure, never a silent skip.\n */\nexport const DRIVER_ROWS: readonly string[] = [\n 'connection',\n 'typert',\n 'typert-loader',\n 'typert-gateway',\n 'session',\n 'session-persistence-jsonl',\n 'session-query-sqlite',\n 'session-projection',\n 'session-title',\n 'storage',\n 'storage-json',\n 'storage-domain',\n 'agent',\n 'agent-loop',\n 'agent-default-model',\n 'llm',\n 'llm-retry',\n 'llm-deepseek',\n 'llm-deepseek-account',\n // The multi-provider twin, mounted dormant: its OAuth flows (Claude, ChatGPT and the rest of the catalog) are what the\n // Models page's \"Sign in with an account\" rows list, and a sign-in there supplies its provider profiles - the workshop\n // guide tells attendees a subscription works, so the catalog has to be composed, not only the DeepSeek account.\n 'llm-pi-ai',\n 'deepseek-account',\n 'credentials',\n 'authorization',\n 'settings',\n 'config-editor',\n 'attachment-local',\n 'file-reference-local',\n 'session-controller',\n 'settings-controller',\n 'api-remotes',\n 'workspace',\n 'workspace-controller',\n 'tools',\n 'system-prompt',\n // The skills registry the preset's `skill-filesystem` fills from the app's `.agents/skills/` and its `tool-skill` reads (DSH keeps it on the host plane).\n 'skill',\n 'file-upload',\n 'fs-sandbox',\n 'sandbox-policy',\n 'commands',\n // What the agent's tools stand on: the file observation rule, the sandbox and approval policy, the shell and its environment, and background jobs. The\n // tools themselves are the frame's own agent preset below, not host rows.\n 'fs-observation-policy',\n 'sandbox',\n 'bash-sandbox',\n 'pwsh-sandbox',\n 'approval',\n 'permission',\n 'shell-env',\n 'jobs',\n // The chat's auto-save: `workspace-changes` diffs the working tree around every turn and says which files the agent changed (and announces a turn that\n // changed none by saying nothing); it runs git through `subprocess`. The frame's Host saves a version from that, and DSH's changed-files card\n // (`ui-deliverables`) shows the same event under the answer.\n 'subprocess',\n 'workspace-changes',\n // What ACRYL IDE's chat stands on beyond the conversation itself, from DSH's own web composition (the desktop app composes the same): the turn and\n // step stats under the composer (`session-stats`, `session-turn-outline`), Like and Dislike on an answer (`message-feedback`), the background jobs\n // roster (`job-controller`), the session header's \"Open in\" button (`open-in-app`), `/export` (`session-log-download`), session references in `@`\n // (`session-reference`) and the workspace file reads the changed-files card uses (`workspace-files`).\n 'message-feedback',\n 'session-stats',\n 'session-turn-outline',\n 'job-controller',\n 'workspace-files',\n 'open-in-app',\n 'session-log-download',\n 'session-reference',\n // The footer's tokens, cache hit and context % (`ui-chat`'s stats read the `tokenUsage` projection this meter keeps, on the host plane as DSH's web\n // bundle keeps it), and the questions a plan or a tool asks the person (`tool-ask-user` and `ui-user-questions` both stand on this service).\n 'token-meter',\n 'user-questions',\n]\n\n/**\n * The chat's agent preset. DSH's web bundle gives every session a preset (`standard`: every tool DSH has, subagents, workflows, skills and plan mode, each\n * needing host rows of its own), and a session without a loadable preset cannot start. The frame offers the agent what builds an app, and nothing more:\n * the shell, the file tools (read, write, edit, search) and background jobs, with the persona and the app folder's instructions. It is the registry's\n * default, so every chat in the frame uses it. A tool added here needs the host rows it stands on in `DRIVER_ROWS`.\n */\nexport const AGENT_PRESET_ID = 'acryl-blends'\n\n/**\n * The plugins the frame's preset takes from DSH's own `standard` preset, as DSH declares them (config and prompt included), so they follow every DSH\n * bump instead of being restated here: the app's skills (`skill-filesystem` finds `.agents/skills/` in the app folder, the skills `acrylblends` writes\n * there with every app, and `tool-skill` lets the agent load one), plan mode and its `/plan` (`planning`, which ui-plan's plan card and Plan tab show),\n * the To-dos list the composer's dock shows (`tool-todo`), the questions the agent can ask (`tool-ask-user`), and context compaction (`compaction`),\n * whose meter is the footer's context %.\n */\nexport const DSH_STANDARD_PLUGINS: readonly string[] = [\n 'skill-filesystem',\n 'tool-skill',\n 'planning',\n 'compaction',\n 'tool-ask-user',\n 'tool-todo',\n]\n\nexport const AGENT_PRESET_ROWS: readonly {\n readonly id: string\n readonly name: string\n readonly config: Readonly<Record<string, unknown>>\n}[] = [\n {\n id: 'agent-preset-registry',\n name: '@deepseek-ai/dsh-agent-preset-registry',\n config: { default: AGENT_PRESET_ID },\n },\n {\n id: `preset-${AGENT_PRESET_ID}`,\n name: '@deepseek-ai/dsh-agent-preset',\n config: {\n id: AGENT_PRESET_ID,\n order: 1,\n plugins: [\n {\n id: 'persona',\n name: '@deepseek-ai/dsh-persona',\n config: {\n prefix: 'You are a coding agent powered by the {{model}} model.',\n suffix: 'Your working directory is {{cwd}}.',\n },\n },\n {\n id: 'agent-instructions',\n name: '@deepseek-ai/dsh-agent-instructions',\n config: { maxBytes: 65536 },\n },\n // The loader evaluates `__jsExpr` (the `!!js` tag) when it composes the row.\n {\n id: 'tool-bash',\n name: '@deepseek-ai/dsh-tool-bash',\n disabled: { __jsExpr: \"process.platform === 'win32'\" },\n },\n {\n id: 'tool-pwsh',\n name: '@deepseek-ai/dsh-tool-pwsh',\n disabled: { __jsExpr: \"process.platform !== 'win32'\" },\n },\n { id: 'tool-fs', name: '@deepseek-ai/dsh-tool-fs' },\n {\n id: 'tool-fs-search',\n name: '@deepseek-ai/dsh-tool-fs-search',\n config: { sampleOverCapGlobResults: false },\n },\n { id: 'tool-jobs', name: '@deepseek-ai/dsh-tool-jobs' },\n // DSH_STANDARD_PLUGINS are appended here at boot, read from DSH's own `standard` preset (see the host script).\n ],\n },\n },\n]\n\n/**\n * The client rows the frame and the chat load: the client-module serving row plus exactly the client packages the frame's and the chat's\n * `dsh.client` inject lists name, as installed in the app, and the two services those rows wait for (`client-resources`, `ui-input-trigger`).\n * DSH's `ui-layout` client row is deliberately absent: the ACRYL frame registers the `layout` service itself (DSH's row would register it twice,\n * the failure the old overlay avoided by disabling the row); the only layout client row mounted is `acryl-app-shell`, so the document renders\n * the ACRYL frame.\n */\n/** One DSH client row of the frame's profile: a package, its Loader id, and the config it mounts with. */\nexport interface ClientRow {\n readonly id: string\n readonly name: string\n readonly config?: Readonly<Record<string, unknown>>\n}\n\nexport const CLIENT_ROWS: readonly ClientRow[] = [\n { id: 'client-modules', name: '@deepseek-ai/dsh-client-modules' },\n { id: 'client-locale', name: '@deepseek-ai/dsh-client-locale' },\n { id: 'client-shortcuts', name: '@deepseek-ai/dsh-client-shortcuts' },\n { id: 'ui-renderer', name: '@deepseek-ai/dsh-client-ui-renderer' },\n // Dark by default, as the Blends frame is drawn (docs/design/blends-frame-mockup.html); a Settings Appearance change still wins over it.\n { id: 'ui-theme', name: '@deepseek-ai/dsh-client-ui-theme', config: { preference: 'dark' } },\n { id: 'ui-settings', name: '@deepseek-ai/dsh-client-ui-settings' },\n // The settings surface itself (the dialog, its sections list and the trigger). Without this row the page has no Settings trigger or dialog at all:\n // ui-settings and ui-settings-models only contribute pages into it.\n { id: 'ui-settings-general', name: '@deepseek-ai/dsh-client-ui-settings-general' },\n // No `ui-settings-plugins` (DSH's \"Built-in plugins\"): the frame's Settings > Plugins lists everything the app is made of.\n { id: 'ui-settings-models', name: '@deepseek-ai/dsh-client-ui-settings-models' },\n { id: 'ui-session', name: '@deepseek-ai/dsh-client-ui-session' },\n // No `ui-sidebar`: the Blends frame draws the left panel itself (branches and versions), and DSH's left sidebar also claims the\n // `sidebar.settings` seat the frame's top bar renders DSH's Settings in. DSH's right sidebar is mounted as it is (`ui-sidebar-right`, the docking\n // surface the Plan, the changed-files review and a file an answer links to open in); the frame shows it as one of its right panel's tabs.\n { id: 'ui-sidebar-right', name: '@deepseek-ai/dsh-client-ui-sidebar-right' },\n // The right sidebar's document tab: a file an answer names opens in it (Markdown, code, HTML, PDF, plain text).\n { id: 'ui-sidebar-documentpreview', name: '@deepseek-ai/dsh-client-ui-sidebar-documentpreview' },\n { id: 'ui-commands', name: '@deepseek-ai/dsh-client-ui-commands' },\n { id: 'ui-workspace', name: '@deepseek-ai/dsh-client-ui-workspace' },\n { id: 'ui-conversation', name: '@deepseek-ai/dsh-client-ui-conversation' },\n // What the chat view draws: the transcript (`ui-chat`, which opens an answer's links through the `sidebarRight` the frame stands in for), the tool calls\n // inside it, the approval card of a tool that asks, and the files attached to a message. Without them a conversation shows the person's message and no answer.\n { id: 'ui-chat', name: '@deepseek-ai/dsh-client-ui-chat' },\n { id: 'ui-tool', name: '@deepseek-ai/dsh-client-ui-tool' },\n { id: 'ui-approval', name: '@deepseek-ai/dsh-client-ui-approval' },\n { id: 'ui-attachment', name: '@deepseek-ai/dsh-client-ui-attachment' },\n // The rest of ACRYL IDE's chat, the same DSH packages in the order DSH's web composition mounts them: the shortcuts surface, workflow runs, `/` skills,\n // subagents, `@` references, the jobs roster, the goal bar, Like and Dislike, the model and effort selector, the permission picker (Read Only, Workspace\n // Write, Full access), the agent-preset setting, the session log setting, user questions, the Trajectory tab, and the session header's \"Open in\".\n // The changed-files card under an answer and its review tab (`ui-deliverables`), and plan mode's composer control, plan card and Plan tab (`ui-plan`).\n { id: 'ui-shortcuts', name: '@deepseek-ai/dsh-client-ui-shortcuts' },\n { id: 'ui-workflow-run', name: '@deepseek-ai/dsh-client-ui-workflow-run' },\n { id: 'ui-skill', name: '@deepseek-ai/dsh-client-ui-skill' },\n { id: 'ui-subagent', name: '@deepseek-ai/dsh-client-ui-subagent' },\n { id: 'ui-reference', name: '@deepseek-ai/dsh-client-ui-reference' },\n { id: 'ui-jobs', name: '@deepseek-ai/dsh-client-ui-jobs' },\n { id: 'ui-goal', name: '@deepseek-ai/dsh-client-ui-goal' },\n { id: 'ui-message-feedback', name: '@deepseek-ai/dsh-client-ui-message-feedback' },\n { id: 'ui-model-selection', name: '@deepseek-ai/dsh-client-ui-model-selection' },\n { id: 'ui-permission', name: '@deepseek-ai/dsh-client-ui-permission-presets' },\n { id: 'ui-agent-preset', name: '@deepseek-ai/dsh-client-ui-agent-preset' },\n { id: 'ui-settings-session-log', name: '@deepseek-ai/dsh-client-ui-settings-session-log' },\n { id: 'ui-user-questions', name: '@deepseek-ai/dsh-client-ui-user-questions' },\n { id: 'ui-trajectory', name: '@deepseek-ai/dsh-client-ui-trajectory' },\n { id: 'ui-deliverables', name: '@deepseek-ai/dsh-client-ui-deliverables' },\n { id: 'ui-plan', name: '@deepseek-ai/dsh-client-ui-plan' },\n { id: 'ui-open-in-app', name: '@deepseek-ai/dsh-client-ui-open-in-app' },\n { id: 'client-resources', name: '@deepseek-ai/dsh-client-resources' },\n { id: 'ui-input-trigger', name: '@deepseek-ai/dsh-client-ui-input-trigger' },\n]\n\n/** The array literal a host script writes for its rows (`const clientRows = [...];`, JSON at two spaces). */\nconst CLIENT_ROWS_BLOCK = /const clientRows = (\\[[\\s\\S]*?\\n\\])/u\n\n/** The client rows a host script names, or undefined when its array cannot be read. */\nexport function parseHostClientRows(hostText: string): ClientRow[] | undefined {\n const match = CLIENT_ROWS_BLOCK.exec(hostText)\n if (match?.[1] === undefined) return undefined\n try {\n const parsed: unknown = JSON.parse(match[1])\n if (!Array.isArray(parsed)) return undefined\n return parsed.filter(\n (row): row is ClientRow =>\n row !== null &&\n typeof row === 'object' &&\n typeof (row as { id?: unknown }).id === 'string' &&\n typeof (row as { name?: unknown }).name === 'string',\n )\n } catch {\n return undefined\n }\n}\n\n/**\n * Add the client rows the current host script names and the app's host script does not, keeping every row the app has exactly as it is: a person's\n * own row, or one whose config they changed. The app's rows keep their order; the missing current rows follow; the rest of the host script is the\n * current one. An app without a host script (a new app) or with an unreadable array gets the current script as it is.\n * @param currentHost - the host script the CLI generates now.\n * @param appHost - the app's existing host script, when it has one.\n * @returns the text to write and the row names it added.\n */\nexport function mergeHostClientRows(\n currentHost: string,\n appHost: string | undefined,\n): { readonly text: string; readonly added: readonly string[] } {\n if (appHost === undefined) return { text: currentHost, added: [] }\n const current = parseHostClientRows(currentHost)\n const app = parseHostClientRows(appHost)\n if (current === undefined || app === undefined) return { text: currentHost, added: [] }\n const names = new Set(app.map((row) => row.name))\n const missing = current.filter((row) => !names.has(row.name))\n const block = CLIENT_ROWS_BLOCK.exec(currentHost)\n if (block === null) return { text: currentHost, added: [] }\n const merged = [...app, ...missing]\n return {\n text: currentHost.replace(block[0], `const clientRows = ${JSON.stringify(merged, null, 2)}`),\n added: missing.map((row) => row.name),\n }\n}\n\n/**\n * `scripts/ide-host.mjs`: the app's launcher. It boots the framework's node server and the webServer port the driver rows register on, then the frame\n * layer, and mounts DSH's driver rows from the base and web bundles (only the ids in `DRIVER_ROWS`) as ordinary rows of this composition. `--check`\n * audits the composition (every row active) and disposes it, for the gate.\n */\nfunction hostScriptOf(profile: string, frameModule: string): string {\n return [\n \"// Generated by acrylblends: boots this app inside the ACRYL frame on the app's own node server. Regenerate instead of editing.\",\n \"import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\",\n \"import { chmodSync } from 'node:fs'\",\n \"import { spawnSync } from 'node:child_process'\",\n \"import { randomBytes, timingSafeEqual } from 'node:crypto'\",\n \"import { createRequire } from 'node:module'\",\n \"import { dirname, isAbsolute, join, resolve } from 'node:path'\",\n \"import { fileURLToPath, pathToFileURL } from 'node:url'\",\n \"import { DEFAULT_SCHEMA, dump as dumpYaml, Type, load as parseYaml } from 'js-yaml'\",\n \"import { Acryl } from 'acrylblends'\",\n '',\n \"// The framework's files (this launcher, the composition it reads) sit next to it; the app is chosen by the app's own family: this folder, or the\",\n '// source a desktop product names when it runs the framework from its bundle. Its home and its port come with it, decided once.',\n \"const frameworkRoot = join(dirname(fileURLToPath(import.meta.url)), '..')\",\n \"const { selectInstance } = await import(new URL('../plugins/instance/index.ts', import.meta.url).href)\",\n 'const chosen = selectInstance({ projectRoot: frameworkRoot, env: process.env })',\n 'const appRoot = chosen.root',\n 'const home = chosen.home',\n `const profileName = '${profile}'`,\n \"// The page and its tab are named for the app: the package's name, as the frame's header says it, else the folder's.\",\n 'const appTitle = (() => {',\n ' try {',\n \" const name = JSON.parse(readFileSync(join(appRoot, 'package.json'), 'utf8')).name\",\n \" if (typeof name === 'string' && name.trim() !== '') return name.trim()\",\n ' } catch {}',\n ' return chosen.name',\n '})()',\n \"// DSH's rows keep their files in this app's own home: set before any driver row is imported.\",\n 'process.env.DSH_HOME = home',\n \"// Development only: the app's code can be loaded again in this host (a version gone back to, a file written by the chat, an agent or an editor). Every file\",\n \"// of the app's src/ then resolves under a generation the frame's Host raises, so nothing but the app's own rows restarts. Before anything of the app loads;\",\n '// a frame package that predates the hook leaves the code as it started.',\n 'try {',\n ` const { installAppGeneration } = await import('${frameModule}/app-generation')`,\n ' installAppGeneration({ appRoot })',\n '} catch (error) {',\n \" if (error?.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED' && error?.code !== 'ERR_MODULE_NOT_FOUND') throw error\",\n '}',\n '// Before any DSH module is imported: its home resolution reads the environment at import time.',\n \"const { boot } = await import('@deepseek-ai/dsh-app-boot')\",\n \"// The frame listens where the app's own server would: the folder's stable port (or the home's, when ACRYLBLENDS_HOME pins one). A restart then\",\n \"// keeps the page's origin, its auth cookie and its tabs; ACRYLBLENDS_PORT still decides, and the gates pass 0 for any free port.\",\n 'const port = String(chosen.port.start)',\n \"const check = process.argv.includes('--check')\",\n 'const require = createRequire(import.meta.url)',\n '/** The `!!js` tag the loader evaluates: js-yaml needs the same type the Cordis include plugin declares. */',\n \"const JsExpr = new Type('tag:yaml.org,2002:js', {\",\n \" kind: 'scalar',\",\n ' construct: (data) => ({ __jsExpr: data }),',\n ' represent: (data) => data.__jsExpr,',\n '})',\n 'const load = (text) => parseYaml(text, { schema: DEFAULT_SCHEMA.extend([JsExpr]) })',\n '',\n '/** An insert names its module beside the patch file that named it (the loader rule, and the rule DSH applies when it reads a profile again). The',\n ' * composition is read as the profile bundle, from its own directory, so a row written relative would turn into a bundle-relative path the first time',\n ' * DSH reconciles the profile (any Settings write) and the app rows would stop loading. Anchoring each layer at the directory it was written for',\n ' * keeps one row meaning one file: boot, a Settings write and the frame reload then import the same absolute URL. */',\n 'function anchorRows(patches, baseDir) {',\n ' const visit = (row) => {',\n \" if (typeof row.name === 'string' && (isAbsolute(row.name) || row.name.startsWith('./') || row.name.startsWith('../')))\",\n ' row.name = pathToFileURL(resolve(baseDir, row.name)).href',\n ' if (row.group && Array.isArray(row.config)) row.config.forEach(visit)',\n ' }',\n ' for (const patch of patches) patch.insert?.forEach(visit)',\n ' return patches',\n '}',\n '',\n '/** The driver rows above are selected from the bundles by id; nothing else of DSH is mounted. */',\n `const DRIVER_ROWS = ${JSON.stringify(DRIVER_ROWS, null, 2).replaceAll('\\n', '\\n')}`,\n '',\n \"/** A bundle's patch files, in application order, as the package declares them, anchored beside that package (a relative name in a bundle is its own file). */\",\n 'function bundlePatches(packageName) {',\n \" const dir = dirname(require.resolve(packageName + '/package.json'))\",\n \" const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'))\",\n ' const files = manifest.dsh?.bundle?.patch',\n \" const list = typeof files === 'string' ? [files] : Array.isArray(files) ? files : []\",\n \" return anchorRows(list.flatMap((file) => load(readFileSync(join(dir, file), 'utf8')) ?? []), dir)\",\n '}',\n '',\n '/** Keep only the driver rows: insert lists filtered entry by entry, id-targeted patches only when they hit a driver row. */',\n 'function onlyDriverRows(patches) {',\n ' const kept = []',\n ' for (const patch of patches) {',\n ' if (Array.isArray(patch.insert)) {',\n ' const insert = patch.insert.filter((entry) => DRIVER_ROWS.includes(entry.id))',\n ' if (insert.length > 0) kept.push({ ...patch, insert })',\n \" } else if (typeof patch.id === 'string' && DRIVER_ROWS.includes(patch.id)) kept.push(patch)\",\n ' }',\n ' return kept',\n '}',\n '',\n \"// The frame layer is the framework's file; its rows are anchored at the app, so the composition DSH keeps names the app's files, never the bundle's.\",\n \"const frameLayer = anchorRows(load(readFileSync(join(frameworkRoot, 'cordis.patch.yml'), 'utf8')) ?? [], appRoot)\",\n \"/** Our host rows first: the framework's node server, then the webServer port the driver rows register on (they are the app's, not DSH's). */\",\n `const clientRows = ${JSON.stringify(CLIENT_ROWS, null, 2).replaceAll('\\\\n', '\\\\n')}`,\n `const agentRows = ${JSON.stringify(AGENT_PRESET_ROWS, null, 2)}`,\n `const DSH_STANDARD_PLUGINS = ${JSON.stringify(DSH_STANDARD_PLUGINS)}`,\n \"/** One plugin row of DSH's `standard` preset, exactly as DSH's web bundle declares it; a missing one fails the boot loudly. */\",\n 'function dshStandardPlugin(id) {',\n \" for (const patch of bundlePatches('@deepseek-ai/dsh-web-app'))\",\n ' for (const entry of patch.insert ?? [])',\n \" if (entry.config?.id === 'standard') {\",\n ' const found = entry.config.plugins?.find((plugin) => plugin.id === id)',\n ' if (found !== undefined) return found',\n ' }',\n \" throw new Error(`DSH's standard preset has no ${id} row`)\",\n '}',\n `agentRows.find((row) => row.id === 'preset-${AGENT_PRESET_ID}').config.plugins.push(...DSH_STANDARD_PLUGINS.map(dshStandardPlugin))`,\n 'const hostLayer = anchorRows([{',\n ' insert: [',\n \" { id: 'node-server', name: './plugins/acryl-node-server/src/index.ts', config: { hostname: '127.0.0.1', port: Number(port), scan: Number(port) === 0 ? 0 : 20 } },\",\n \" { id: 'web-server', name: './plugins/acryl-web-server/src/index.ts' },\",\n \" { id: 'page', name: './plugins/acryl-page/src/index.ts', config: { title: appTitle } },\",\n ' ...clientRows,',\n ' ...agentRows,',\n ' ],',\n '}], appRoot)',\n \"// The composition: the driver rows selected from the bundles, the host rows and the app's frame layer, in the order they mount.\",\n 'const composition = [',\n \" ...onlyDriverRows(bundlePatches('@deepseek-ai/dsh-base')),\",\n \" ...onlyDriverRows(bundlePatches('@deepseek-ai/dsh-web-app')),\",\n ' ...hostLayer,',\n ' ...frameLayer,',\n ']',\n '',\n \"// The app's DSH profile, which DSH's Settings, config editor and plugin manager read and write (docs/plans/ide-host-inversion.md, section 20):\",\n '// - package.json: the profile manifest (no DSH bundles; the composition is ours), created once when the app has none.',\n '// - cordis.patch.yml: the composition, regenerated on every boot from the layers above, so an upgrade or a framework bump never leaves a stale copy.',\n '// - settings.patch.yml: the profile patch document ProfileContext.patchPath points at. Only the config editor writes it; the host creates it empty',\n '// when missing and never rewrites it, so the Settings edits of the app survive a boot, an upgrade and a framework bump.',\n 'mkdirSync(home, { recursive: true })',\n \"const manifestPath = join(home, 'package.json')\",\n '// The composition is a profile bundle: bundle layers are applied before the profile patch document, so a Settings edit always wins over a row the',\n '// composition also configures. DSH resolves it from the profile directory like any installed bundle, and the launcher rewrites it on every boot.',\n \"const bundleDir = join(home, 'node_modules', 'acrylblends-frame')\",\n 'mkdirSync(bundleDir, { recursive: true })',\n \"writeFileSync(join(bundleDir, 'package.json'), `${JSON.stringify({ name: 'acrylblends-frame', version: '0.0.0', private: true, dsh: { bundle: { patch: 'cordis.patch.yml' } } }, null, 2)}\\\\n`)\",\n 'const compositionHeader =',\n ' \"# Generated by acrylblends: the host composition of this app, a profile bundle. Rewritten on every boot from the launcher layers; do not edit. Settings live in settings.patch.yml.\\\\n\"',\n \"writeFileSync(join(bundleDir, 'cordis.patch.yml'), compositionHeader + dumpYaml(composition, { schema: DEFAULT_SCHEMA.extend([JsExpr]), noRefs: true }))\",\n \"writeFileSync(manifestPath, `${JSON.stringify({ name: profileName, private: true, dependencies: { 'acrylblends-frame': '0.0.0' }, dsh: { profile: { bundles: ['acrylblends-frame'] } } }, null, 2)}\\\\n`)\",\n \"const settingsPath = join(home, 'settings.patch.yml')\",\n \"if (!existsSync(settingsPath)) writeFileSync(settingsPath, '[]\\\\n')\",\n '',\n '// Boot reads the same list readProfilePatches will: the composition bundle first, then the settings document, which is also the order DSH uses for',\n '// bundles and the profile patch, so the first reconcile is a no-op recomposition and every later one differs only by the edited patch.',\n \"const compositionLayer = load(readFileSync(join(bundleDir, 'cordis.patch.yml'), 'utf8')) ?? []\",\n \"const settingsLayer = anchorRows(load(readFileSync(settingsPath, 'utf8')) ?? [], home)\",\n \"/** The profile context DSH's settings row, config editor and plugin manager read. */\",\n 'const profileContext = {',\n \" name: 'web',\",\n ' dir: home,',\n ' patchPath: settingsPath,',\n \" installAnchor: join(appRoot, 'package.json'),\",\n ' cwd: appRoot,',\n ' home,',\n ' startedBundles: [],',\n ' overlays: [],',\n ' telemetryDisabledEnv: process.env.DSH_TELEMETRY_DISABLED,',\n '}',\n 'const layers = [compositionLayer, settingsLayer]',\n 'let ctx',\n 'try {',\n \" ctx = await boot('\" +\n profile +\n \"', join(frameworkRoot, 'cordis.yml'), layers.flat(), (hostContext) => {\",\n \" hostContext.provide('profileContext', profileContext)\",\n \" }, pathToFileURL(join(appRoot, 'package.json')).href)\",\n '} catch (error) {',\n \" // A row that fails before it has a plugin (a module that is not one, an import that throws) is reported as 'failed to import' with no cause; the boot's\",\n ' // StartupError carries the original failures. Print each one, by row id and module, then fail: a silent half-composed host is never an option.',\n ' const entries = Array.isArray(error?.entries) ? error.entries : []',\n ' const failures = entries.filter(({ outcome }) => outcome?.kind === \"failed\")',\n ' console.error(`acrylblends host: startup failed (${String(failures.length)} row(s))`)',\n ' // Each entry is the boot report of one row: { id, module, required, fiberState, outcome }.',\n ' for (const { id, module, outcome } of failures) {',\n ' const cause = outcome.error',\n ' const detail = cause instanceof Error ? cause.stack ?? cause.message : String(cause)',\n ' console.error(`- ${String(id)} (${String(module)}): ${detail}`)',\n ' }',\n ' const cause = error?.cause',\n ' if (failures.length === 0 && cause !== undefined) console.error(cause instanceof Error ? cause.stack ?? cause.message : String(cause))',\n ' process.exit(1)',\n '}',\n \"const server = ctx.get('nodeServer')\",\n \"const connection = ctx.get('connection')\",\n '// The frame listens on a stable port; its launch token is made stable the same way: the address of the previous run is read back and the connection',\n '// is given that token, so a restart keeps the same URL and an open page - even one whose cookie was lost - comes back by reloading it. The record is',\n '// the launcher own bookkeeping (the installed framework may predate `acrylblends open`), so it is read and written here: owner-only (0600, and a',\n '// restricted ACL on Windows), a temp file renamed over the old one. The token lives in the connection instance field DSH reads on every check;',\n '// nothing in the pinned package is edited, and a connection without that field keeps the token the process minted.',\n \"const frameRecordPath = join(home, 'frame-url.json')\",\n 'function readFrameRecord() {',\n ' try {',\n ' return JSON.parse(readFileSync(frameRecordPath, \"utf8\"))',\n ' } catch {',\n ' return undefined',\n ' }',\n '}',\n 'function restrictFrameRecord(path) {',\n ' chmodSync(path, 0o600)',\n ' if (process.platform !== \"win32\" || !process.env.USERNAME) return',\n ' spawnSync(\"icacls\", [path, \"/inheritance:r\", \"/grant:r\", `${process.env.USERNAME}:F`], { stdio: \"ignore\", windowsHide: true })',\n '}',\n 'function writeFrameRecord(url) {',\n ' let token = \"\"',\n ' try {',\n ' token = new URL(url).searchParams.get(\"token\") ?? \"\"',\n ' } catch {}',\n ' mkdirSync(home, { recursive: true })',\n ' const temporary = `${frameRecordPath}.${String(process.pid)}.tmp`',\n ' writeFileSync(temporary, JSON.stringify({ version: 1, url, token, pid: process.pid, writtenAt: new Date().toISOString() }, null, 2) + \"\\\\n\", { mode: 0o600 })',\n ' restrictFrameRecord(temporary)',\n ' renameSync(temporary, frameRecordPath)',\n ' restrictFrameRecord(frameRecordPath)',\n '}',\n 'const connectionAuth = connection !== undefined && typeof connection === \"object\" ? connection.browserAuth : undefined',\n 'const previousAddress = readFrameRecord()',\n 'const reusedToken = connectionAuth !== undefined && typeof connectionAuth.launchToken === \"string\" && previousAddress !== undefined && typeof previousAddress.token === \"string\" && previousAddress.token !== \"\"',\n 'if (reusedToken) connectionAuth.launchToken = previousAddress.token',\n \"// The chat's remote RPC is token-authenticated: the printed URL carries the launch token, and the page exchange turns it into a cookie.\",\n 'const url = server === undefined ? undefined : connection === undefined ? server.url : connection.authenticatedUrl(server.url)',\n \"console.log(`acrylblends host: ${url ?? 'no node server'}`)\",\n '// `acrylblends open` prints the record back; a stopped host leaves it behind on purpose, so the command can say the frame is not running instead of',\n '// pretending the app never ran one.',\n 'if (url !== undefined) {',\n ' try {',\n ' writeFrameRecord(url)',\n ' } catch (error) {',\n ' console.error(`acrylblends host: could not write the frame address: ${String(error)}`)',\n ' }',\n ' // `acrylblends open --new` rotates the token in the running host: the stored token presents itself, the connection gets a new one, the new address is',\n ' // written and answered; every other request is refused.',\n ' const frameRouter = ctx.get(\"router\")',\n ' const frameRouterHttp = ctx.get(\"routerHttp\")',\n ' if (frameRouter !== undefined && frameRouterHttp !== undefined && connectionAuth !== undefined && typeof connectionAuth.launchToken === \"string\") {',\n ' const sameToken = (actual, expected) => {',\n ' const a = Buffer.from(String(actual), \"utf8\")',\n ' const b = Buffer.from(String(expected), \"utf8\")',\n ' return a.byteLength === b.byteLength && timingSafeEqual(a, b)',\n ' }',\n ' const rotate = () => {',\n ' connectionAuth.launchToken = randomBytes(32).toString(\"base64url\")',\n ' const next = connection.authenticatedUrl(server.url)',\n ' writeFrameRecord(next)',\n ' return next',\n ' }',\n ' frameRouter.route(\"/api/acryl-frame\", new Acryl().post(\"/token/new\", (c) => {',\n ' const presented = c.req.header(\"x-frame-token\") ?? \"\"',\n ' if (!sameToken(presented, connectionAuth.launchToken)) return c.text(\"forbidden\", 403)',\n ' return c.json({ url: rotate() })',\n ' }))',\n ' frameRouterHttp.mount(\"/api/acryl-frame\")',\n ' }',\n '}',\n 'if (previousAddress !== undefined && !reusedToken && url !== undefined) {',\n ' console.warn(\"acrylblends host: the stored frame address could not be reused on this boot; a new one was minted\")',\n '}',\n 'if (check) {',\n \" const loader = ctx.get('loader')\",\n ' const rows = [...loader.entries()]',\n ' const stalled = []',\n ' for (const row of rows) {',\n ' const state = row.fiber?.state',\n ' if (row.disabled) continue',\n \" if (state !== 2 && state !== 4) stalled.push(`${row.id} (${row.options.name}): ${state === undefined ? 'no fiber' : String(state)}`)\",\n ' }',\n ' const webRows = rows.filter((row) => /(^|\\\\/)(host-webserver|web-app|web-frontend)$/u.test(row.options.name))',\n ' if (stalled.length > 0 || webRows.length > 0) {',\n \" console.error('acrylblends host: composition check failed')\",\n ' for (const line of stalled) console.error(`- not active: ${line}`)',\n ' for (const row of webRows) console.error(`- a DSH web row is mounted: ${row.id} (${row.options.name})`)',\n ' process.exitCode = 1',\n ' } else {',\n ' console.log(`acrylblends host: every row active (${String(rows.length)} rows), no DSH web row mounted`)',\n ' }',\n ' // The Cordis way: a provider removed puts its consumer PENDING, restoring it activates it again; a reload adds nothing twice.',\n ' // The rows live under the include entry (the launcher mounts one config file), so an id is found by its last segment.',\n ' const findRow = (id) => rows.find((row) => row.id === id || row.id.endsWith(`:${id}`))',\n ' const stateOf = (id) => findRow(id)?.fiber?.state',\n \" await loader.update(findRow('credentials').id, { disabled: true })\",\n ' await loader.await()',\n \" const pendingAfterRemoval = stateOf('connection') === 0\",\n \" await loader.update(findRow('credentials').id, { disabled: false })\",\n ' await loader.await()',\n \" const activeAfterRestore = stateOf('connection') === 2\",\n ' for (let round = 0; round < 2; round += 1) {',\n \" await loader.update(findRow('session-title').id, { disabled: true })\",\n \" await loader.update(findRow('session-title').id, { disabled: false })\",\n ' await loader.await()',\n ' }',\n \" const reloaded = stateOf('session-title') === 2\",\n ' if (!pendingAfterRemoval || !activeAfterRestore || !reloaded) {',\n ' console.error(`acrylblends host: lifecycle check failed (pending ${String(pendingAfterRemoval)}, restored ${String(activeAfterRestore)}, reloaded ${String(reloaded)})`)',\n ' process.exitCode = 1',\n ' } else {',\n \" console.log('acrylblends host: PENDING on provider removal, active on restore, two reloads clean')\",\n ' }',\n ' await ctx.fiber.dispose()',\n '} else {',\n \" for (const signal of ['SIGINT', 'SIGTERM']) process.once(signal, () => { void ctx.fiber.dispose() })\",\n '}',\n '',\n ].join('\\n')\n}\n\n/** The row that roots the page (the Blends frame) and the module it is installed as: its source name in a checkout, its published name from npm. */\nexport const BLENDS_FRAME_ROW = 'acryl-blends-frame'\n\nfunction frameModuleOf(packages: readonly IdeProfilePackage[]): string {\n return (\n packages.find((entry) => (entry.row ?? entry.name) === BLENDS_FRAME_ROW)?.name ??\n BLENDS_FRAME_ROW\n )\n}\n\n/** `acrylblends-<app>`, safe for a DSH profile name: lowercase, dashes, nothing else. */\nexport function profileNameFor(appName: string): string {\n const slug = appName\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/gu, '-')\n .replaceAll(/^-+|-+$/gu, '')\n return `acrylblends-${slug === '' ? 'app' : slug}`\n}\n\n/** A package name as a YAML scalar: a scoped name starts with `@`, which YAML reserves at the start of a scalar, so it is quoted. */\nconst yamlName = (name: string): string => (name.startsWith('@') ? `'${name}'` : name)\n\nfunction overlayOf(\n packages: readonly IdeProfilePackage[],\n door = false,\n appRows: readonly {\n readonly id: string\n readonly name: string\n /** The Loader entry option: services this row's whole subtree provides in its own realm instead of the host's. */\n readonly isolate?: Readonly<Record<string, true>>\n }[] = [],\n): string {\n const rows = packages\n const disabled = new Set(packages.flatMap((entry) => entry.disables ?? []))\n const lines = [\n '# Generated by acrylblends: the DSH overlay that mounts the ACRYL frame. Regenerate instead of editing.',\n '- insert:',\n ' - id: app-instance',\n ' name: ./app/runtime.mjs',\n // The frame's index is not the app: the door drives the app's page in the canvas, never the frame around it (an agent's navigate would leave the frame).\n // The canvas prefix goes with it: `act navigate --path /board` reaches the app's /board at the canvas prefix, the same path it has on its own server.\n ...(door\n ? [\n ' - id: acryl-door',\n ' name: ./plugins/acryl-door/src/index.ts',\n ' config:',\n ' hostIndex: false',\n ` appBase: ${FRAME_PAGE_PREFIX}`,\n ]\n : []),\n ...appRows.flatMap((row) => [\n ` - id: ${row.id}`,\n ` name: ${row.name}`,\n ...(row.isolate === undefined ? [] : [` isolate: ${JSON.stringify(row.isolate)}`]),\n ]),\n ]\n for (const entry of rows) {\n lines.push(` - id: ${entry.row ?? entry.name}`, ` name: ${yamlName(entry.name)}`)\n // The app's page, which the frame shows in its centre.\n if ((entry.row ?? entry.name) === BLENDS_FRAME_ROW)\n lines.push(' config:', ` appPage: ${FRAME_PAGE_PREFIX}`)\n // The app is mounted under the canvas prefix, and a person (or a link) writes its root as `/app/` as often as `/app`: the frame's router reads a\n // trailing slash as the same path, so both answer the app's root and a relative link on it stays in the app.\n if ((entry.row ?? entry.name) === 'acryl-router')\n lines.push(' config:', ' trailingSlash: ignore')\n }\n // The chat driver's connection registers node routes on the framework's webServer port; it needs no DSH web runtime, so the row's\n // webRuntime inject and its expression are replaced by the one fact it needs here: which hosts the remote may trust.\n lines.push('- id: connection', ' inject: []', ' config:', \" trustedHosts: ['127.0.0.1']\")\n // The chat's first workspace lives in the app's own data home, not the user's Documents: the workspace controller creates the\n // default workspace under this directory, so a first chat is inside the app folder (which the workspace registers as its first\n // project) and a test run never writes into a real home.\n lines.push(\n '- id: workspace-controller',\n ' config:',\n ' documentsDirectory: !!js process.env.DSH_HOME',\n )\n for (const id of [...disabled].sort()) {\n lines.push(`- id: ${id}`, ' disabled: true')\n }\n return `${lines.join('\\n')}\\n`\n}\n\n/**\n * The files a fresh app's profile needs, without touching the disk: the overlay text and the app's `package.json` with the DSH pin, the framework, the\n * frame packages and a `dev` script on the app's own profile. An existing `package.json` keeps its keys; only the generated fields are set.\n */\nexport function ideProfilePlan(input: IdeProfileInput, existing?: string): IdeProfilePlan {\n const profile = profileNameFor(input.appName)\n const packages = (input.packages ?? FRAME_PACKAGES).map((entry) => ({\n ...entry,\n spec: entry.spec === '' ? (input.spec?.(entry.row ?? entry.name) ?? '') : entry.spec,\n }))\n const dependencies: Record<string, string> = {\n // DSH rides as the chat/session driver: the two bundles it is selected from, its boot glue and the generic Cordis loader; pinned exactly.\n // One Cordis for the whole composition: DSH 0.2.0-rc.2's own rows run on 4.0.5, and the framework's plugins accept it (^4.0.2).\n '@deepseek-ai/cordis': '4.0.5',\n '@deepseek-ai/cordis-plugin-loader': '~1.0.6',\n '@deepseek-ai/dsh-app-boot': input.dshVersion,\n '@deepseek-ai/dsh-base': input.dshVersion,\n '@deepseek-ai/dsh-web-app': input.dshVersion,\n acrylblends: input.frameworkVersion,\n 'js-yaml': '^4.2.0',\n ...input.dependencies,\n }\n for (const entry of packages)\n if (entry.installed !== true && entry.spec !== '') dependencies[entry.name] = entry.spec\n const manifest =\n existing === undefined\n ? { name: input.appName, private: true, type: 'module' }\n : JSON.parse(existing)\n return {\n profile,\n overlay: overlayOf(packages, input.door === true, input.rows ?? []),\n packageJson: `${JSON.stringify(mergeManifest(manifest, dependencies), null, 2)}\\n`,\n rootConfig: '[]\\n',\n hostScript: hostScriptOf(profile, frameModuleOf(packages)),\n }\n}\n\n/** The app's manifest with the generated fields set: dependencies merged (generated wins), the dev script on its own profile, keys in a stable order. */\nfunction mergeManifest(\n manifest: Record<string, unknown>,\n dependencies: Record<string, string>,\n): Record<string, unknown> {\n const sorted = Object.fromEntries(\n Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)),\n )\n // `dev` boots the launcher; `start` stays the app's own server (production).\n const scripts = {\n ...(manifest.scripts as Record<string, string> | undefined),\n dev: 'node --import ./plugins/acryl-node-server/src/register.ts scripts/ide-host.mjs',\n }\n return {\n ...manifest,\n dependencies: { ...(manifest.dependencies as Record<string, string> | undefined), ...sorted },\n scripts,\n }\n}\n\n/** What `applyIdeProfile` did: the files it wrote, and the ones already right. */\nexport interface IdeProfileWrite {\n readonly written: readonly string[]\n readonly unchanged: readonly string[]\n}\n\n/**\n * The first line of every file this module generates. `acrylblends upgrade` uses it to tell a file it may rewrite from one the app wrote (a hand-made\n * `cordis.patch.yml` for DSH, say): a file without the marker is refused, never overwritten.\n */\nexport const GENERATED_MARKER = /^(?:#|\\/\\/) Generated by acrylblends:/u\n\n/** The generated files, in the order this module writes them, with the plan field each one's text comes from. */\nconst GENERATED_FILES: readonly {\n readonly file: string\n readonly of: (plan: IdeProfilePlan) => string\n}[] = [\n { file: 'cordis.patch.yml', of: (plan) => plan.overlay },\n { file: 'cordis.yml', of: (plan) => plan.rootConfig },\n { file: 'scripts/ide-host.mjs', of: (plan) => plan.hostScript },\n { file: 'app/runtime.mjs', of: () => APP_INSTANCE_ROW },\n]\n\n/** What writing the profile into an existing app would do to one generated file. `conflict`: the file exists and acrylblends did not write it. */\nexport interface IdeProfileFileWrite {\n readonly file: string\n readonly text: string\n readonly state: 'add' | 'same' | 'update' | 'conflict'\n}\n\n/** The write plan for an app that already exists, computed before anything is touched so a refusal leaves the folder exactly as it was. */\nexport interface IdeProfileWritePlan {\n readonly plan: IdeProfilePlan\n /** The generated files, with what writing would do to each. */\n readonly files: readonly IdeProfileFileWrite[]\n /** Whether `package.json` changes once merged (a merge, never a conflict: the app owns that file). */\n readonly manifestChanged: boolean\n /** Client rows the app's host script did not name yet, which the write adds. */\n readonly clientRowsAdded: readonly string[]\n /** `pnpm-workspace.yaml` with the frame's pnpm settings merged in, or undefined when it already has them (a merge: the app owns that file too). */\n readonly pnpmWorkspace: string | undefined\n}\n\n/** The door is app source, not a registry package: the row exists when the app carries it, and nothing fails when it does not. */\nfunction withDoor(root: string, input: IdeProfileInput): IdeProfileInput {\n return { ...input, door: existsSync(join(root, 'plugins', 'acryl-door', 'src', 'index.ts')) }\n}\n\n/**\n * Plan writing the profile into `root`, which may already hold an app: a file this module generated (its first line carries the marker) is an `update`, a\n * file the app wrote is a `conflict` and must be refused by the caller, a missing file is an `add`. The bytes are exactly what `applyIdeProfile` writes.\n */\nexport function planIdeProfileWrite(root: string, input: IdeProfileInput): IdeProfileWritePlan {\n const manifestFile = join(root, 'package.json')\n const existing = existsSync(manifestFile) ? readFileSync(manifestFile, 'utf8') : undefined\n const plan = ideProfilePlan(withDoor(root, input), existing)\n const hostPath = join(root, 'scripts/ide-host.mjs')\n const appHost = existsSync(hostPath) ? readFileSync(hostPath, 'utf8') : undefined\n let clientRowsAdded: readonly string[] = []\n const files = GENERATED_FILES.map(({ file, of }) => {\n let text = of(plan)\n if (file === 'scripts/ide-host.mjs') {\n // The launcher updates as a whole; its rows are a merge, so a person's own row or a row they changed survives and only missing rows are added.\n const merged = mergeHostClientRows(text, appHost)\n text = merged.text\n clientRowsAdded = merged.added\n }\n const path = join(root, file)\n if (!existsSync(path)) return { file, text, state: 'add' as const }\n const before = readFileSync(path, 'utf8')\n if (before === text) return { file, text, state: 'same' as const }\n const firstLine = before.split('\\n', 1)[0] ?? ''\n return {\n file,\n text,\n state: GENERATED_MARKER.test(firstLine) ? ('update' as const) : ('conflict' as const),\n }\n })\n const workspaceFile = join(root, PNPM_WORKSPACE_FILE)\n const workspaceBefore = existsSync(workspaceFile)\n ? readFileSync(workspaceFile, 'utf8')\n : undefined\n const workspaceAfter = withFramePnpmSettings(workspaceBefore)\n return {\n plan,\n files,\n manifestChanged: existing !== plan.packageJson,\n clientRowsAdded,\n pnpmWorkspace: workspaceAfter === workspaceBefore ? undefined : workspaceAfter,\n }\n}\n\n/** Write a plan made by {@link planIdeProfileWrite}: only the files whose bytes differ, and `package.json` only when the merge changes it. */\nexport function writeIdeProfilePlan(root: string, planned: IdeProfileWritePlan): IdeProfileWrite {\n const written: string[] = []\n const unchanged: string[] = []\n for (const file of planned.files) {\n if (file.state === 'same') {\n unchanged.push(file.file)\n continue\n }\n const path = join(root, file.file)\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, file.text)\n written.push(file.file)\n }\n if (planned.manifestChanged) {\n writeFileSync(join(root, 'package.json'), planned.plan.packageJson)\n written.push('package.json')\n } else unchanged.push('package.json')\n if (planned.pnpmWorkspace !== undefined) {\n writeFileSync(join(root, PNPM_WORKSPACE_FILE), planned.pnpmWorkspace)\n written.push(PNPM_WORKSPACE_FILE)\n } else unchanged.push(PNPM_WORKSPACE_FILE)\n return { written, unchanged }\n}\n\n/**\n * Write the profile into `root`: `cordis.patch.yml` and the app's `package.json`. Idempotent: a file whose bytes are already the generated ones is not\n * touched, so a second run reports it under `unchanged` and the git working tree stays clean.\n */\nexport function applyIdeProfile(root: string, input: IdeProfileInput): IdeProfileWrite {\n mkdirSync(root, { recursive: true })\n return writeIdeProfilePlan(root, planIdeProfileWrite(root, input))\n}\n","import { createRequire } from 'node:module'\n\n/** The version of this package. */\nexport const VERSION: string = (\n createRequire(import.meta.url)('../package.json') as { version: string }\n).version\n\n/** Semver order for the release versions (numeric segments; a pre-release sorts before its release). */\nexport function compareVersions(a: string, b: string): number {\n const pa = a.split(/[.-]/u).map((part) => (Number.isNaN(Number(part)) ? part : Number(part)))\n const pb = b.split(/[.-]/u).map((part) => (Number.isNaN(Number(part)) ? part : Number(part)))\n for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {\n const x = pa[i] ?? 0\n const y = pb[i] ?? 0\n if (x < y) return -1\n if (x > y) return 1\n }\n return 0\n}\n","/**\n * Where an app's frame packages come from. npm by default: `frame/packages.json` (bundled into the CLI at build) names each frame package's published\n * name and version and the DSH release they are built against, and `init --desktop` installs those. `ACRYLBLENDS_FRAME_PACKAGES` overrides them: a JSON\n * set of specs for a checkout's unpublished build, or `none` for an app on the framework alone. An app that already carries frame packages upgrades from\n * its own pins.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport release from '../../../frame/packages.json' with { type: 'json' }\nimport { FRAME_PACKAGES, type IdeProfileInput, type IdeProfilePackage } from './ide-profile'\nimport { VERSION } from './version'\n\n/** One frame package as it publishes: its folder under `ide/` (a row package's folder is its source name), its published name, its version. */\nexport interface FrameReleasePackage {\n readonly source: string\n readonly name: string\n readonly version: string\n}\n\n/** The npm scope every frame package publishes under. */\nexport const FRAME_SCOPE: string = release.scope\n\n/** The DSH release the frame's packages pin as peers, so an app's DSH pin is the same one. */\nexport const FRAME_DSH_VERSION: string = release.dshVersion\n\n/** Every package the frame publishes, in the file's order. */\nexport const FRAME_RELEASE: readonly FrameReleasePackage[] = Object.entries(release.packages).map(\n ([source, entry]) => ({ source, name: `${release.scope}/${entry.name}`, version: entry.version }),\n)\n\n/**\n * The specs an app's frame installs: the DSH pin, the framework, and a spec per frame package keyed by its source name. `names` maps a source name to\n * the module it installs as when that differs (a published package); without an entry the module is the source name (a checkout's `file:` spec).\n */\nexport interface FrameProfileSpecs {\n readonly dshVersion: string\n readonly frameworkVersion: string\n readonly packages: Readonly<Record<string, string>>\n readonly names?: Readonly<Record<string, string>>\n}\n\n/** `ACRYLBLENDS_FRAME_PACKAGES=none`: the app runs on the framework alone, whatever the default would be. */\nexport const FRAMEWORK_ONLY = 'framework-only'\n\n/**\n * The frame's specs from npm: every row package by its published name at a caret over its released version, the DSH pin the packages are built against,\n * and this CLI's own release as the framework. A row package the file does not name is an error (the CLI tests catch it), never a silent skip.\n */\nexport function publishedFrameProfile(): FrameProfileSpecs {\n const bySource = new Map(FRAME_RELEASE.map((entry) => [entry.source, entry]))\n const packages: Record<string, string> = {}\n const names: Record<string, string> = {}\n for (const entry of FRAME_PACKAGES) {\n if (entry.installed === true) continue\n const published = bySource.get(entry.name)\n if (published === undefined)\n throw new Error(`frame/packages.json does not name the frame row ${entry.name}`)\n packages[entry.name] = `^${published.version}`\n names[entry.name] = published.name\n }\n return { dshVersion: FRAME_DSH_VERSION, frameworkVersion: `^${VERSION}`, packages, names }\n}\n\n/** `ACRYLBLENDS_FRAME_PACKAGES`: specs (a checkout's build), {@link FRAMEWORK_ONLY} for `none`, undefined when unset or malformed. */\nexport function frameProfileFromEnv(): FrameProfileSpecs | typeof FRAMEWORK_ONLY | undefined {\n const raw = process.env.ACRYLBLENDS_FRAME_PACKAGES\n if (raw === undefined || raw.trim() === '') return undefined\n if (raw.trim() === 'none') return FRAMEWORK_ONLY\n try {\n const parsed = JSON.parse(raw) as {\n dshVersion?: unknown\n frameworkVersion?: unknown\n packages?: unknown\n }\n if (typeof parsed.dshVersion !== 'string' || typeof parsed.frameworkVersion !== 'string')\n return undefined\n const packages: Record<string, string> = {}\n for (const [key, value] of Object.entries((parsed.packages ?? {}) as Record<string, unknown>))\n if (typeof value === 'string') packages[key] = value\n return { dshVersion: parsed.dshVersion, frameworkVersion: parsed.frameworkVersion, packages }\n } catch {\n return undefined\n }\n}\n\n/** The specs a new app's frame installs: the environment's, else npm's for a desktop app; undefined leaves the app on the framework alone. */\nexport function frameProfileForInit(desktop: boolean): FrameProfileSpecs | undefined {\n const fromEnv = frameProfileFromEnv()\n if (fromEnv === FRAMEWORK_ONLY) return undefined\n return fromEnv ?? (desktop ? publishedFrameProfile() : undefined)\n}\n\n/**\n * The specs for an app that may or may not be framed already: the environment first, then the app's own `package.json` (a framed app carries the DSH\n * pin, the framework and the frame packages, by their source or published names), then npm's for a desktop app (one with `plugins/desktop`).\n * Undefined when none applies: the caller then leaves the app on the framework alone and says why.\n */\nexport function frameProfileForApp(root: string): FrameProfileSpecs | undefined {\n const fromEnv = frameProfileFromEnv()\n if (fromEnv === FRAMEWORK_ONLY) return undefined\n if (fromEnv !== undefined) return fromEnv\n const fromApp = frameProfileOfManifest(root)\n if (fromApp !== undefined) return fromApp\n return existsSync(join(root, 'plugins', 'desktop')) ? publishedFrameProfile() : undefined\n}\n\nfunction frameProfileOfManifest(root: string): FrameProfileSpecs | undefined {\n const manifestFile = join(root, 'package.json')\n if (!existsSync(manifestFile)) return undefined\n let manifest: { dependencies?: Record<string, unknown> }\n try {\n manifest = JSON.parse(readFileSync(manifestFile, 'utf8')) as typeof manifest\n } catch {\n return undefined\n }\n const dependencies = manifest.dependencies ?? {}\n const dshVersion = dependencies['@deepseek-ai/dsh-base']\n const frameworkVersion = dependencies.acrylblends\n if (typeof dshVersion !== 'string' || typeof frameworkVersion !== 'string') return undefined\n const publishedOf = new Map(FRAME_RELEASE.map((entry) => [entry.source, entry.name]))\n const packages: Record<string, string> = {}\n const names: Record<string, string> = {}\n for (const entry of FRAME_PACKAGES) {\n const source = dependencies[entry.name]\n const publishedName = publishedOf.get(entry.name)\n const published = publishedName === undefined ? undefined : dependencies[publishedName]\n if (typeof source === 'string') packages[entry.name] = source\n else if (typeof published === 'string' && publishedName !== undefined) {\n packages[entry.name] = published\n names[entry.name] = publishedName\n }\n }\n if (Object.keys(packages).length === 0) return undefined\n return { dshVersion, frameworkVersion, packages, names }\n}\n\n/**\n * The profile input for an app from its specs: every frame row named by the module it installs as, its Loader row id kept as the source name (so a checkout\n * app and a published one have the same rows, and an upgrade between them renames nothing), and the specs as dependencies under the same module names.\n */\nexport function frameProfileInput(\n specs: FrameProfileSpecs,\n appName: string,\n rows: IdeProfileInput['rows'],\n): IdeProfileInput {\n const moduleOf = (source: string) => specs.names?.[source] ?? source\n const packages: IdeProfilePackage[] = FRAME_PACKAGES.map((entry) =>\n entry.installed === true\n ? entry\n : {\n ...entry,\n name: moduleOf(entry.name),\n row: entry.row ?? entry.name,\n spec: specs.packages[entry.name] ?? '',\n },\n )\n return {\n appName,\n dshVersion: specs.dshVersion,\n frameworkVersion: specs.frameworkVersion,\n packages,\n dependencies: Object.fromEntries(\n Object.entries(specs.packages).map(([source, spec]) => [moduleOf(source), spec]),\n ),\n ...(rows === undefined ? {} : { rows }),\n }\n}\n","/**\n * The rows the frame's layer needs for an app's own code, and the generated composition row that runs the app's plugins beside the host.\n *\n * A template with `src/frame.ts` gets the app's routes row. A template whose `src/app.ts` exports `composeInto` also gets `frame/app-plugins.mjs`, one row\n * that composes the app exactly as its own server does (same rows, order and config; `host: true` leaves the host's infrastructure rows out). The services\n * the app's own rows provide are read from their sources, never a hand list, and isolated through the Loader's own entry option, so the host's DSH rows keep\n * their copies of the same names.\n *\n * `init` writes these for a fresh app and `upgrade` writes them for an app made before the frame: one home for the row ids, the isolate rule and the\n * generated file.\n */\nimport { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { FRAME_PAGE_PREFIX } from './ide-profile'\n\n/** Rows the host mounts itself: an app's manifest may name them, but the generated layer does not. */\nexport const HOST_PROVIDED_IDS: ReadonlySet<string> = new Set([\n 'instance',\n 'acryl-router',\n 'acryl-node-server',\n 'acryl-web-server',\n 'acryl-page',\n 'manifest',\n 'save',\n 'acryl-door',\n])\n\n/**\n * Services the app's rows provide that the host's own rows call: the door and the frame reach `pluginShare`, and no host row provides that name. Left out of\n * the isolate list, so they find it; every other service stays in the app's realm.\n */\nconst REACHED_FROM_HOST: ReadonlySet<string> = new Set(['pluginShare'])\n\n/** One row of the generated layer. `isolate` is the Loader's entry option: the services this row's whole subtree provides in its own realm. */\nexport interface FrameRow {\n readonly id: string\n readonly name: string\n readonly isolate?: Readonly<Record<string, true>>\n}\n\n/** The generated `frame/app-plugins.mjs`, by the row that mounts it. */\nexport const APP_PLUGINS_FILE = 'frame/app-plugins.mjs'\n\n/**\n * The generated row source: the app's plugins composed beside the host, with the services they provide isolated (the Loader entry option). Every\n * route they mount lands under the frame's app prefix (`prefix`): a plugin's `ctx.router.route('/board')` answers `/app/board` in the canvas and\n * `/board` on the app's own server, so a plugin never writes the prefix. `@param prefix` is the canvas prefix, {@link FRAME_PAGE_PREFIX}.\n */\nexport function appPluginsRow(prefix: string): string {\n return [\n \"// Generated by acrylblends: the app's own plugins, composed as the app's server composes them (src/app.ts composeInto). The layer row isolates the\",\n \"// services this app provides (the Loader entry option), so the host's driver rows keep their own. Every route these rows mount lands under the\",\n `// frame's app prefix (${prefix}): a plugin's ctx.router.route('/board') answers /${prefix.replace(/^\\/+/u, '')}/board in the canvas and /board on the app's own server.`,\n \"import { composeInto } from '../src/app.ts'\",\n '',\n \"export const name = 'app-plugins'\",\n \"export const inject = ['appInstance', 'router']\",\n '',\n '/** The path a route lands at under the prefix: \"/\" is the prefix itself, a path already under it stays as it is. */',\n 'function prefixedAppRoute(prefix, path) {',\n \" const base = prefix.replace(/\\\\/+$/, '')\",\n \" if (base === '') return path\",\n \" if (path === '' || path === '/') return base\",\n \" return path === base || path.startsWith(base + '/') ? path : base + (path.startsWith('/') ? '' : '/') + path\",\n '}',\n '',\n \"/** The router as the app's rows see it: mounts land under the prefix, and a plugin's own request (its install check) finds them there; every other member is the host router's. */\",\n 'function appRouter(router, prefix) {',\n ' const route = (path, app) => router.route(prefixedAppRoute(prefix, path), app)',\n \" const request = (input, ...rest) => router.request(typeof input === 'string' && input.startsWith('/') ? prefixedAppRoute(prefix, input) : input, ...rest)\",\n ' return new Proxy(router, {',\n ' get(target, property) {',\n \" if (property === 'route') return route\",\n \" if (property === 'request') return request\",\n ' const value = Reflect.get(target, property, target)',\n \" return typeof value === 'function' ? value.bind(target) : value\",\n ' },',\n ' })',\n '}',\n '',\n 'export async function apply(ctx, config) {',\n ` const prefix = typeof config?.appPrefix === 'string' ? config.appPrefix : ${JSON.stringify(prefix)}`,\n \" // The rows read ctx.router as the app's own router: the same paths as on their own server, under the frame's prefix. A getter, read by each plugin\",\n ' // from its own context: it gets the host router traced for that context, so a route it mounts is its own and goes when the plugin is removed',\n \" // (a router taken once here would make every route this row's, served until the whole app reloads).\",\n \" const appScope = ctx.extend({ get router() { return appRouter(this.get('router'), prefix) } })\",\n ' const fibers = await composeInto(appScope, ctx.appInstance, { host: true })',\n \" ctx.effect(() => () => { for (const fiber of [...fibers].reverse()) void fiber.dispose() }, 'app-plugins: dispose')\",\n '}',\n '',\n ].join('\\n')\n}\n\n/** Whether an app's composition root is the one `composeInto` makes: the row can call it only then. */\nexport function carriesComposeInto(appTs: string): boolean {\n return appTs.includes('export async function composeInto')\n}\n\n/** The ids of the app's own rows (blend.yaml), without the ones the host provides. */\nfunction ownRowIds(appDir: string): string[] {\n const blend = join(appDir, 'blend.yaml')\n if (!existsSync(blend)) return []\n const ids: string[] = []\n for (const match of readFileSync(blend, 'utf8').matchAll(/^\\s*- id:\\s*(\\S+)\\s*$/gmu))\n if (!HOST_PROVIDED_IDS.has(match[1] as string)) ids.push(match[1] as string)\n return ids\n}\n\n/** The service names the app's own plugins provide, read from their sources. */\nfunction providedServices(appDir: string): Record<string, true> {\n const provided = new Set<string>()\n const walk = (folder: string): void => {\n if (!existsSync(folder)) return\n for (const entry of readdirSync(folder, { withFileTypes: true })) {\n const path = join(folder, entry.name)\n if (entry.isDirectory()) {\n walk(path)\n continue\n }\n if (!/\\.(ts|tsx|mjs)$/u.test(entry.name)) continue\n const text = readFileSync(path, 'utf8')\n for (const match of text.matchAll(/super\\(\\s*ctx\\s*,\\s*['\"]([\\w.-]+)['\"]/gu))\n provided.add(match[1] as string)\n for (const match of text.matchAll(/reflect\\.provide\\(\\s*['\"]([\\w.-]+)['\"]/gu))\n provided.add(match[1] as string)\n for (const match of text.matchAll(/ctx\\.provide\\(\\s*['\"]([\\w.-]+)['\"]/gu))\n provided.add(match[1] as string)\n }\n }\n for (const row of ownRowIds(appDir)) walk(join(appDir, 'plugins', row))\n return Object.fromEntries(\n [...provided]\n .filter((name) => !REACHED_FROM_HOST.has(name))\n .sort()\n .map((name) => [name, true as const]),\n )\n}\n\nexport interface AppFrameRows {\n /** The layer rows, in order: the app's plugins first, then its routes. Empty for an app with no `src/frame.ts`. */\n readonly rows: readonly FrameRow[]\n /** The generated row file's text, when the app's composition exports `composeInto`; the caller writes it. */\n readonly appPlugins: string | undefined\n}\n\n/**\n * The frame rows for an app: its routes row when it carries `src/frame.ts`, and the composition row when its `src/app.ts` exports `composeInto`.\n * `appTs` and `hasFrame` let a caller plan against the files it is about to write (an upgrade replacing the scaffold) rather than the ones on disk.\n */\nexport function appFrameRows(\n appDir: string,\n options: { appTs?: string; hasFrame?: boolean } = {},\n): AppFrameRows {\n if (options.hasFrame !== true && !existsSync(join(appDir, 'src', 'frame.ts')))\n return { rows: [], appPlugins: undefined }\n const appTs = options.appTs ?? readAppTs(appDir)\n const composes = appTs !== undefined && carriesComposeInto(appTs)\n const rows: FrameRow[] = []\n if (composes) {\n const isolate = providedServices(appDir)\n rows.push({\n id: 'app-plugins',\n name: `./${APP_PLUGINS_FILE}`,\n ...(Object.keys(isolate).length === 0 ? {} : { isolate }),\n })\n }\n rows.push({ id: 'app-routes', name: './src/frame.ts' })\n return { rows, appPlugins: composes ? appPluginsRow(FRAME_PAGE_PREFIX) : undefined }\n}\n\nfunction readAppTs(appDir: string): string | undefined {\n const file = join(appDir, 'src', 'app.ts')\n return existsSync(file) ? readFileSync(file, 'utf8') : undefined\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** The templates, all Cordis apps: a web app with a page and a client component (nodejs), a notes app and a chat app on the db port (notes, chat), a blank app on the router, a todo list on the router and state, and the blank app with the offline agent. */\nexport const TEMPLATES = ['blank', 'nodejs', 'notes', 'chat', 'todo', 'agent'] as const\nexport type Template = (typeof TEMPLATES)[number]\n\n/** One line per template: the single list `acrylblends init` and `create-acrylblends` both show (`create-acrylblends` tests that its own copy equals this one). */\nexport const TEMPLATE_SUMMARY: Record<(typeof TEMPLATES)[number], string> = {\n blank: 'the router, a server and one route: the smallest app',\n nodejs: 'a web app with a page and a client component',\n notes: 'a multi-user notes app on the db port (sign-up, log-in, sessions)',\n chat: 'a chat app that streams an answer over server-sent events',\n todo: 'a to-do list on the router and state',\n agent: 'the blank app with an offline agent that builds plugins into the running app',\n}\n\n/** Names that were templates once and still work: `create-acrylblends --template acryl-app` made the agent app. */\nexport const TEMPLATE_ALIASES: Record<string, Template> = { 'acryl-app': 'agent' }\n\nexport function isTemplate(value: string): value is Template {\n return (TEMPLATES as readonly string[]).includes(value)\n}\n\n/** The template a name means: itself, or what an old name stands for; undefined for anything else. */\nexport function resolveTemplate(value: string): Template | undefined {\n return isTemplate(value) ? value : TEMPLATE_ALIASES[value]\n}\n\n/**\n * The `templates/` folder of this package. The module sits at different depths in source (`src/`) and in the build\n * (`dist/`), so walk up until a `templates` folder holds the `blank` template.\n */\nexport function templatesRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'templates')\n if (existsSync(join(candidate, 'blank'))) return candidate\n const parent = dirname(dir)\n if (parent === dir) throw new Error('acrylblends cannot find its templates folder')\n dir = parent\n }\n}\n\n/** Absolute path of a template folder. */\nexport function templateDir(template: Template): string {\n return join(templatesRoot(), template)\n}\n\n/** The text every generated app's `AGENTS.md` starts with (`shared/`, beside `templates/`). */\nexport function sharedDir(): string {\n return join(dirname(templatesRoot()), 'shared')\n}\n\n/**\n * The framework's docs, as ACRYL wrote them: `docs/framework` of the repository, or the copy `prepack` puts inside the package. The module sits at\n * different depths in source and in the build, so walk up until a `docs/framework` folder holds the README.\n */\nexport function frameworkDocsRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'docs', 'framework')\n if (existsSync(join(candidate, 'README.md'))) return candidate\n const parent = dirname(dir)\n if (parent === dir)\n throw new Error('acrylblends cannot find the framework docs (docs/framework)')\n dir = parent\n }\n}\n","/**\n * What an app knows about the library plugins it carries: `plugins/library.lock.json` records, for every library plugin copied into the app, the version it came from and a hash of each file\n * as it was copied. `acrylblends upgrade` reads it to tell a file nobody touched (safe to replace) from one the app edited (never replaced without `--force`). An app made before the lock existed\n * has none; for it `upgrade` falls back to the hashes of the released versions (`shared/library-history.json`, made by `scripts/build-library-history.mjs`).\n */\nimport { createHash } from 'node:crypto'\nimport { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { sharedDir } from './templates'\nimport { VERSION } from './version'\n\nexport const LOCK_FILE = 'library.lock.json'\n\nexport interface Lock {\n version: number\n plugins: Record<string, { from: string; files: Record<string, string> }>\n}\n\n/** A file's identity: a hash of its text with line endings normalized (a checkout may have changed them), cut to 20 hex digits. */\nexport function digest(content: Buffer | string): string {\n const text = typeof content === 'string' ? content : content.toString('utf8')\n return createHash('sha256').update(text.replace(/\\r\\n/gu, '\\n')).digest('hex').slice(0, 20)\n}\n\n/** Every file below `dir`, as `relative/path` (forward slashes) to its {@link digest}. */\nexport function hashFolder(dir: string): Record<string, string> {\n const files: Record<string, string> = {}\n const walk = (folder: string, inside: string): void => {\n for (const entry of readdirSync(folder).sort()) {\n const path = join(folder, entry)\n const name = inside === '' ? entry : `${inside}/${entry}`\n if (statSync(path).isDirectory()) walk(path, name)\n else files[name] = digest(readFileSync(path))\n }\n }\n if (existsSync(dir)) walk(dir, '')\n return files\n}\n\nexport function readLock(pluginsDir: string): Lock {\n const file = join(pluginsDir, LOCK_FILE)\n if (!existsSync(file)) return { version: 1, plugins: {} }\n try {\n const lock = JSON.parse(readFileSync(file, 'utf8')) as Lock\n return { version: 1, plugins: lock.plugins ?? {} }\n } catch {\n return { version: 1, plugins: {} }\n }\n}\n\n/** Record that `name` was copied from this version of the library, with the hashes of the files as they now are. */\nexport function recordLock(pluginsDir: string, name: string, files: Record<string, string>): void {\n const lock = readLock(pluginsDir)\n lock.plugins[name] = { from: VERSION, files }\n const sorted = Object.fromEntries(\n Object.entries(lock.plugins).sort(([a], [b]) => a.localeCompare(b)),\n )\n writeFileSync(\n join(pluginsDir, LOCK_FILE),\n `${JSON.stringify({ version: 1, plugins: sorted }, null, 2)}\\n`,\n )\n}\n\n/** Plugin to file to the hashes that file has had in released versions. */\nexport type History = Record<string, Record<string, string[]>>\n\nexport function readHistory(): History {\n const file = join(sharedDir(), 'library-history.json')\n if (!existsSync(file)) return {}\n return JSON.parse(readFileSync(file, 'utf8')) as History\n}\n","import { dirname, posix } from 'node:path'\n\n/**\n * The framework plugins import each other by package name (`acryl-router`, `acryl-router/middleware`): inside this repository they are workspace packages. An app has\n * no such packages, only the copied source, so `acrylblends init` and `add` rewrite those specifiers to relative paths when they copy a plugin. The file each name stands\n * for is the `entries` table of the plugin's `plugin.json`.\n */\n\n/** `acryl-router/middleware` to `acryl-router/src/middleware/index.ts`: every importable name of every library plugin. */\nexport type EntryTable = ReadonlyMap<string, string>\n\nexport function entryTable(\n library: ReadonlyMap<string, { readonly entries?: Readonly<Record<string, string>> | undefined }>,\n): EntryTable {\n const table = new Map<string, string>()\n for (const [plugin, { entries }] of library)\n for (const [subpath, file] of Object.entries(entries ?? {}))\n table.set(`${plugin}${subpath}`, `${plugin}/${file}`)\n return table\n}\n\n/** For every character of a TypeScript source: 0 = code, 1 = inside a comment, 2 = inside a string or template literal. */\nfunction classify(source: string): Uint8Array {\n const kind = new Uint8Array(source.length)\n let i = 0\n while (i < source.length) {\n const c = source[i]\n const next = source[i + 1]\n if (c === '/' && next === '/') {\n const end = source.indexOf('\\n', i)\n const stop = end === -1 ? source.length : end\n kind.fill(1, i, stop)\n i = stop\n } else if (c === '/' && next === '*') {\n const end = source.indexOf('*/', i + 2)\n const stop = end === -1 ? source.length : end + 2\n kind.fill(1, i, stop)\n i = stop\n } else if (c === \"'\" || c === '\"' || c === '`') {\n let j = i + 1\n while (j < source.length && source[j] !== c) {\n if (source[j] === '\\\\') j += 1\n else if (c !== '`' && source[j] === '\\n') break\n j += 1\n }\n kind.fill(2, i, Math.min(j + 1, source.length))\n i = j + 1\n } else i += 1\n }\n return kind\n}\n\nconst SPECIFIER =\n /(\\bfrom\\s+|\\bimport\\s*\\(\\s*|\\bimport\\s+)(['\"])(acryl-[A-Za-z0-9-]+(?:\\/[A-Za-z0-9./-]+)?)\\2/gu\n\n/**\n * Rewrite the specifiers that name another library plugin to relative paths. `file` is the path of the file inside the plugins folder (for example\n * `acryl-state/src/service.ts`). Only real import and export statements are touched: a specifier named in a comment or in a string (a documentation example) is text,\n * not a dependency. Any other `acryl-*` specifier in code is an error: a library plugin may depend only on the others and on public packages.\n */\nexport function rewriteSpecifiers(source: string, file: string, entries: EntryTable): string {\n const kind = classify(source)\n return source.replace(\n SPECIFIER,\n (match, lead: string, quote: string, specifier: string, offset: number) => {\n if (kind[offset] !== 0) return match\n const target = entries.get(specifier)\n if (target === undefined)\n throw new Error(`${file}: imports \"${specifier}\", which is not a library plugin`)\n let path = posix.relative(dirname(file), target)\n if (!path.startsWith('.')) path = `./${path}`\n return `${lead}${quote}${path}${quote}`\n },\n )\n}\n","import { cpSync, existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, join, sep } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { hashFolder, recordLock } from './lock'\nimport { type EntryTable, entryTable, rewriteSpecifiers } from './rewrite'\n\n/** One entry of the plugin library: `plugins/<name>/plugin.json` in this repository. */\nexport interface LibraryPlugin {\n readonly name: string\n readonly description: string\n /** Library plugins this one imports; they are copied with it. */\n readonly requires: readonly string[]\n /** npm packages an app needs at run time for this plugin. */\n readonly dependencies: Readonly<Record<string, string>>\n /** npm packages an app needs only to develop or run this plugin's tooling (for example `electron` for the desktop shell). */\n readonly devDependencies?: Readonly<Record<string, string>>\n /** `package.json` scripts the plugin adds; a script the app already has is kept. */\n readonly scripts?: Readonly<Record<string, string>>\n /** Lines the app's `.gitignore` needs for this plugin (added when missing, if the app has a `.gitignore`). */\n readonly gitignore?: readonly string[]\n /** The port's conformance suite, a path inside the plugin: shipped in the package, but copied into an app only by `acrylblends add <plugin> --suite` (it needs vitest). */\n readonly suite?: string\n /** What other library plugins may import from this one: the import name after the plugin's own (`''`, `/middleware`) and the file it stands for. Used to rewrite `acryl-*` imports when a plugin is copied. */\n readonly entries?: Readonly<Record<string, string>>\n /** `package.json` \"imports\" entries the app gets (`#acryl-kv`): a path below the plugins folder. A plugin of your own imports the port by that name, in tests and the type-check as well as when installed live. */\n readonly imports?: Readonly<Record<string, string>>\n /** How to mount it, shown after `acrylblends add`. */\n readonly mount?: string\n /**\n * Paths inside the plugin folder that are not copied into an app (for example a React hook an app without React cannot type-check, or colocated test\n * files). A pattern is an exact path or a glob: `*` matches within one folder name, `**` across folders, `?` one character; `**` followed by a slash also matches no folder.\n */\n readonly omit?: readonly string[]\n}\n\n/** A glob as a regular expression: `*` within a name, `**` across folders, `**` plus a slash may match no folder, `?` one character. */\nfunction globToRegExp(glob: string): RegExp {\n let source = ''\n for (let i = 0; i < glob.length; i += 1) {\n const c = glob[i] as string\n if (c === '*' && glob[i + 1] === '*') {\n if (glob[i + 2] === '/') {\n source += '(?:.*/)?'\n i += 2\n } else {\n source += '.*'\n i += 1\n }\n } else if (c === '*') source += '[^/]*'\n else if (c === '?') source += '[^/]'\n else source += c.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n }\n return new RegExp(`^${source}$`)\n}\n\n/** Whether a path inside a plugin folder (relative, any separator) is named by one of its `omit` patterns. */\nexport function isOmitted(inside: string, patterns: readonly string[] | undefined): boolean {\n if (!patterns || patterns.length === 0) return false\n const path = inside.split(sep).join('/')\n return patterns.some((pattern) =>\n /[*?]/.test(pattern) ? globToRegExp(pattern).test(path) : pattern === path,\n )\n}\n\n/** Files of a library plugin that stay in the repository: its manifest and its tests. */\nconst LIBRARY_ONLY = new Set(['plugin.json', 'test', 'tests'])\n\nexport class LibraryError extends Error {\n override name = 'LibraryError'\n}\n\n/** The library folder (`plugins/` at the repository root): walk up from this module, in source or in the build. */\nexport function libraryRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'plugins')\n if (existsSync(join(candidate, 'instance', 'plugin.json'))) return candidate\n const parent = dirname(dir)\n if (parent === dir)\n throw new LibraryError('acrylblends cannot find its plugin library (plugins/)')\n dir = parent\n }\n}\n\n/** Every plugin in the library, by name. */\nexport function readLibrary(root: string = libraryRoot()): Map<string, LibraryPlugin> {\n const plugins = new Map<string, LibraryPlugin>()\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n const manifest = join(root, entry.name, 'plugin.json')\n if (!entry.isDirectory() || !existsSync(manifest)) continue\n const plugin = JSON.parse(readFileSync(manifest, 'utf8')) as LibraryPlugin\n if (plugin.name !== entry.name)\n throw new LibraryError(`plugins/${entry.name}/plugin.json names \"${plugin.name}\"`)\n plugins.set(plugin.name, {\n ...plugin,\n requires: plugin.requires ?? [],\n dependencies: plugin.dependencies ?? {},\n })\n }\n return plugins\n}\n\n/** The plugins to copy for `names`: each one after everything it requires, no duplicates. */\nexport function closure(\n names: readonly string[],\n library: ReadonlyMap<string, LibraryPlugin>,\n): LibraryPlugin[] {\n const ordered: LibraryPlugin[] = []\n const visiting = new Set<string>()\n const visit = (name: string, trail: string[]) => {\n if (ordered.some((plugin) => plugin.name === name)) return\n const plugin = library.get(name)\n if (!plugin)\n throw new LibraryError(\n `no plugin \"${name}\" in the library${trail.length ? ` (required by ${trail.at(-1)})` : ''}`,\n )\n if (visiting.has(name))\n throw new LibraryError(\n `plugins require each other in a cycle: ${[...trail, name].join(' -> ')}`,\n )\n visiting.add(name)\n for (const required of plugin.requires) visit(required, [...trail, name])\n visiting.delete(name)\n ordered.push(plugin)\n }\n for (const name of names) visit(name, [])\n return ordered\n}\n\n/** The one folder every plugin of an app lives in, library copies and the ones an agent writes. */\nexport const PLUGINS_DIR = 'plugins'\n/** Where apps made before 0.7.8 kept library copies; still understood for one release. */\nexport const LEGACY_PLUGINS_DIR = join('src', 'plugins')\n/** The list of library copies in the plugins folder; the extensions plugin installs every other folder there. */\nexport const LIBRARY_LIST = 'library.json'\n\n/** The plugins folder of an app: `plugins/`, or `src/plugins/` for an app that still has the old layout (and no `plugins/`). */\nexport function pluginsFolder(appDir: string): { dir: string; legacy: boolean } {\n const legacy =\n existsSync(join(appDir, LEGACY_PLUGINS_DIR)) && !existsSync(join(appDir, PLUGINS_DIR))\n return { dir: join(appDir, legacy ? LEGACY_PLUGINS_DIR : PLUGINS_DIR), legacy }\n}\n\n/** The warning for an app with the old layout. */\nexport const LEGACY_WARNING =\n 'warning: this app keeps its plugins in src/plugins/ (and extensions/); plugins now live in one folder, plugins/. Move them with \"git mv src/plugins plugins\", fix the paths in tsconfig.json, package.json and src/, and see docs/framework/agents/index.md. The old layout is read for this release only.'\n\n/** List `name` among the library copies of a plugins folder (`library.json`). */\nexport function recordLibraryCopy(folder: string, name: string): void {\n const file = join(folder, LIBRARY_LIST)\n const names: string[] = existsSync(file)\n ? (JSON.parse(readFileSync(file, 'utf8')) as { plugins: string[] }).plugins\n : []\n if (!names.includes(name)) names.push(name)\n writeFileSync(file, `${JSON.stringify({ plugins: names.sort() }, null, 2)}\\n`)\n}\n\n/** Copy one library plugin's source into `<app>/plugins/<name>/`. Throws when that folder already exists. */\nexport function copyPlugin(\n plugin: LibraryPlugin,\n appDir: string,\n root: string = libraryRoot(),\n): string {\n const folder = pluginsFolder(appDir)\n const target = join(folder.dir, plugin.name)\n if (existsSync(target))\n throw new LibraryError(\n `${relativeName(appDir, target)} already exists; remove it first to copy a fresh one`,\n )\n const source = join(root, plugin.name)\n // A framework package (it has a package.json in the repository) is copied as its source: `src/` and the licence; the package shipped by npm already holds only that. Its\n // `plugin.json` lists `entries`, and the imports of such a plugin are rewritten to relative paths, in the repository and in the installed package alike.\n const isPackage = existsSync(join(source, 'package.json'))\n const entries = plugin.entries === undefined ? undefined : entryTable(readLibrary(root))\n cpSync(source, target, {\n recursive: true,\n filter: (path) => {\n const inside = path\n .slice(source.length + 1)\n .split(sep)\n .join('/')\n if (\n isPackage &&\n inside !== '' &&\n inside !== 'LICENSE' &&\n inside !== 'src' &&\n !inside.startsWith('src/')\n )\n return false\n return (\n !(dirname(path) === source && LIBRARY_ONLY.has(inside)) &&\n !isOmitted(inside, plugin.omit) &&\n inside !== plugin.suite\n )\n },\n })\n if (entries) rewriteTree(join(target, 'src'), plugin.name, 'src', entries)\n if (!folder.legacy) {\n recordLibraryCopy(folder.dir, plugin.name)\n // The hashes of the files as copied: `acrylblends upgrade` tells an untouched file from an edited one by them.\n recordLock(folder.dir, plugin.name, hashFolder(target))\n }\n return target\n}\n\n/** Rewrite the `acryl-*` imports of every TypeScript file below `dir` (the plugin's `src/`) to relative paths. */\nfunction rewriteTree(dir: string, plugin: string, inside: string, entries: EntryTable): void {\n for (const entry of readdirSync(dir)) {\n const path = join(dir, entry)\n if (statSync(path).isDirectory()) rewriteTree(path, plugin, `${inside}/${entry}`, entries)\n else if (/\\.(?:ts|tsx|mts)$/u.test(entry))\n writeFileSync(\n path,\n rewriteSpecifiers(readFileSync(path, 'utf8'), `${plugin}/${inside}/${entry}`, entries),\n )\n }\n}\n\n/** Copy one file of a library plugin (a conformance suite) with its `acryl-*` imports rewritten, as `copyPlugin` does for the rest. */\nexport function copyRewritten(\n from: string,\n to: string,\n plugin: string,\n inside: string,\n root: string = libraryRoot(),\n): void {\n writeFileSync(\n to,\n rewriteSpecifiers(\n readFileSync(from, 'utf8'),\n `${plugin}/${inside}`,\n entryTable(readLibrary(root)),\n ),\n )\n}\n\nconst relativeName = (appDir: string, path: string) =>\n path\n .slice(appDir.length + 1)\n .split(sep)\n .join('/')\n","import {\n cpSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from 'node:fs'\nimport { join } from 'node:path'\nimport { compareVersions, VERSION } from './version'\n\n/** Thrown when the target directory exists and is not empty. Nothing has been written. */\nexport class TargetNotEmptyError extends Error {\n constructor(readonly target: string) {\n super(\n `refusing to write into \"${target}\": the directory is not empty. Choose an empty or new directory.`,\n )\n this.name = 'TargetNotEmptyError'\n }\n}\n\n/** A project name npm accepts: lower case letters, digits and dashes, starting with a letter. */\nexport function isValidName(name: string): boolean {\n return /^[a-z][a-z0-9-]*$/.test(name) && name.length <= 214\n}\n\n/** A valid default project name derived from a directory path. */\nexport function nameFrom(directory: string): string {\n const base = directory.split(/[\\\\/]/).filter(Boolean).pop() ?? ''\n const cleaned = base\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/^[^a-z]+/, '')\n .replace(/-+$/, '')\n return cleaned || 'my-app'\n}\n\n/** Refuse a target that exists and is not an empty directory. A missing target is fine: it is created. */\nexport function assertTargetFree(target: string): void {\n if (!existsSync(target)) return\n if (!statSync(target).isDirectory() || readdirSync(target).length > 0)\n throw new TargetNotEmptyError(target)\n}\n\n/**\n * Copy a template into `target`, replacing `__APP_NAME__` with `name` and `__ACRYLBLENDS_RANGE__` with the range of this release. Checks the target before writing anything.\n *\n * @returns the copied files, relative to `target`\n */\nexport function copyTemplate(source: string, target: string, name: string): string[] {\n assertTargetFree(target)\n mkdirSync(target, { recursive: true })\n const written: string[] = []\n const walk = (from: string, to: string, prefix: string) => {\n for (const entry of readdirSync(from, { withFileTypes: true })) {\n if (\n entry.name === 'node_modules' ||\n (prefix === '' &&\n (entry.name === TEMPLATE_MANIFEST ||\n entry.name === 'upstream.json' ||\n entry.name === 'AGENTS.md'))\n )\n continue\n const sourcePath = join(from, entry.name)\n // npm and git treat a file named .gitignore specially, so templates keep it as `gitignore` (and `dockerignore`).\n const targetName =\n prefix === '' && (entry.name === 'gitignore' || entry.name === 'dockerignore')\n ? `.${entry.name}`\n : entry.name\n const targetPath = join(to, targetName)\n if (entry.isDirectory()) {\n mkdirSync(targetPath, { recursive: true })\n walk(sourcePath, targetPath, `${prefix}${entry.name}/`)\n continue\n }\n const text = readFileSync(sourcePath, 'utf8')\n if (text.includes('__APP_NAME__') || text.includes('__ACRYLBLENDS_RANGE__'))\n writeFileSync(\n targetPath,\n text.replaceAll('__APP_NAME__', name).replaceAll('__ACRYLBLENDS_RANGE__', `^${VERSION}`),\n )\n else cpSync(sourcePath, targetPath)\n written.push(`${prefix}${targetName}`)\n }\n }\n walk(source, target, '')\n return written\n}\n\n/** A template's own manifest, `template.json`: the library plugins it is built from. Never copied (nor is `upstream.json`, the record of what was synced from ACRYL, nor the template's `AGENTS.md`, which `writeAgentFiles` joins to the shared core). */\nexport const TEMPLATE_MANIFEST = 'template.json'\n\n/** The address a template's desktop window opens on, when it is not the root (`template.json`: `desktopPath`). */\nexport function readTemplateDesktopPath(templateDir: string): string | undefined {\n const file = join(templateDir, TEMPLATE_MANIFEST)\n if (!existsSync(file)) return undefined\n return (JSON.parse(readFileSync(file, 'utf8')) as { desktopPath?: string }).desktopPath\n}\n\n/** Record where the desktop window opens in package.json (`acrylblends.desktopPath`), read by `plugins/desktop/main.ts`. */\nexport function writeDesktopPath(appDir: string, path: string): void {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n acrylblends?: Record<string, unknown>\n }\n manifest.acrylblends = { ...manifest.acrylblends, desktopPath: path }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\nexport function readTemplatePlugins(templateDir: string): string[] {\n const file = join(templateDir, TEMPLATE_MANIFEST)\n if (!existsSync(file)) return []\n const { plugins } = JSON.parse(readFileSync(file, 'utf8')) as { plugins?: string[] }\n return plugins ?? []\n}\n\n/** A version a plugin's pin can be compared with: an exact one or a caret or tilde range. A tag, a path or a URL is the app's own choice. */\nfunction comparableVersion(spec: string): string | undefined {\n return /^[\\^~]?\\d+\\.\\d+\\.\\d+(?:-[\\w.]+)?$/u.test(spec) ? spec.replace(/^[\\^~]/u, '') : undefined\n}\n\n/** Whether a plugin's pin asks for more than what the app has: the plugin's code was written against that version. */\nfunction asksForNewer(wanted: string, current: string): boolean {\n const a = comparableVersion(wanted)\n const b = comparableVersion(current)\n return a !== undefined && b !== undefined && compareVersions(a, b) > 0\n}\n\n/**\n * Add run-time dependencies to an app's package.json and sort the keys. Returns the names to install: those added, and those moved because a plugin asks for a newer\n * version than the app lists (the plugin's new code needs it). A version the app lists that is the same or newer stays. A package the app lists as a development\n * dependency too is taken out of that list: npm lets the development entry win, so `npm install --omit=dev` would leave a run-time dependency out.\n */\nexport function mergeDependencies(\n appDir: string,\n dependencies: Readonly<Record<string, string>>,\n): string[] {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n }\n const current = manifest.dependencies ?? {}\n const added = Object.keys(dependencies).filter((name) => !(name in current))\n const moved = Object.entries(dependencies).filter(\n ([name, wanted]) => current[name] !== undefined && asksForNewer(wanted, current[name]),\n )\n const merged = { ...dependencies, ...current, ...Object.fromEntries(moved) }\n manifest.dependencies = Object.fromEntries(\n Object.entries(merged).sort(([a], [b]) => a.localeCompare(b)),\n )\n if (manifest.devDependencies !== undefined)\n manifest.devDependencies = Object.fromEntries(\n Object.entries(manifest.devDependencies).filter(([name]) => !(name in merged)),\n )\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return [...added, ...moved.map(([name]) => name)]\n}\n\n/** Add `package.json` \"imports\" entries (`#acryl-kv`: `./plugins/acryl-kv/src/index.ts`); an entry the app already has is kept. Returns the names added. */\nexport function mergeImports(\n appDir: string,\n imports: Readonly<Record<string, string>>,\n pluginsDir: string,\n): string[] {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as { imports?: Record<string, string> }\n const current = manifest.imports ?? {}\n const added = Object.keys(imports).filter((name) => !(name in current))\n manifest.imports = {\n ...Object.fromEntries(\n Object.entries(imports).map(([name, path]) => [name, `./${pluginsDir}/${path}`]),\n ),\n ...current,\n }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return added\n}\n\n/** Declare a file as an opener in package.json (`\"acryl\": { \"opener\": [...] }`): the one place that may import a database provider. */\nexport function mergeOpener(appDir: string, opener: string): void {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n acryl?: { opener?: string | string[] }\n }\n const current = manifest.acryl?.opener\n const list = typeof current === 'string' ? [current] : (current ?? [])\n if (list.includes(opener)) return\n manifest.acryl = { ...manifest.acryl, opener: [...list, opener] }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\n/**\n * Add a plugin's development dependencies and scripts to an app's package.json: nothing the app already has is replaced, and a package the app already needs at run time\n * (`dependencies`) is not listed again as a development one. Returns the names of the development dependencies and scripts added.\n */\nexport function mergeTooling(\n appDir: string,\n tooling: {\n devDependencies?: Readonly<Record<string, string>> | undefined\n scripts?: Readonly<Record<string, string>> | undefined\n },\n): { devDependencies: string[]; scripts: string[] } {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n scripts?: Record<string, string>\n }\n const dev = manifest.devDependencies ?? {}\n const runtime = manifest.dependencies ?? {}\n const scripts = manifest.scripts ?? {}\n const wanted = Object.entries(tooling.devDependencies ?? {}).filter(\n ([name]) => !(name in runtime),\n )\n const addedDev = wanted.map(([name]) => name).filter((name) => !(name in dev))\n const addedScripts = Object.keys(tooling.scripts ?? {}).filter((name) => !(name in scripts))\n manifest.devDependencies = Object.fromEntries(\n Object.entries({ ...Object.fromEntries(wanted), ...dev })\n .filter(([name]) => !(name in runtime))\n .sort(([a], [b]) => a.localeCompare(b)),\n )\n manifest.scripts = { ...tooling.scripts, ...scripts }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return { devDependencies: addedDev, scripts: addedScripts }\n}\n\n/** What an app runs before `dev`, `start`, `test` and `check`: refuses a Node too old to run it, in plain words, before anything that would fail confusingly (a plain CommonJS file, so any Node can read it). */\nconst CHECK_NODE = `// Refuses a Node that is too old to run this app, before the command that would fail on it. Plain CommonJS so that any Node can read it.\nconst [major, minor] = process.versions.node.split('.').map(Number)\nif (major < 22 || (major === 22 && minor < 19)) {\n console.error(\n \\`Node 22.19+ needed, you have \\${process.versions.node}: install it from https://nodejs.org or with nvm (nvm install 22), then run this again.\\`,\n )\n process.exit(1)\n}\n`\n\n/** Write \\`scripts/check-node.cjs\\` and run it before the app's \\`dev\\`, \\`start\\`, \\`test\\` and \\`check\\` scripts (npm and pnpm run \\`pre<script>\\` first). Existing \\`pre*\\` scripts are kept. */\nexport function addNodePreflight(appDir: string): void {\n mkdirSync(join(appDir, 'scripts'), { recursive: true })\n writeFileSync(join(appDir, 'scripts', 'check-node.cjs'), CHECK_NODE)\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as { scripts?: Record<string, string> }\n const scripts = manifest.scripts ?? {}\n for (const name of ['dev', 'start', 'test', 'check'])\n if (name in scripts && !(`pre${name}` in scripts))\n scripts[`pre${name}`] = 'node scripts/check-node.cjs'\n manifest.scripts = scripts\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\n/** Add the lines a plugin needs to the app's \\`.gitignore\\` when it has one and they are missing. Returns the lines added. */\nexport function appendGitignore(appDir: string, lines: readonly string[] | undefined): string[] {\n const file = join(appDir, '.gitignore')\n if (!lines || lines.length === 0 || !existsSync(file)) return []\n const current = readFileSync(file, 'utf8')\n const present = new Set(current.split('\\n').map((line) => line.trim()))\n const missing = lines.filter((line) => !present.has(line))\n if (missing.length > 0)\n writeFileSync(\n file,\n `${current}${current.endsWith('\\n') || current === '' ? '' : '\\n'}${missing.join('\\n')}\\n`,\n )\n return missing\n}\n\n/**\n * The generated app's guide for a coding agent: the shared core, then the template's own part. `CLAUDE.md` is only an import of it. The skills\n * every app ships (`shared/skills/<name>/SKILL.md`) go to `.claude/skills/` (Claude Code) and `.agents/skills/` (the other agents), the same text in both.\n */\nexport function writeAgentFiles(\n sharedDir: string,\n templateDir: string,\n target: string,\n name: string,\n): string[] {\n const core = readFileSync(join(sharedDir, 'AGENTS.core.md'), 'utf8')\n const own = existsSync(join(templateDir, 'AGENTS.md'))\n ? readFileSync(join(templateDir, 'AGENTS.md'), 'utf8')\n : ''\n const defaults = readFileSync(join(sharedDir, 'DEFAULTS.md'), 'utf8')\n const text = `${core}${defaults}${own}`.replaceAll('__APP_NAME__', name)\n // AGENTS.md and CLAUDE.md are the same text; the skills are written where each kind of agent looks for them.\n writeFileSync(join(target, 'AGENTS.md'), text)\n writeFileSync(join(target, 'CLAUDE.md'), text)\n const written = ['AGENTS.md', 'CLAUDE.md']\n for (const skill of sharedSkills(sharedDir)) {\n const skillText = readFileSync(join(sharedDir, 'skills', skill, 'SKILL.md'), 'utf8').replaceAll(\n '__APP_NAME__',\n name,\n )\n for (const scope of ['.claude/skills', '.agents/skills']) {\n const folder = `${scope}/${skill}`\n mkdirSync(join(target, folder), { recursive: true })\n writeFileSync(join(target, folder, 'SKILL.md'), skillText)\n written.push(`${folder}/SKILL.md`)\n }\n }\n return written\n}\n\n/** The skills every generated app carries: the folder name of each under `shared/skills/`. */\nexport function sharedSkills(sharedDir: string): string[] {\n const root = join(sharedDir, 'skills')\n if (!existsSync(root)) return []\n return readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && existsSync(join(root, entry.name, 'SKILL.md')))\n .map((entry) => entry.name)\n .sort((a, b) => a.localeCompare(b))\n}\n\n/**\n * Copy the framework's docs (a generated copy of ACRYL's `docs/framework`, already checked against its digest) into `<app>/docs/framework/`,\n * so a coding agent working in the app reads the API from the folder it is in. The generated-copy notes and the digest record stay behind.\n *\n * @returns the copied files, relative to `target`\n */\nexport function copyFrameworkDocs(source: string, target: string): string[] {\n const written: string[] = []\n const walk = (from: string, to: string, prefix: string) => {\n for (const entry of readdirSync(from, { withFileTypes: true })) {\n if (entry.name === 'upstream.json' || entry.name === 'UPSTREAM.md') continue\n const sourcePath = join(from, entry.name)\n const targetPath = join(to, entry.name)\n if (entry.isDirectory()) {\n mkdirSync(targetPath, { recursive: true })\n walk(sourcePath, targetPath, `${prefix}${entry.name}/`)\n continue\n }\n cpSync(sourcePath, targetPath)\n written.push(`docs/framework/${prefix}${entry.name}`)\n }\n }\n mkdirSync(join(target, 'docs', 'framework'), { recursive: true })\n walk(source, join(target, 'docs', 'framework'), '')\n return written\n}\n","import { spawnSync } from 'node:child_process'\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { frameProfileForInit, frameProfileInput } from './frame-release'\nimport { APP_PLUGINS_FILE, appFrameRows } from './frame-rows'\nimport { applyIdeProfile } from './ide-profile'\nimport type { Io } from './io'\nimport { closure, copyPlugin, readLibrary } from './library'\nimport {\n addNodePreflight,\n copyFrameworkDocs,\n copyTemplate,\n isValidName,\n mergeDependencies,\n mergeImports,\n mergeTooling,\n nameFrom,\n readTemplateDesktopPath,\n readTemplatePlugins,\n TargetNotEmptyError,\n writeAgentFiles,\n writeDesktopPath,\n} from './project'\nimport {\n frameworkDocsRoot,\n resolveTemplate,\n sharedDir,\n TEMPLATE_SUMMARY,\n TEMPLATES,\n type Template,\n templateDir,\n} from './templates'\n\n/** A command line mistake. The CLI prints it with the help text and exits 2. */\nexport class UsageError extends Error {\n override name = 'UsageError'\n}\n\n/**\n * A new app is a git repository from the start: its `.gitignore` is written with the scaffold, `npm run save` commits it, and the frame's Files and\n * Changes views work on a git worktree (the app's own folder; without one they answer \"file request failed\"). A folder that already is a repository, or\n * a machine without git, is left alone.\n */\nfunction startRepository(directory: string): void {\n if (existsSync(join(directory, '.git'))) return\n const run = (args: string[]) =>\n spawnSync('git', args, { cwd: directory, stdio: 'ignore', windowsHide: true }).status === 0\n if (!run(['init', '-q'])) return\n if (!run(['add', '-A'])) return\n // A machine without a git identity still gets the first commit: this one is the scaffolder's, and later commits use the person's own identity. The `-c`\n // options are git's own and must precede the command.\n const identity = run(['config', 'user.email'])\n ? []\n : ['-c', 'user.name=acrylblends', '-c', 'user.email=scaffold@acrylblends.invalid']\n run([...identity, 'commit', '-q', '-m', 'Initial commit from acrylblends'])\n}\n\n/** What `init` was asked for. Anything left undefined is asked for on a terminal, or defaulted. */\nexport interface InitOptions {\n directory: string | undefined\n name: string | undefined\n template: Template | undefined\n install: boolean | undefined\n /** In the ACRYL frame with the desktop shell (`add desktop`): the default; `--bare` makes a plain app. */\n desktop: boolean\n /** Print no \"Next:\" block: `create-acrylblends` runs `init` and prints its own, for the package manager it knows. */\n quietNext: boolean\n yes: boolean\n}\n\n/** Parse `init [dir] [--template t] [--agent|--no-agent] [--name n] [--install] [--bare] [-y]`. `--agent` means `--template agent`, `--no-agent` means `--template blank`. */\nexport function parseInitOptions(argv: readonly string[]): InitOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n const { values, positionals } = parsed\n if (positionals.length > 1) throw new UsageError('init takes at most one directory')\n if (values.bare === true && values.desktop === true)\n throw new UsageError('use --bare or --desktop, not both')\n if (values.agent && values['no-agent'])\n throw new UsageError('use --agent or --no-agent, not both')\n const named = values.template === undefined ? undefined : resolveTemplate(values.template)\n if (values.template !== undefined && named === undefined)\n throw new UsageError(`unknown template \"${values.template}\" (${TEMPLATES.join(', ')})`)\n const alias: Template | undefined = values.agent\n ? 'agent'\n : values['no-agent']\n ? 'blank'\n : undefined\n if (named !== undefined && alias !== undefined && named !== alias)\n throw new UsageError(\n `--template ${values.template} contradicts --${alias === 'agent' ? 'agent' : 'no-agent'}`,\n )\n return {\n directory: positionals[0],\n name: values.name,\n template: named ?? alias,\n install: values.install,\n // The app is made in the ACRYL frame (chat, agents, Settings, the window) unless --bare: the frame is the product, a plain app is the exception.\n desktop: values.bare !== true,\n quietNext: values['no-next'] === true,\n yes: values.yes === true,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n name: { type: 'string' },\n template: { type: 'string' },\n agent: { type: 'boolean' },\n 'no-agent': { type: 'boolean' },\n install: { type: 'boolean' },\n desktop: { type: 'boolean' },\n bare: { type: 'boolean' },\n 'no-next': { type: 'boolean' },\n yes: { type: 'boolean', short: 'y' },\n },\n })\n\nconst yes = (answer: string) => ['y', 'yes'].includes(answer.trim().toLowerCase())\n\nasync function askTemplate(ask: (question: string) => Promise<string>): Promise<Template> {\n const answer = (\n await ask(\n `Templates:\\n${TEMPLATES.map((name) => ` ${name.padEnd(7)} ${TEMPLATE_SUMMARY[name]}`).join('\\n')}\\nTemplate [blank]: `,\n )\n )\n .trim()\n .toLowerCase()\n if (answer === '') return 'blank'\n const chosen = resolveTemplate(answer)\n if (chosen !== undefined) return chosen\n throw new UsageError(`unknown template \"${answer}\" (${TEMPLATES.join(', ')})`)\n}\n\n/** Scaffold a Cordis app. Returns the exit code: 0 done, 1 refused or failed. Never overwrites anything. */\nexport async function init(options: InitOptions, io: Io): Promise<number> {\n const relative = options.directory ?? '.'\n const directory = resolve(io.cwd, relative)\n const ask = options.yes ? undefined : io.ask\n\n let name = options.name ?? nameFrom(directory)\n if (options.name === undefined && ask)\n name = (await ask(`Project name (${name}): `)).trim() || name\n if (!isValidName(name)) {\n throw new UsageError(\n `\"${name}\" is not a valid project name (lower case letters, digits and dashes, starting with a letter)`,\n )\n }\n const template = options.template ?? (ask ? await askTemplate(ask) : 'blank')\n const install =\n options.install ?? (ask ? yes(await ask('Install dependencies with npm now? (y/N): ')) : false)\n\n let files: string[]\n try {\n files = copyTemplate(templateDir(template), directory, name)\n files.push(...writeAgentFiles(sharedDir(), templateDir(template), directory, name))\n files.push(...copyFrameworkDocs(frameworkDocsRoot(), directory))\n } catch (error) {\n if (!(error instanceof TargetNotEmptyError)) throw error\n io.stderr.write(`error: ${error.message}\\n`)\n return 1\n }\n const own = closure(readTemplatePlugins(templateDir(template)), readLibrary())\n const plugins = options.desktop\n ? closure([...own.map((plugin) => plugin.name), 'desktop'], readLibrary())\n : own\n // What only the desktop shell brought (and needs), named apart so the list does not read as if the web app had asked for it.\n const forDesktop = plugins.filter((plugin) => !own.some((owned) => owned.name === plugin.name))\n for (const plugin of plugins) {\n copyPlugin(plugin, directory)\n if (plugin.imports) mergeImports(directory, plugin.imports, 'plugins')\n mergeDependencies(directory, plugin.dependencies)\n mergeTooling(directory, plugin)\n }\n addNodePreflight(directory)\n // The ACRYL frame (docs/plans/ide-port.md, slice 6): a template with a frame row (`src/frame.ts`) gets the app's own DSH profile and overlay, so\n // `npm run dev` opens it inside the frame with its page at /app. A desktop app installs the frame's published packages (frame/packages.json);\n // ACRYLBLENDS_FRAME_PACKAGES overrides them (a checkout's build, or `none`), and an app with neither stays on the framework alone and says so.\n // The ACRYL frame's layer needs two rows for the app itself: its routes row (src/frame.ts), and the one composition row that runs the app's own plugins\n // beside the host through composeInto (frame/app-plugins.mjs), with the services they provide isolated through the Loader's entry option. appFrameRows is\n // the one home for those rows, their isolate rule and the generated file; `upgrade` writes the same ones for an app made before the frame.\n const frame = appFrameRows(directory)\n if (frame.appPlugins !== undefined) {\n mkdirSync(join(directory, 'frame'), { recursive: true })\n writeFileSync(join(directory, APP_PLUGINS_FILE), frame.appPlugins)\n }\n const frameRow = frame.rows\n if (frameRow.length > 0) {\n const specs = frameProfileForInit(options.desktop)\n if (specs === undefined) {\n const why = options.desktop\n ? 'ACRYLBLENDS_FRAME_PACKAGES is none'\n : 'init --desktop puts it inside the ACRYL frame'\n io.stdout.write(`note: this app runs on the framework alone; ${why}\\n`)\n } else {\n const wrote = applyIdeProfile(directory, frameProfileInput(specs, name, frameRow))\n io.stdout.write(`frame: wrote ${wrote.written.join(', ') || 'nothing (already right)'}\\n`)\n }\n }\n const desktopPath = options.desktop ? readTemplateDesktopPath(templateDir(template)) : undefined\n if (desktopPath !== undefined) writeDesktopPath(directory, desktopPath)\n io.stdout.write(\n `created ${name} (${template}) in ${directory}: ${files.length} files, plugins ${own.map((plugin) => plugin.name).join(', ')}${forDesktop.length > 0 ? `; the desktop shell adds ${forDesktop.map((plugin) => plugin.name).join(', ')}, which it needs` : ''}\\n`,\n )\n\n // The scaffold is the repository's first commit, so the frame's Changes view starts from what the person (or their agent) changed afterwards.\n startRepository(directory)\n if (install && (await io.install(directory)) !== 0) {\n io.stderr.write('error: npm install failed; run it yourself in the new directory\\n')\n return 1\n }\n const steps = [\n relative === '.' ? '' : ` cd ${relative}\\n`,\n install ? '' : ' npm install\\n',\n ' npm run dev\\n npm test\\n',\n ]\n if (!options.quietNext) io.stdout.write(`\\nNext:\\n${steps.join('')}`)\n return 0\n}\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\n/**\n * Plugins that `acrylblends add` mounts itself: a row in `blend.yaml` (after the row it needs, before the `routes` row) and an entry in the `registry` of `src/app.ts`.\n * `needs` are the rows the plugin cannot run without; an app that lacks them is refused before anything is copied.\n */\nexport interface Mounted {\n readonly name: string\n /** The identifier of the registry import, and the plugin names (the `name:` of a row) it needs. */\n readonly local: string\n readonly needs: readonly string[]\n /** The row this one goes after (it goes before `routes` otherwise). */\n readonly after?: string\n /** The `id:` of its row, when it is not the plugin's name (`db` for acryl-db-sqlite). */\n readonly row?: string\n /** Its entry file inside the plugin's folder (`index.ts`, or `src/index.ts` for the plugins that keep their source in `src/`). */\n readonly entry?: string\n}\n\nexport const MOUNTED: readonly Mounted[] = [\n { name: 'agent-panel', local: 'panel', needs: ['agent', 'extensions'] },\n { name: 'driver-pty', local: 'driverPty', needs: ['agent-panel'], after: 'agent-panel' },\n { name: 'driver-acp', local: 'driverAcp', needs: ['agent-panel'], after: 'agent-panel' },\n { name: 'feedback', local: 'feedback', needs: ['acryl-router'] },\n { name: 'showcase', local: 'showcase', needs: ['plugin-share'], after: 'plugin-share' },\n]\n\n/** A row, in either line ending: a file edited on Windows (or checked out with core.autocrlf) is still a Blend. */\nconst ROW = /^( *)- id: ?(\\S+)\\r?\\n\\1 {2}name: ?(\\S+)\\r?\\n/gmu\n\n/** The line ending the text already uses, so an edit keeps the file's own style. */\nconst eolOf = (text: string): string => (text.includes('\\r\\n') ? '\\r\\n' : '\\n')\n\n/** The `name:` of every row of the app's `blend.yaml`, or `undefined` when it has none. */\nexport function rowNames(appDir: string): string[] | undefined {\n const file = join(appDir, 'blend.yaml')\n if (!existsSync(file)) return undefined\n return [...readFileSync(file, 'utf8').matchAll(ROW)].map((match) => match[3] as string)\n}\n\n/** What a plugin to be mounted is missing: the rows it needs that neither the app nor this same command brings. */\nexport function missingRows(\n appDir: string,\n plugin: Mounted,\n alsoAdded: readonly string[],\n): string[] {\n const have = rowNames(appDir) ?? []\n return plugin.needs.filter((need) => !have.includes(need) && !alsoAdded.includes(need))\n}\n\nexport interface MountResult {\n readonly done: boolean\n /** What was edited, or why it was not (the person finishes by hand). */\n readonly note: string\n}\n\n/** Add the row to `blend.yaml` and the entry to `src/app.ts`; each only when it is not there yet and its place is where the template puts it. */\nexport function mount(appDir: string, plugin: Mounted): MountResult {\n const done: string[] = []\n const failed: string[] = []\n const manifestFile = join(appDir, 'blend.yaml')\n if (existsSync(manifestFile)) {\n let text = readFileSync(manifestFile, 'utf8')\n if ([...text.matchAll(ROW)].some((match) => match[3] === plugin.name))\n done.push('blend.yaml (already has the row)')\n else {\n const eol = eolOf(text)\n const anchor = plugin.after\n ? new RegExp(\n `^( *)- id: ?\\\\S+\\\\r?\\\\n\\\\1 {2}name: ?${plugin.after}\\\\r?\\\\n(?:\\\\1 {2}config:\\\\r?\\\\n(?:\\\\1 {4}.*\\\\r?\\\\n|\\\\1 {2}\\\\s*\\\\r?\\\\n)*)?`,\n 'mu',\n )\n : /^( *)- id: ?routes\\r?\\n/mu\n const found = anchor.exec(text)\n if (found) {\n const indent = found[1] as string\n const row = `${indent}- id: ${plugin.row ?? plugin.name}${eol}${indent} name: ${plugin.name}${eol}`\n const at = plugin.after ? found.index + found[0].length : found.index\n text = text.slice(0, at) + row + text.slice(at)\n writeFileSync(manifestFile, text)\n done.push(`blend.yaml (row ${plugin.name})`)\n } else\n failed.push(\n `blend.yaml: add a row \\`- id: ${plugin.name}\\` / \\`name: ${plugin.name}\\` ${plugin.after ? `after the ${plugin.after} row` : 'before the `routes` row'}`,\n )\n }\n }\n const appFile = join(appDir, 'src', 'app.ts')\n if (existsSync(appFile)) {\n let text = readFileSync(appFile, 'utf8')\n const entry = `'${plugin.name}': ${plugin.local}`\n if (text.includes(entry)) done.push('src/app.ts (already registered)')\n else {\n const eol = eolOf(text)\n const imports = [...text.matchAll(/^import .* from '\\.\\.\\/plugins\\/[^']+'\\r?\\n/gmu)]\n const last = imports.at(-1)\n // The registry as the templates write it: one entry per line, or all on one line (`{ 'acryl-router': router, routes }`).\n const table = /^export const registry = \\{(\\r?\\n)/mu.exec(text)\n if (last && table) {\n const file = plugin.entry ?? 'index.ts'\n const line = `import * as ${plugin.local} from '../plugins/${plugin.name}/${file}'${eol}`\n const at = table.index + table[0].length\n text =\n text.slice(0, at) +\n (table[1] === '\\n' || table[1] === '\\r\\n' ? ` ${entry},${eol}` : `${entry}, `) +\n text.slice(at)\n const lastEnd = last.index + last[0].length\n text = text.slice(0, lastEnd) + line + text.slice(lastEnd)\n writeFileSync(appFile, text)\n done.push(`src/app.ts (registered as ${plugin.local})`)\n } else\n failed.push(\n `src/app.ts: import it as \\`${plugin.local}\\` from '../plugins/${plugin.name}/${plugin.entry ?? 'index.ts'}' and add \\`${entry}\\` to the registry`,\n )\n }\n } else failed.push(`src/app.ts: not found; register ${entry(plugin)} in the app's registry`)\n return {\n done: failed.length === 0,\n note: [\n done.length > 0 ? `mounted in ${done.join(' and ')}` : '',\n ...failed.map((line) => `could not edit ${line}`),\n ]\n .filter(Boolean)\n .join('\\n'),\n }\n}\n\nconst entry = (plugin: Mounted) => `'${plugin.name}': ${plugin.local}`\n","import { cpSync, existsSync, mkdirSync, readFileSync, statSync } from 'node:fs'\nimport { basename, dirname, join, relative, resolve, sep } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { UsageError } from './init'\nimport type { Io } from './io'\nimport {\n closure,\n copyPlugin,\n copyRewritten,\n LEGACY_WARNING,\n type LibraryPlugin,\n libraryRoot,\n PLUGINS_DIR,\n pluginsFolder,\n readLibrary,\n} from './library'\nimport { MOUNTED, type Mounted, missingRows, mount, rowNames } from './mount'\nimport {\n appendGitignore,\n mergeDependencies,\n mergeImports,\n mergeOpener,\n mergeTooling,\n} from './project'\n\nexport interface AddOptions {\n readonly names: readonly string[]\n readonly directory: string | undefined\n /** Also copy the port's conformance suite (`src/conformance.ts`) and the `vitest` it runs on: what a provider of your own must pass. */\n readonly suite: boolean\n}\n\n/** Parse `add <plugin...> [--dir <app>] [--suite]`. */\nexport function parseAddOptions(argv: readonly string[]): AddOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n if (parsed.positionals.length === 0)\n throw new UsageError('add needs at least one plugin name (see \"acrylblends plugins\")')\n return {\n names: parsed.positionals,\n directory: parsed.values.dir,\n suite: parsed.values.suite === true,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: { dir: { type: 'string' }, suite: { type: 'boolean' } },\n })\n\nconst present = (appDir: string, name: string) => existsSync(join(pluginsFolder(appDir).dir, name))\n\n/**\n * Copy library plugins into an existing app, with the plugins they require, and add their npm packages to its package.json.\n * Refuses a plugin the app already has. Returns the exit code.\n */\nexport async function add(options: AddOptions, io: Io): Promise<number> {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n if (!existsSync(join(appDir, 'package.json')) || !existsSync(join(appDir, 'src'))) {\n io.stderr.write(\n `error: ${appDir} is not an app (no package.json and src/); run \"acrylblends init\" first\\n`,\n )\n return 1\n }\n if (pluginsFolder(appDir).legacy) io.stderr.write(`${LEGACY_WARNING}\\n`)\n // A path is an external plugin folder; anything else is a library plugin name.\n const external = options.names.filter((name) => /[\\\\/]/.test(name) || name.startsWith('.'))\n for (const folder of external) {\n const code = addExternal(resolve(io.cwd, folder), appDir, io)\n if (code !== 0) return code\n }\n // The desktop shell has two forms: the Blend app (it needs the `instance` plugin) and a plain Acryl app (`export default app`); `desktop` means the one that fits.\n // A Blend app has a blend.yaml (an older one at least its instance plugin). A plain app may have the instance plugin too, when desktop-web brought it.\n const folder = pluginsFolder(appDir).dir\n const plain =\n !existsSync(join(appDir, 'blend.yaml')) &&\n (!existsSync(join(folder, 'instance')) || existsSync(join(folder, 'desktop-web')))\n if (plain && options.names.includes('db')) {\n io.stderr.write(\n 'error: \"db\" is the Cordis plugin for a Blend app. A plain Acryl app opens its database with openDb from \"acrylblends/db/sqlite\" (see docs/framework/guides/data.md)\\n',\n )\n return 1\n }\n if (plain && options.names.includes('kv')) {\n io.stderr.write(\n 'error: \"kv\" is the Cordis plugin for a Blend app. A plain Acryl app opens its store with openKv from \"acrylblends/kv/json\" (see docs/framework/guides/data.md#a-tiny-app-kv)\\n',\n )\n return 1\n }\n const names = options.names\n .filter((name) => !external.includes(name))\n // `db` is the SQLite provider of the db port (it brings the port); a plain app opens its database with `acrylblends/db/sqlite` instead.\n .map((name) => (name === 'db' ? 'acryl-db-sqlite' : name))\n // `kv` is the JSON-file provider of the kv port (it brings the port); `acryl-kv-db` is the one over db.\n .map((name) => (name === 'kv' ? 'acryl-kv-json' : name))\n .map((name) => (name === 'desktop' && plain ? 'desktop-web' : name))\n if (names.length === 0) return 0\n const already = options.suite ? [] : names.filter((name) => present(appDir, name))\n if (already.length > 0) {\n io.stderr.write(\n `error: the app already has ${already\n .map(\n (name) =>\n `${relative(appDir, join(pluginsFolder(appDir).dir, name))\n .split(sep)\n .join('/')}`,\n )\n .join(', ')}\\n`,\n )\n return 1\n }\n // The agent panel and the PTY driver cannot run without the rows of the agent template: refuse before anything is copied, and say exactly what is missing.\n const mounting = MOUNTED.filter((plugin) => names.includes(plugin.name))\n for (const plugin of mounting) {\n const missing = missingRows(appDir, plugin, names)\n if (missing.length > 0) {\n io.stderr.write(\n `error: ${plugin.name} needs the ${missing.map((row) => `\\`${row}\\``).join(' and ')} ${missing.length === 1 ? 'row' : 'rows'} in blend.yaml, and this app has ${describeRows(appDir)}. ` +\n `${plugin.name === 'driver-pty' ? 'Add agent-panel first (acrylblends add agent-panel driver-pty), which needs the agent template. ' : ''}` +\n `The rows come with the agent template: start from it (npm create acrylblends@latest my-app agent) and add ${plugin.name} there. Nothing was changed.\\n`,\n )\n return 1\n }\n }\n const toCopy: LibraryPlugin[] = closure(names, readLibrary()).filter(\n (plugin) => !present(appDir, plugin.name),\n )\n const addedDependencies: string[] = []\n for (const plugin of toCopy) {\n copyPlugin(plugin, appDir)\n if (plugin.imports)\n mergeImports(\n appDir,\n plugin.imports,\n relative(appDir, pluginsFolder(appDir).dir).split(sep).join('/'),\n )\n addedDependencies.push(...mergeDependencies(appDir, plugin.dependencies))\n const tooling = mergeTooling(appDir, plugin)\n appendGitignore(appDir, plugin.gitignore)\n // The provider is mounted from src/app.ts, which is therefore the one file that may import it (`acrylblends check-deps`).\n if (plugin.name === 'acryl-db-sqlite') mergeOpener(appDir, 'src/app.ts')\n addedDependencies.push(...tooling.devDependencies)\n if (tooling.scripts.length > 0)\n io.stdout.write(\n `new scripts: ${tooling.scripts.map((name) => `npm run ${name}`).join(', ')}\\n`,\n )\n }\n if (options.suite) {\n const refused = copySuites(names, appDir, addedDependencies, io)\n if (refused !== 0) return refused\n }\n io.stdout.write(`added ${toCopy.map((plugin) => plugin.name).join(', ')} to ${appDir}\\n`)\n if (addedDependencies.length > 0)\n io.stdout.write(`dependencies to install: ${addedDependencies.join(', ')} (run npm install)\\n`)\n for (const plugin of toCopy.filter((plugin) => names.includes(plugin.name))) {\n const mounted = MOUNTED.find((candidate) => candidate.name === plugin.name)\n const result = mounted ? mount(appDir, mounted) : undefined\n io.stdout.write(\n `\\n${plugin.name}: ${result?.done ? `${result.note}${extraNote(mounted)}` : `${result ? `${result.note}\\n` : ''}${plugin.mount ?? `import it from '../plugins/${plugin.name}/index.ts' and mount it in src/app.ts`}`}\\n`,\n )\n }\n return 0\n}\n\n/** What an app has to say about its rows, for an error: the `name:` of each, or that it has no blend.yaml. */\nfunction describeRows(appDir: string): string {\n const rows = rowNames(appDir)\n return rows === undefined ? 'no blend.yaml' : `the rows ${rows.join(', ') || '(none)'}`\n}\n\n/** What to do after `npm install` for a plugin that needs more than the install: node-pty compiles a native module in an install script, which npm 11 and pnpm skip until approved. */\nfunction extraNote(plugin: Mounted | undefined): string {\n return plugin?.name === 'driver-pty'\n ? \"\\nnpm 11 and pnpm warn that node-pty's install script was not run, because they ask you to approve dependency install scripts. node-pty ships prebuilt binaries for macOS, Windows and Linux (x64, arm64), so you can usually ignore the warning. If the panel lists no `pty:` terminal (the app prints why at start), approve it and rebuild: `npm approve-scripts node-pty`, then `npm rebuild node-pty` (pnpm: `pnpm approve-builds`).\"\n : ''\n}\n\n/**\n * The vitest the suites run on. 3.2.x, not the newest: npm 10 (the npm of Node 22) crashes on the dependency tree of vitest 4 and 5 (\"Cannot read properties of null (reading\n * 'edgesOut')\"), and the suites use only `describe`, `it` and `expect`, which are the same in all of them. An app on a newer npm may raise it.\n */\nconst SUITE_RUNNER = { vitest: '3.2.4' }\n\n/**\n * Copy the conformance suite of each named port next to its source (`plugins/<name>/src/conformance.ts`), and add `vitest` to the app's development\n * dependencies. The suite is left out of a normal copy because an app that only uses a port has no use for it. Returns the exit code: 1 when a plugin has none.\n */\nfunction copySuites(names: readonly string[], appDir: string, added: string[], io: Io): number {\n const copies: Array<{ from: string; to: string }> = []\n // The suite belongs to the port: `add kv --suite` (acryl-kv-json, or acryl-kv-db) brings the suite of acryl-kv.\n const ports = [\n ...new Set(names.map((name) => name.replace(/^(acryl-(?:kv|db))(?:-.*)?$/u, '$1'))),\n ]\n const library = readLibrary()\n for (const name of ports) {\n const suite = library.get(name)?.suite\n const from = suite === undefined ? '' : join(libraryRoot(), name, suite)\n if (suite === undefined || !existsSync(from)) {\n io.stderr.write(`error: ${name} has no conformance suite (the ports db and kv do)\\n`)\n return 1\n }\n copies.push({ from, to: join(pluginsFolder(appDir).dir, name, suite) })\n }\n for (const { from, to } of copies) {\n mkdirSync(dirname(to), { recursive: true })\n copyRewritten(from, to, pluginOf(to), suiteOf(to))\n io.stdout.write(`added the conformance suite ${relative(appDir, to).split(sep).join('/')}\\n`)\n }\n added.push(...mergeTooling(appDir, { devDependencies: SUITE_RUNNER }).devDependencies)\n return 0\n}\n\n/** The plugin folder name and the file inside it, from a path below the plugins folder (`.../plugins/acryl-kv/src/conformance.ts`). */\nconst pluginOf = (path: string): string => path.split(sep).slice(-3, -2)[0] ?? ''\nconst suiteOf = (path: string): string => path.split(sep).slice(-2).join('/')\n\nconst PLUGIN_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/\n\n/**\n * Bring an external plugin folder (a package made with the builder's package_plugin, or any folder with an index.ts that exports a\n * Cordis plugin) into the app's `plugins/`, where the extensions plugin installs it like any other: checked, staged, started,\n * verified. Its runtime dependencies are not installed here; they are reported so the user adds them deliberately.\n */\nfunction addExternal(folder: string, appDir: string, io: Io): number {\n if (!existsSync(folder) || !statSync(folder).isDirectory()) {\n io.stderr.write(`error: ${folder} is not a folder\\n`)\n return 1\n }\n if (!['index.ts', 'index.js', 'index.mjs'].some((entry) => existsSync(join(folder, entry)))) {\n io.stderr.write(`error: ${folder} has no index.ts that exports a Cordis plugin\\n`)\n return 1\n }\n const manifestFile = join(folder, 'package.json')\n const manifest = existsSync(manifestFile)\n ? (JSON.parse(readFileSync(manifestFile, 'utf8')) as {\n name?: string\n dependencies?: Record<string, string>\n })\n : {}\n const name = (manifest.name ?? basename(folder)).replace(/^@[^/]+\\//, '')\n if (!PLUGIN_NAME.test(name)) {\n io.stderr.write(\n `error: \"${name}\" is not a plugin name (lowercase letters, digits and dashes)\\n`,\n )\n return 1\n }\n const target = join(\n pluginsFolder(appDir).legacy ? join(appDir, 'extensions') : join(appDir, PLUGINS_DIR),\n name,\n )\n if (existsSync(target)) {\n io.stderr.write(`error: the app already has ${relative(appDir, target).split(sep).join('/')}\\n`)\n return 1\n }\n cpSync(folder, target, {\n recursive: true,\n filter: (path) => !/[\\\\/](node_modules|\\.git)([\\\\/]|$)/.test(path.slice(folder.length)),\n })\n io.stdout.write(\n `added external plugin ${name} to ${dirname(target)}; it installs at the next start\\n`,\n )\n const dependencies = Object.keys(manifest.dependencies ?? {})\n if (dependencies.length > 0)\n io.stdout.write(\n `it uses ${dependencies.join(', ')}: add them to the app with npm install first\\n`,\n )\n return 0\n}\n\n/** `acrylblends plugins`: the library, one line per plugin. */\nexport function listPlugins(io: Io): number {\n for (const plugin of readLibrary().values()) {\n const requires = plugin.requires.length > 0 ? ` (requires ${plugin.requires.join(', ')})` : ''\n io.stdout.write(`${plugin.name.padEnd(12)} ${plugin.description}${requires}\\n`)\n }\n return 0\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { relative, resolve } from 'node:path'\nimport { UsageError } from './init'\nimport type { Io } from './io'\n\n/** Parse `build-client [root] [outDir]`. */\nexport function parseBuildClientOptions(argv: readonly string[]): { root: string; outDir: string } {\n const args = argv.filter((a) => !a.startsWith('-'))\n if (args.length !== argv.length)\n throw new UsageError('build-client takes [root] [outDir] and no options')\n if (args.length > 2)\n throw new UsageError('build-client takes at most two arguments: [root] [outDir]')\n return { root: args[0] ?? 'src/client', outDir: args[1] ?? 'dist/client' }\n}\n\n/**\n * Compile the app's client modules once (`buildClientModules` of the framework), for a production install that has no compiler at run time.\n * The framework is the one installed in the app (so the compiled files match the server that serves them), else this package's own.\n */\nexport async function buildClient(\n options: { root: string; outDir: string },\n io: Io,\n): Promise<number> {\n const root = resolve(io.cwd, options.root)\n if (!existsSync(root)) {\n io.stderr.write(\n `error: ${root} does not exist; the client modules live in src/client by default\\n`,\n )\n return 1\n }\n const outDir = resolve(io.cwd, options.outDir)\n const here = createRequire(resolve(io.cwd, 'package.json'))\n let specifier = 'acrylblends/node-server'\n try {\n here.resolve(specifier)\n } catch {\n specifier = new URL('../lib/node-server.js', import.meta.url).href\n }\n const mod = (await import(\n specifier.startsWith('file:') ? specifier : here.resolve(specifier)\n )) as typeof import('../lib/node-server')\n const written = mod.buildClientModules({ root, outDir })\n for (const file of written) io.stdout.write(`${relative(io.cwd, resolve(outDir, file))}\\n`)\n io.stdout.write(`built ${written.length} files in ${relative(io.cwd, outDir) || '.'}\\n`)\n return 0\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { join, relative, resolve, sep } from 'node:path'\nimport type { Io } from './io'\n\n/**\n * The dependency rule (spec 049): app code imports a port, never a provider. A port is `acrylblends/db` (the `Db` type); a provider is a\n * driver or the one function that opens a database. Only the opener, a file the app names once in package.json, may import a provider.\n */\n\n/** A package that is a database driver. An import of it, or of anything below it, is a provider import. */\nconst DRIVERS = [\n 'node:sqlite',\n 'sqlite',\n 'sqlite3',\n 'better-sqlite3',\n '@libsql/client',\n 'libsql',\n 'pg',\n 'pg-pool',\n 'pg-native',\n 'postgres',\n '@electric-sql/pglite',\n '@neondatabase/serverless',\n '@vercel/postgres',\n 'mysql',\n 'mysql2',\n]\n/** The openers a provider ships: importing one of them is opening a database. */\nconst OPENERS = ['acrylblends/db/sqlite', 'acryl-db-sqlite']\n/** The desktop shell is where Electron lives (`acrylblends add desktop` copies it). */\nconst ELECTRON = 'electron'\nconst ELECTRON_HOMES = [\n 'plugins/desktop/',\n 'plugins/desktop-web/',\n 'src/plugins/desktop/',\n 'src/plugins/desktop-web/',\n]\n/** The provider plugins themselves may import what they wrap. */\nconst PROVIDER_HOMES = ['plugins/acryl-db-sqlite/', 'src/plugins/acryl-db-sqlite/']\n\nconst SOURCE = /\\.(?:[cm]?[jt]s|[jt]sx)$/u\nconst SKIP_DIRS = new Set(['node_modules', 'dist', 'lib', 'build', 'coverage'])\nconst ROOTS = ['src', 'test', 'tests', 'plugins', 'extensions']\n\nexport interface Violation {\n readonly file: string\n readonly line: number\n readonly specifier: string\n readonly kind: 'driver' | 'opener' | 'electron'\n}\n\nconst matches = (specifier: string, name: string): boolean =>\n specifier === name || specifier.startsWith(`${name}/`)\n\n/** What a specifier is to the rule, or undefined when it is not a provider. */\nexport function classifySpecifier(specifier: string): Violation['kind'] | undefined {\n if (DRIVERS.some((name) => matches(specifier, name))) return 'driver'\n if (OPENERS.some((name) => matches(specifier, name))) return 'opener'\n if (matches(specifier, ELECTRON)) return 'electron'\n return undefined\n}\n\n/** The source with every comment replaced by spaces (newlines kept), so a line number still means what it did and a commented import is not an import. */\nexport function blankComments(source: string): string {\n let out = ''\n let i = 0\n while (i < source.length) {\n const c = source[i] as string\n const next = source[i + 1]\n if (c === '/' && next === '/') {\n const end = source.indexOf('\\n', i)\n const stop = end === -1 ? source.length : end\n out += ' '.repeat(stop - i)\n i = stop\n } else if (c === '/' && next === '*') {\n const end = source.indexOf('*/', i + 2)\n const stop = end === -1 ? source.length : end + 2\n out += source.slice(i, stop).replace(/[^\\n]/gu, ' ')\n i = stop\n } else if (c === \"'\" || c === '\"' || c === '`') {\n let j = i + 1\n while (j < source.length && source[j] !== c) {\n if (source[j] === '\\\\') j += 1\n else if (c !== '`' && source[j] === '\\n') break\n j += 1\n }\n out += source.slice(i, Math.min(j + 1, source.length))\n i = j + 1\n } else {\n out += c\n i += 1\n }\n }\n return out\n}\n\n/** `import x from 'a'`, `export * from 'a'`, `import 'a'`, `import('a')` and `require('a')`, with the line of each. */\nconst IMPORT =\n /(?:^|[\\s;{}()])(?:import\\s*(?:type\\s+)?(?:[\\w*${},\\s]*?\\s*from\\s*)?|export\\s*(?:type\\s+)?[\\w*${},\\s]*?\\s*from\\s*|import\\s*\\(\\s*|require\\s*\\(\\s*)(['\"])([^'\"\\n]+)\\1/gu\n\nexport function importsOf(source: string): { specifier: string; line: number }[] {\n const code = blankComments(source)\n const found: { specifier: string; line: number }[] = []\n for (const match of code.matchAll(IMPORT)) {\n const specifier = match[2] as string\n const at = (match.index ?? 0) + match[0].length - specifier.length - 2\n found.push({ specifier, line: code.slice(0, at).split('\\n').length })\n }\n return found\n}\n\nfunction listSources(dir: string, out: string[] = []): string[] {\n if (!existsSync(dir)) return out\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name.startsWith('.') || SKIP_DIRS.has(entry.name)) continue\n const path = join(dir, entry.name)\n if (entry.isDirectory()) listSources(path, out)\n else if (SOURCE.test(entry.name) && !entry.name.endsWith('.d.ts')) out.push(path)\n }\n return out\n}\n\n/** The opener files the app declares: `\"acryl\": { \"opener\": \"src/db.ts\" }` (or a list) in package.json. */\nexport function openersOf(appDir: string): string[] {\n const file = join(appDir, 'package.json')\n if (!existsSync(file)) return []\n const declared = (JSON.parse(readFileSync(file, 'utf8')) as { acryl?: { opener?: unknown } })\n .acryl?.opener\n const list = typeof declared === 'string' ? [declared] : Array.isArray(declared) ? declared : []\n return list.filter((entry): entry is string => typeof entry === 'string')\n}\n\n/** Every provider import outside the declared opener (and the homes that may hold one), in file and line order. */\nexport function findViolations(appDir: string): Violation[] {\n const openers = new Set(openersOf(appDir).map((entry) => entry.split(sep).join('/')))\n const violations: Violation[] = []\n for (const root of ROOTS) {\n for (const path of listSources(join(appDir, root)).sort()) {\n const file = relative(appDir, path).split(sep).join('/')\n if (openers.has(file)) continue\n for (const { specifier, line } of importsOf(readFileSync(path, 'utf8'))) {\n const kind = classifySpecifier(specifier)\n if (kind === undefined) continue\n if (kind === 'electron' && ELECTRON_HOMES.some((home) => file.startsWith(home))) continue\n if (kind !== 'electron' && PROVIDER_HOMES.some((home) => file.startsWith(home))) continue\n violations.push({ file, line, specifier, kind })\n }\n }\n }\n return violations\n}\n\nconst GUIDE = 'docs/framework/guides/data.md#why-the-database-is-opened-in-one-place'\n\nfunction advice(violation: Violation, openers: readonly string[]): string {\n if (violation.kind === 'electron')\n return `\"${violation.specifier}\" is the desktop shell's: only plugins/desktop (added with \"acrylblends add desktop\") imports electron`\n const where =\n openers.length > 0\n ? openers.join(', ')\n : 'a file named in package.json \"acryl\": { \"opener\": \"...\" }'\n return violation.kind === 'driver'\n ? `\"${violation.specifier}\" is a database driver; take a Db (import type { Db } from \"acrylblends/db\") as an argument and let ${where} open the database`\n : `\"${violation.specifier}\" opens a database, and only ${where} may; take a Db (import type { Db } from \"acrylblends/db\") as an argument`\n}\n\n/** Parse `check-deps [dir]`. */\nexport function parseCheckDepsOptions(argv: readonly string[]): { directory: string | undefined } {\n const args = argv.filter((a) => !a.startsWith('-'))\n if (args.length !== argv.length || args.length > 1)\n throw new Error('check-deps takes at most one argument: the app folder')\n return { directory: args[0] }\n}\n\n/** Report each provider import outside the opener, as `file:line: why`. Returns the exit code: 1 when there is one. */\nexport function checkDeps(options: { directory: string | undefined }, io: Io): number {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n const violations = findViolations(appDir)\n if (violations.length === 0) {\n io.stdout.write('check-deps: no app code imports a database provider outside the opener\\n')\n return 0\n }\n const openers = openersOf(appDir)\n for (const violation of violations)\n io.stderr.write(`${violation.file}:${violation.line}: ${advice(violation, openers)}\\n`)\n io.stderr.write(`See ${GUIDE}\\n`)\n return 1\n}\n","/**\n * The running frame's address, kept where the app's own data lives.\n *\n * The generated launcher prints the frame's URL with the launch token in it, and that token is the only way into a frame whose browser session ended.\n * So the address is written where the app keeps its data - its folder under `~/.acryl`, outside the repository - at mint time,\n * owner-only (0600 here; a restricted ACL on Windows), and atomically (a temp file in the same folder renamed over it, so a reader never sees half a\n * record). On every later boot the launcher reuses the stored token instead of minting a new one, so the address - and an open page - survives a\n * restart; `acrylblends open` prints it, and `acrylblends open --new` rotates it (the running host replaces the token in place through\n * `/api/acryl-frame/token/new`), invalidating the old one immediately.\n *\n * DSH's connection package mints its launch token as a random value with no configuration seam (BrowserAuth's constructor calls\n * `processLaunchToken`, cached per process owner). The one seam it offers is the instance field the class keeps: `browserAuth.launchToken`, which\n * `authorizeIndex` and `authenticatedUrl` read on every use. The launcher sets that field from the stored record (guarded: a DSH package whose shape\n * lacks it simply keeps its fresh token), and sets it again on a rotation. Nothing inside the pinned package is edited.\n */\nimport { spawnSync } from 'node:child_process'\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { connect } from 'node:net'\nimport { join, resolve } from 'node:path'\nimport type { Io } from './io'\n\n/** The record's file name inside the app home. */\nexport const FRAME_URL_FILE = 'frame-url.json'\n\n/** Where a running frame rotates its token: a route the launcher mounts on the frame's own router. */\nexport const FRAME_TOKEN_PATH = '/api/acryl-frame/token/new'\n\n/** What the launcher writes: the address, the token it carries, the process that wrote it, and when. */\nexport interface FrameUrlRecord {\n readonly version: 1\n readonly url: string\n readonly token: string\n readonly pid: number\n readonly writtenAt: string\n}\n\n/** Where an app keeps its own family: the instance plugin its launcher imports. */\nconst familyOf = (appRoot: string): string =>\n join(resolve(appRoot), 'plugins', 'instance', 'index.ts')\n\n/**\n * The app home the frame uses, as the app's own family chooses it (`plugins/instance`, which its launcher asks too): the CLI never keeps a second copy\n * of that rule. A folder without the family is not a frame app.\n */\nexport function frameHome(\n appRoot: string,\n env: Record<string, string | undefined> = process.env,\n): string {\n const family = familyOf(appRoot)\n if (!existsSync(family))\n throw new Error(`${appRoot} is not a frame app: it has no plugins/instance/index.ts`)\n const { selectInstance } = createRequire(import.meta.url)(family) as {\n selectInstance(options: { projectRoot: string; env?: Record<string, string | undefined> }): {\n home: string\n }\n }\n return selectInstance({ projectRoot: resolve(appRoot), env }).home\n}\n\nexport function frameUrlPath(home: string): string {\n return join(home, FRAME_URL_FILE)\n}\n\n/** Windows has no mode bits; the equivalent is an ACL with inheritance removed and only this account allowed. Best effort: a failure leaves the\n * inherited ACL, which is still the user's folder on a normal install. */\nfunction restrictToOwner(path: string): void {\n chmodSync(path, 0o600)\n if (process.platform !== 'win32') return\n const user = process.env.USERNAME\n if (user === undefined || user === '') return\n spawnSync('icacls', [path, '/inheritance:r', '/grant:r', `${user}:F`], {\n stdio: 'ignore',\n windowsHide: true,\n })\n}\n\n/** The token a frame address carries, if it carries one. */\nexport function tokenOfFrameUrl(url: string): string | undefined {\n try {\n const token = new URL(url).searchParams.get('token')\n return token === null || token === '' ? undefined : token\n } catch {\n return undefined\n }\n}\n\n/** Write the frame's address for `home`, owner-only and atomically. Throws when the folder or file cannot be written; the caller decides whether\n * that is fatal (the launcher prints why and keeps serving the app). */\nexport function writeFrameUrl(home: string, url: string, pid: number = process.pid): void {\n mkdirSync(home, { recursive: true })\n const record: FrameUrlRecord = {\n version: 1,\n url,\n token: tokenOfFrameUrl(url) ?? '',\n pid,\n writtenAt: new Date().toISOString(),\n }\n const target = frameUrlPath(home)\n const temporary = join(home, `${FRAME_URL_FILE}.${String(pid)}.tmp`)\n writeFileSync(temporary, `${JSON.stringify(record, null, 2)}\\n`, { mode: 0o600 })\n restrictToOwner(temporary)\n renameSync(temporary, target)\n restrictToOwner(target)\n}\n\n/** Remove the record, but only the one `pid` wrote: a host that is stopping must not erase the address of a newer one that already started. */\nexport function removeFrameUrl(home: string, pid: number = process.pid): void {\n const record = readFrameUrl(home)\n if (record === undefined || record.pid !== pid) return\n rmSync(frameUrlPath(home), { force: true })\n}\n\n/** The record, or undefined when there is none, it is not ours, or it is malformed. */\nexport function readFrameUrl(home: string): FrameUrlRecord | undefined {\n const path = frameUrlPath(home)\n if (!existsSync(path)) return undefined\n let parsed: unknown\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'))\n } catch {\n return undefined\n }\n if (typeof parsed !== 'object' || parsed === null) return undefined\n const record = parsed as Record<string, unknown>\n if (record.version !== 1) return undefined\n if (typeof record.url !== 'string' || record.url === '') return undefined\n if (typeof record.pid !== 'number' || !Number.isInteger(record.pid)) return undefined\n if (typeof record.writtenAt !== 'string') return undefined\n const token =\n typeof record.token === 'string' ? record.token : (tokenOfFrameUrl(record.url) ?? '')\n return { version: 1, url: record.url, token, pid: record.pid, writtenAt: record.writtenAt }\n}\n\n/** Whether something accepts a TCP connection at the record's address (the host process is alive and listening). */\nexport function frameIsListening(url: string, timeoutMs = 500): Promise<boolean> {\n let target: URL\n try {\n target = new URL(url)\n } catch {\n return Promise.resolve(false)\n }\n const port = Number(target.port === '' ? (target.protocol === 'https:' ? 443 : 80) : target.port)\n return new Promise((done) => {\n const socket = connect({ host: target.hostname, port })\n const finish = (listening: boolean) => {\n socket.destroy()\n done(listening)\n }\n socket.setTimeout(timeoutMs)\n socket.once('connect', () => finish(true))\n socket.once('timeout', () => finish(false))\n socket.once('error', () => finish(false))\n })\n}\n\n/** What `acrylblends open` found. */\nexport type FrameUrlLookup =\n | { readonly kind: 'running'; readonly record: FrameUrlRecord }\n | { readonly kind: 'stopped'; readonly record: FrameUrlRecord }\n | { readonly kind: 'none'; readonly home: string }\n\n/** Read the record and say whether the frame it names is still listening. */\nexport async function lookUpFrameUrl(\n appRoot: string,\n env: Record<string, string | undefined> = process.env,\n): Promise<FrameUrlLookup> {\n const home = frameHome(appRoot, env)\n const record = readFrameUrl(home)\n if (record === undefined) return { kind: 'none', home }\n return (await frameIsListening(record.url))\n ? { kind: 'running', record }\n : { kind: 'stopped', record }\n}\n\n/** Ask the running host to rotate its token, presenting the current one. Returns the new address, or a reason it failed. */\nasync function rotateFrameToken(\n record: FrameUrlRecord,\n): Promise<{ url: string } | { reason: string }> {\n let origin: string\n try {\n origin = new URL(record.url).origin\n } catch {\n return { reason: 'the stored address is not a URL' }\n }\n let response: Response\n try {\n response = await fetch(`${origin}${FRAME_TOKEN_PATH}`, {\n method: 'POST',\n headers: { 'x-frame-token': record.token, accept: 'application/json' },\n })\n } catch (cause) {\n return { reason: cause instanceof Error ? cause.message : String(cause) }\n }\n if (!response.ok) return { reason: `the host answered ${String(response.status)}` }\n const body = (await response.json().catch(() => undefined)) as { url?: unknown } | undefined\n if (typeof body?.url !== 'string' || body.url === '')\n return { reason: 'the host answered without an address' }\n return { url: body.url }\n}\n\n/**\n * `acrylblends open [dir] [--new]`: print the address of the frame running the app in this folder, so a page whose session ended (or the person, after\n * a restart) can open it again without restarting the host. Only a listening host's address is printed, and only the address goes to stdout, for copy\n * and paste. `--new` rotates the token in the running host first: the old address stops working at once and the new one is printed; with no host\n * running, the stored address is simply forgotten and the next start mints a fresh one.\n */\nexport async function openFrame(args: readonly string[], io: Io): Promise<number> {\n const fresh = args.includes('--new')\n const rest = args.filter((arg) => arg !== '--new')\n if (rest.length > 1) {\n io.stderr.write('usage: acrylblends open [dir] [--new]\\n')\n return 2\n }\n const directory = resolve(io.cwd, rest[0] ?? '.')\n if (!existsSync(familyOf(directory))) {\n io.stderr.write(\n `no frame has run in ${directory}: it is not a frame app (it has no plugins/instance)\\n`,\n )\n return 1\n }\n const found = await lookUpFrameUrl(directory)\n if (found.kind === 'none') {\n io.stderr.write(`no frame has run in ${directory} yet; start it with npm run dev\\n`)\n return 1\n }\n if (!fresh) {\n if (found.kind === 'running') {\n io.stdout.write(`${found.record.url}\\n`)\n return 0\n }\n io.stderr.write(\n `the frame of ${directory} is not running (its last address was minted ${found.record.writtenAt}); run npm run dev, then this command again\\n`,\n )\n return 1\n }\n if (found.kind === 'stopped') {\n rmSync(frameUrlPath(frameHome(directory)), { force: true })\n io.stderr.write(\n `no frame is running in ${directory}; its stored address was forgotten and the next npm run dev mints a new one\\n`,\n )\n return 0\n }\n const rotated = await rotateFrameToken(found.record)\n if ('reason' in rotated) {\n io.stderr.write(\n `the running frame did not rotate its address (${rotated.reason}); run npm run dev to start it with a fresh one\\n`,\n )\n return 1\n }\n writeFrameUrl(frameHome(directory), rotated.url)\n io.stdout.write(`${rotated.url}\\n`)\n return 0\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nexport interface Invocation {\n file: string\n args: string[]\n}\n\n/**\n * How to start a package manager without the shell option (Node warns about passing args to a shell, DEP0190, and a `.cmd` shim cannot be spawned directly).\n * Off Windows the command runs as it is. On Windows npm runs as `node npm-cli.js` (the one npm is running from, else the one beside `node`); any other\n * manager goes through `cmd.exe /d /s /c <name>.cmd`, whose arguments stay an array.\n */\nexport function packageManagerInvocation(\n command: string,\n args: readonly string[],\n platform: NodeJS.Platform = process.platform,\n env: NodeJS.ProcessEnv = process.env,\n exists: (path: string) => boolean = existsSync,\n execPath: string = process.execPath,\n): Invocation {\n if (platform !== 'win32') return { file: command, args: [...args] }\n if (command === 'npm') {\n const running = env.npm_execpath\n const cli =\n running !== undefined && /npm-cli\\.c?js$/.test(running)\n ? running\n : join(dirname(execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js')\n if (exists(cli)) return { file: execPath, args: [cli, ...args] }\n }\n return { file: env.ComSpec ?? 'cmd.exe', args: ['/d', '/s', '/c', `${command}.cmd`, ...args] }\n}\n","import { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline/promises'\nimport { packageManagerInvocation } from './spawn'\n\n/** Everything the CLI touches outside the filesystem. Tests pass their own. */\nexport interface Io {\n stdout: { write(text: string): unknown }\n stderr: { write(text: string): unknown }\n cwd: string\n /** Ask one question; `undefined` when there is no terminal to ask on. */\n ask: ((question: string) => Promise<string>) | undefined\n /** Install dependencies in a directory; resolves to the exit code. */\n install(directory: string): Promise<number>\n}\n\n/** The real terminal and `npm install`. */\nexport function processIo(): Io {\n const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY)\n return {\n stdout: process.stdout,\n stderr: process.stderr,\n cwd: process.cwd(),\n ask: interactive\n ? async (question) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n try {\n return await rl.question(question)\n } finally {\n rl.close()\n }\n }\n : undefined,\n install: (directory) =>\n new Promise((done) => {\n const { file, args } = packageManagerInvocation('npm', ['install'])\n const child = spawn(file, args, { cwd: directory, stdio: 'inherit' })\n child.on('error', () => done(1))\n child.on('exit', (code) => done(code ?? 1))\n }),\n }\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { resolve } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { UsageError } from './init'\nimport type { Io } from './io'\n\nexport interface KvImportOptions {\n readonly from: string\n readonly to: string\n readonly overwrite: boolean\n readonly table: string | undefined\n readonly migrations: string | undefined\n}\n\n/** Parse `kv import <kv.json> <database.sqlite> [--overwrite] [--table name] [--migrations dir]`. */\nexport function parseKvOptions(argv: readonly string[]): KvImportOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n const [action, from, to, ...rest] = parsed.positionals\n if (action !== 'import')\n throw new UsageError('kv takes one subcommand: kv import <kv.json> <database.sqlite>')\n if (from === undefined || to === undefined || rest.length > 0)\n throw new UsageError('kv import takes two arguments: the JSON file and the SQLite file')\n return {\n from,\n to,\n overwrite: parsed.values.overwrite ?? false,\n table: parsed.values.table,\n migrations: parsed.values.migrations,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n overwrite: { type: 'boolean' },\n table: { type: 'string' },\n migrations: { type: 'string' },\n },\n })\n\n/** The framework as the app has it installed (so the code that reads the file is the code the app runs), else this package's own. */\nasync function framework<T>(io: Io, subpath: string, own: string): Promise<T> {\n const here = createRequire(resolve(io.cwd, 'package.json'))\n try {\n return (await import(here.resolve(`acrylblends/${subpath}`))) as T\n } catch {\n return (await import(new URL(`../lib/${own}`, import.meta.url).href)) as T\n }\n}\n\n/**\n * Import a JSON-file kv store into the `kv` table of an app's SQLite database: the move from `acryl-kv-json` to SQL. The file is not changed.\n * The database is opened with the app's `migrations/` folder when it has one (a database whose schema is newer than the files given is refused),\n * and the table is created when missing. Refuses to overwrite a key the table already has unless `--overwrite`.\n */\nexport async function kvImport(options: KvImportOptions, io: Io): Promise<number> {\n const from = resolve(io.cwd, options.from)\n const to = resolve(io.cwd, options.to)\n if (!existsSync(from)) {\n io.stderr.write(`error: ${from} does not exist\\n`)\n return 1\n }\n const kvCore = await framework<typeof import('../lib/kv')>(io, 'kv', 'kv.js')\n const json = await framework<typeof import('../lib/kv/json')>(io, 'kv/json', 'kv/json.js')\n const onDb = await framework<typeof import('../lib/kv/db')>(io, 'kv/db', 'kv/db.js')\n const sqlite = await framework<typeof import('../lib/db/sqlite')>(io, 'db/sqlite', 'db/sqlite.js')\n const migrations =\n options.migrations !== undefined\n ? resolve(io.cwd, options.migrations)\n : existsSync(resolve(io.cwd, 'migrations'))\n ? resolve(io.cwd, 'migrations')\n : undefined\n const source = await json.openKv({ path: from })\n try {\n const database = await sqlite.openDb({\n path: to,\n ...(migrations === undefined ? {} : { migrations }),\n })\n try {\n const target = onDb.kvOnDb(\n database.db,\n options.table === undefined ? {} : { table: options.table },\n )\n const result = await kvCore.copyKv(source.kv, target, { overwrite: options.overwrite })\n io.stdout.write(\n `imported ${result.copied} ${result.copied === 1 ? 'key' : 'keys'} from ${options.from} into ${options.to}` +\n `${options.table === undefined ? '' : ` (table ${options.table})`}` +\n `${result.overwritten > 0 ? `, ${result.overwritten} overwritten` : ''}\\n`,\n )\n return 0\n } finally {\n await database.close()\n }\n } finally {\n await source.close()\n }\n}\n","import { spawn, spawnSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { createServer } from 'node:net'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport type { Io } from './io'\nimport { packageManagerInvocation } from './spawn'\n\n/**\n * `acrylblends preflight`: what a workshop machine needs, checked before the first command, with one line per check. PASS names what was\n * found; FIX is the exact command or address that fixes it; NOTE is something the first blocks do without. Exit 1 while anything needs a fix.\n * Every probe of the machine goes through `Probes`, so each check's text is tested for every platform on any platform.\n */\n\nexport type Status = 'PASS' | 'FIX' | 'NOTE'\n\nexport interface Check {\n name: string\n status: Status\n detail: string\n}\n\n/** A program's run: `code` is null when it could not be started (not installed). */\nexport interface Run {\n code: number | null\n stdout: string\n}\n\nexport interface Probes {\n platform: NodeJS.Platform\n nodeVersion: string\n env: NodeJS.ProcessEnv\n home: string\n run(file: string, args: readonly string[]): Promise<Run>\n onPath(program: string): boolean\n readFile(path: string): string | undefined\n /** The first port in [from, to] that 127.0.0.1 can listen on, or the error code that stopped every one. */\n freePort(from: number, to: number): Promise<number | string>\n}\n\n/** The Node the framework needs (the bin refuses an older one before loading anything). */\nexport const NODE_MINIMUM = [22, 19] as const\n/** The range an app's port is chosen in (plugins/instance: PORT_BASE and PORT_SPAN). */\nexport const APP_PORTS = [3100, 3999] as const\n\nconst AGENTS = ['claude', 'codex', 'pi'] as const\nconst AGENT_INSTALL =\n 'install one coding agent: npm i -g @openai/codex then codex login, or Claude Code (https://claude.com/claude-code), or npm i -g @earendil-works/pi-coding-agent with DEEPSEEK_API_KEY set'\n/** Off by default on Windows; a full workshop ran without it, so it is a NOTE: what to do if a path is ever too long. */\nconst LONG_PATHS_NOTE =\n 'off; if an install fails with ENAMETOOLONG or \"path too long\", in PowerShell as administrator: New-ItemProperty -Path HKLM:\\\\SYSTEM\\\\CurrentControlSet\\\\Control\\\\FileSystem -Name LongPathsEnabled -Value 1 -PropertyType DWORD -Force'\n\nconst firstLine = (text: string) => text.trim().split(/\\r?\\n/)[0]?.trim() ?? ''\n\nexport function checkNode(version: string): Check {\n const [major = 0, minor = 0] = version.split('.').map(Number)\n const [needMajor, needMinor] = NODE_MINIMUM\n const ok = major > needMajor || (major === needMajor && minor >= needMinor)\n return ok\n ? { name: 'node', status: 'PASS', detail: version }\n : {\n name: 'node',\n status: 'FIX',\n detail: `${version} is too old: install Node ${needMajor}.${needMinor} or newer (the LTS) from https://nodejs.org`,\n }\n}\n\nexport async function checkNpm(probes: Probes): Promise<Check> {\n const npm = packageManagerInvocation('npm', ['--version'], probes.platform, probes.env)\n const result = await probes.run(npm.file, npm.args)\n return result.code === 0\n ? { name: 'npm', status: 'PASS', detail: firstLine(result.stdout) }\n : {\n name: 'npm',\n status: 'FIX',\n detail: 'npm did not run: it comes with Node, reinstall Node from https://nodejs.org',\n }\n}\n\nexport async function checkGit(probes: Probes): Promise<Check> {\n const result = await probes.run('git', ['--version'])\n return result.code === 0\n ? { name: 'git', status: 'PASS', detail: firstLine(result.stdout).replace(/^git version /, '') }\n : { name: 'git', status: 'FIX', detail: 'install git from https://git-scm.com' }\n}\n\n/** Whether pi can reach DeepSeek: the key in the environment, or the one pi stored when its owner logged in (the panel's rule). */\nexport function piHasKey(probes: Pick<Probes, 'env' | 'home' | 'readFile'>): boolean {\n if (probes.env.DEEPSEEK_API_KEY) return true\n const stored = probes.readFile(\n join(probes.env.PI_CODING_AGENT_DIR ?? join(probes.home, '.pi', 'agent'), 'auth.json'),\n )\n if (stored === undefined) return false\n try {\n const key = (JSON.parse(stored) as Record<string, { key?: unknown } | undefined>).deepseek?.key\n return typeof key === 'string' && key !== ''\n } catch {\n return false\n }\n}\n\n/**\n * The coding agents the agent panel would offer: on the PATH, and pi only with a DeepSeek key. On Windows the panel also reads an npm shim to find\n * its script (plugins/agent-panel/launch.ts); that is plugin code an app carries, not something the command imports, so the PATH is checked here.\n */\nconst HINT: Record<(typeof AGENTS)[number], string> = {\n claude: 'be logged in: run claude once',\n codex: 'be logged in: codex login',\n pi: 'DeepSeek key found',\n}\n\nexport function checkAgent(probes: Probes): Check {\n const installed = AGENTS.filter((agent) => probes.onPath(agent))\n const usable = installed.filter((agent) => agent !== 'pi' || piHasKey(probes))\n if (usable.length > 0)\n return {\n name: 'agent',\n status: 'PASS',\n // Each agent says what it needs, so a line never asks for a login pi does not use: claude and codex use their own login, pi uses the DeepSeek key.\n detail: usable.map((agent) => `${agent} (${HINT[agent]})`).join(', '),\n }\n if (installed.includes('pi'))\n return {\n name: 'agent',\n status: 'FIX',\n detail:\n 'pi has no DeepSeek key: set DEEPSEEK_API_KEY, or log in to pi once, or install another agent',\n }\n return { name: 'agent', status: 'FIX', detail: AGENT_INSTALL }\n}\n\nexport async function checkPort(probes: Probes): Promise<Check> {\n const [from, to] = APP_PORTS\n const found = await probes.freePort(from, to)\n return typeof found === 'number'\n ? {\n name: 'port',\n status: 'PASS',\n detail: `127.0.0.1:${found} is free (apps use ${from}-${to})`,\n }\n : {\n name: 'port',\n status: 'FIX',\n detail: `nothing can listen on 127.0.0.1 in ${from}-${to} (${found}): allow Node in the firewall or security software, or free a port`,\n }\n}\n\n/** Only block 5 needs gh, so it is a NOTE, never a FIX. */\nexport async function checkGh(probes: Probes): Promise<Check> {\n if (!probes.onPath('gh'))\n return {\n name: 'gh',\n status: 'NOTE',\n detail:\n 'not installed; block 5 publishes with it: https://cli.github.com, then gh auth login',\n }\n const status = await probes.run('gh', ['auth', 'status'])\n return status.code === 0\n ? { name: 'gh', status: 'PASS', detail: 'logged in' }\n : { name: 'gh', status: 'NOTE', detail: 'not logged in; block 5 needs it: gh auth login' }\n}\n\nexport async function checkPowerShell(probes: Probes): Promise<Check> {\n const pwsh = probes.onPath('pwsh')\n const result = await probes.run('powershell.exe', [\n '-NoProfile',\n '-Command',\n '$PSVersionTable.PSVersion.ToString()',\n ])\n const version = result.code === 0 ? firstLine(result.stdout) : undefined\n if (pwsh)\n return {\n name: 'powershell',\n status: 'PASS',\n detail: `pwsh 7 found${version ? `, Windows PowerShell ${version}` : ''}`,\n }\n if (version !== undefined)\n return {\n name: 'powershell',\n status: 'PASS',\n detail: `Windows PowerShell ${version}: use the guide's PowerShell lines where they differ (&& and NODE_ENV=... need PowerShell 7)`,\n }\n return {\n name: 'powershell',\n status: 'NOTE',\n detail:\n 'powershell.exe did not run; use cmd.exe lines or install PowerShell 7 (winget install Microsoft.PowerShell)',\n }\n}\n\nexport async function checkLongPaths(probes: Probes): Promise<Check> {\n const result = await probes.run('reg', [\n 'query',\n 'HKLM\\\\SYSTEM\\\\CurrentControlSet\\\\Control\\\\FileSystem',\n '/v',\n 'LongPathsEnabled',\n ])\n const value = /LongPathsEnabled\\s+REG_DWORD\\s+0x([0-9a-f]+)/i.exec(result.stdout)?.[1]\n return value !== undefined && Number.parseInt(value, 16) === 1\n ? { name: 'long paths', status: 'PASS', detail: 'enabled' }\n : { name: 'long paths', status: 'NOTE', detail: LONG_PATHS_NOTE }\n}\n\n/** Git Bash rewrites a slash-led argument into a Windows path (`/board` becomes `C:/Program Files/Git/board`); the door undoes it from 1.0.7. */\nexport function checkGitBash(probes: Pick<Probes, 'env'>): Check | undefined {\n if (!probes.env.MSYSTEM) return undefined\n return {\n name: 'git bash',\n status: 'NOTE',\n detail:\n 'this shell rewrites /paths: the door handles it from acrylblends 1.0.7; on an older app run .acryl/bin/acryl from PowerShell, or npx acrylblends@latest upgrade',\n }\n}\n\nexport async function preflight(probes: Probes): Promise<Check[]> {\n const checks = [\n checkNode(probes.nodeVersion),\n await checkNpm(probes),\n await checkGit(probes),\n checkAgent(probes),\n await checkPort(probes),\n await checkGh(probes),\n ]\n if (probes.platform === 'win32') {\n checks.push(await checkPowerShell(probes), await checkLongPaths(probes))\n const gitBash = checkGitBash(probes)\n if (gitBash) checks.push(gitBash)\n }\n return checks\n}\n\nexport function formatChecks(checks: readonly Check[]): string {\n const width = Math.max(...checks.map((check) => check.name.length))\n const lines = checks.map(\n (check) => `${check.status.padEnd(4)} ${check.name.padEnd(width)} ${check.detail}`,\n )\n const fixes = checks.filter((check) => check.status === 'FIX').length\n lines.push(\n fixes === 0\n ? 'Ready for the workshop.'\n : `${fixes} to fix before the workshop: run each FIX line, then npx acrylblends@latest preflight again.`,\n )\n return `${lines.join('\\n')}\\n`\n}\n\n/** The real machine. A program that cannot start resolves with code null; nothing here throws. */\nexport function machineProbes(): Probes {\n return {\n platform: process.platform,\n nodeVersion: process.versions.node,\n env: process.env,\n home: homedir(),\n run: (file, args) =>\n new Promise((done) => {\n let stdout = ''\n const child = spawn(file, args, { stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true })\n const timer = setTimeout(() => child.kill(), 15_000)\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString()\n })\n child.on('error', () => {\n clearTimeout(timer)\n done({ code: null, stdout })\n })\n child.on('close', (code) => {\n clearTimeout(timer)\n done({ code, stdout })\n })\n }),\n onPath: (program) =>\n spawnSync(process.platform === 'win32' ? 'where' : 'which', [program], { windowsHide: true })\n .status === 0,\n readFile: (path) => {\n try {\n return readFileSync(path, 'utf8')\n } catch {\n return undefined\n }\n },\n freePort: async (from, to) => {\n let last = 'no port tried'\n for (let port = from; port <= to; port++) {\n const result = await new Promise<true | string>((done) => {\n const server = createServer()\n server.once('error', (error: NodeJS.ErrnoException) => done(error.code ?? error.message))\n server.listen({ port, host: '127.0.0.1', exclusive: true }, () =>\n server.close(() => done(true)),\n )\n })\n if (result === true) return port\n last = result\n // A refusal that is not \"in use\" (EACCES from a firewall or policy) holds for every port: stop at once.\n if (last !== 'EADDRINUSE') return last\n }\n return last\n },\n }\n}\n\nexport async function runPreflight(\n io: Pick<Io, 'stdout'>,\n probes: Probes = machineProbes(),\n): Promise<number> {\n const checks = await preflight(probes)\n io.stdout.write(formatChecks(checks))\n return checks.some((check) => check.status === 'FIX') ? 1 : 0\n}\n","/**\n * The two `src/app.ts` edits `acrylblends add` makes for a plugin it mounts (`mount.ts`): an import line after the last plugin import, and a\n * registry entry after `export const registry = {`. An app's own copy of these lines is not the owner's change; the upgrade recognises them,\n * keeps them on the new file it writes, and reports only the owner's other lines.\n */\n\nimport { MOUNTED, type Mounted } from './mount.ts'\n\n/** One registration line the framework wrote into `src/app.ts`. */\nexport interface AppRegistration {\n readonly plugin: Mounted\n readonly kind: 'import' | 'registry'\n /** The line without its line ending. */\n readonly line: string\n}\n\nconst eolOf = (text: string): string => (text.includes('\\r\\n') ? '\\r\\n' : '\\n')\n\n/** The import line `mount` writes for one plugin. */\nexport function registrationImport(plugin: Mounted): string {\n return `import * as ${plugin.local} from '../plugins/${plugin.name}/${plugin.entry ?? 'index.ts'}'`\n}\n\n/** The registry entry line `mount` writes for one plugin (its indent and comma included). */\nexport function registrationEntry(plugin: Mounted): string {\n return ` '${plugin.name}': ${plugin.local},`\n}\n\n/** Every registration line at the start of `text`, the line ending kept; a line is one registration. */\nfunction registrations(text: string, mounted: readonly Mounted[]): AppRegistration[] {\n const found: AppRegistration[] = []\n for (const raw of text.split('\\n')) {\n const line = raw.endsWith('\\r') ? raw.slice(0, -1) : raw\n const importMatch = /^import \\* as (\\w+) from '\\.\\.\\/plugins\\/([^/]+)\\/([^']+)'$/u.exec(line)\n if (importMatch !== null) {\n const plugin = mounted.find(\n (candidate) => candidate.name === importMatch[2] && candidate.local === importMatch[1],\n )\n if (plugin !== undefined) found.push({ plugin, kind: 'import', line })\n continue\n }\n const entryMatch = /^ {2}'([^']+)': (\\w+),$/u.exec(line)\n if (entryMatch !== null) {\n const plugin = mounted.find(\n (candidate) => candidate.name === entryMatch[1] && candidate.local === entryMatch[2],\n )\n if (plugin !== undefined) found.push({ plugin, kind: 'registry', line })\n }\n }\n return found\n}\n\n/** The text without the framework's registration lines, and those lines in file order. A line the released template already has is the\n * template's own (agent-panel's import ships with the agent template), never the add command's edit. */\nexport function stripRegistrations(\n text: string,\n mounted: readonly Mounted[] = MOUNTED,\n template?: string,\n): { readonly text: string; readonly edits: readonly AppRegistration[] } {\n const inTemplate = new Set(\n (template ?? '').split('\\n').map((raw) => (raw.endsWith('\\r') ? raw.slice(0, -1) : raw)),\n )\n const edits = registrations(text, mounted).filter((edit) => !inTemplate.has(edit.line))\n if (edits.length === 0) return { text, edits }\n const wanted = new Set(edits.map((edit) => edit.line))\n const kept = text.split('\\n').filter((raw) => {\n const line = raw.endsWith('\\r') ? raw.slice(0, -1) : raw\n return !wanted.has(line)\n })\n return { text: kept.join('\\n'), edits }\n}\n\n/**\n * Put the framework's registration lines back into a template, at the same places `mount` inserts them: every import after the last\n * `../plugins/` import, every registry entry after the `export const registry = {` line. Lines already present are left alone.\n */\nexport function withRegistrations(text: string, edits: readonly AppRegistration[]): string {\n const eol = eolOf(text)\n let next = text\n const imports = edits.filter(\n (edit) =>\n edit.kind === 'import' &&\n !next.split('\\n').some((raw) => (raw.endsWith('\\r') ? raw.slice(0, -1) : raw) === edit.line),\n )\n if (imports.length > 0) {\n const last = [...next.matchAll(/^import .* from '\\.\\.\\/plugins\\/[^']+'\\r?\\n/gmu)].at(-1)\n if (last !== undefined) {\n const at = last.index + last[0].length\n next =\n next.slice(0, at) + imports.map((edit) => `${edit.line}${eol}`).join('') + next.slice(at)\n }\n }\n const entries = edits.filter(\n (edit) =>\n edit.kind === 'registry' &&\n !next.split('\\n').some((raw) => (raw.endsWith('\\r') ? raw.slice(0, -1) : raw) === edit.line),\n )\n if (entries.length > 0) {\n const table = /^export const registry = \\{(\\r?\\n)/mu.exec(next)\n if (table !== null) {\n const at = table.index + table[0].length\n next =\n next.slice(0, at) + entries.map((edit) => `${edit.line}${eol}`).join('') + next.slice(at)\n }\n }\n return next\n}\n\n/** One line of the app that is its own, not the framework's registration. */\nexport interface ChangedLine {\n /** 1-based. */\n readonly line: number\n readonly text: string\n}\n\n/** One line of the app that is its own, not the framework's registration. */\nexport interface ChangedLine {\n /** 1-based. */\n readonly line: number\n readonly text: string\n}\n\n/**\n * The app's lines that the expected file does not have, in order: the owner's real changes. A line-level longest common subsequence, cut to\n * `limit` lines, so a change reads as the lines it is, not as a whole file.\n */\nexport function ownerChangeLines(actual: string, expected: string, limit = 5): ChangedLine[] {\n const lineOf = (raw: string | undefined): string =>\n raw === undefined ? '' : raw.endsWith('\\r') ? raw.slice(0, -1) : raw\n const left = actual.split('\\n')\n const right = expected.split('\\n')\n // One table of the longest common subsequence lengths, then the walk reports the app's lines it cannot match.\n const lengths: number[][] = Array.from({ length: left.length + 1 }, () =>\n new Array<number>(right.length + 1).fill(0),\n )\n for (let i = left.length - 1; i >= 0; i -= 1) {\n for (let j = right.length - 1; j >= 0; j -= 1) {\n lengths[i]![j] =\n lineOf(left[i]) === lineOf(right[j])\n ? (lengths[i + 1]![j + 1] ?? 0) + 1\n : Math.max(lengths[i + 1]![j] ?? 0, lengths[i]![j + 1] ?? 0)\n }\n }\n const changed: ChangedLine[] = []\n let i = 0\n let j = 0\n while (i < left.length && changed.length < limit) {\n if (j < right.length && lineOf(left[i]) === lineOf(right[j])) {\n i += 1\n j += 1\n continue\n }\n if (j < right.length && (lengths[i + 1]![j] ?? 0) < (lengths[i]![j + 1] ?? 0)) {\n // The expected file has a line here the app does not: a template line, not the owner's.\n j += 1\n continue\n }\n changed.push({ line: i + 1, text: lineOf(left[i]) })\n i += 1\n }\n return changed\n}\n","/** A unified diff of two texts (3 lines of context), for showing what an upgrade changes. Very large pairs are summarized instead. */\n\nexport interface DiffStat {\n readonly added: number\n readonly removed: number\n}\n\ntype Op = { kind: ' ' | '+' | '-'; line: string }\n\nfunction ops(a: string[], b: string[]): Op[] | undefined {\n if (a.length * b.length > 6_000_000) return undefined\n // Longest common subsequence by dynamic programming over the suffixes.\n const width = b.length + 1\n const table = new Uint32Array((a.length + 1) * width)\n for (let i = a.length - 1; i >= 0; i -= 1)\n for (let j = b.length - 1; j >= 0; j -= 1)\n table[i * width + j] =\n a[i] === b[j]\n ? (table[(i + 1) * width + j + 1] as number) + 1\n : Math.max(table[(i + 1) * width + j] as number, table[i * width + j + 1] as number)\n const out: Op[] = []\n let i = 0\n let j = 0\n while (i < a.length && j < b.length) {\n if (a[i] === b[j]) {\n out.push({ kind: ' ', line: a[i] as string })\n i += 1\n j += 1\n } else if ((table[(i + 1) * width + j] as number) >= (table[i * width + j + 1] as number)) {\n out.push({ kind: '-', line: a[i] as string })\n i += 1\n } else {\n out.push({ kind: '+', line: b[j] as string })\n j += 1\n }\n }\n for (; i < a.length; i += 1) out.push({ kind: '-', line: a[i] as string })\n for (; j < b.length; j += 1) out.push({ kind: '+', line: b[j] as string })\n return out\n}\n\nconst lines = (text: string): string[] =>\n text === '' ? [] : text.replace(/\\r\\n/gu, '\\n').replace(/\\n$/u, '').split('\\n')\n\n/** How many lines a change adds and removes; undefined counts when the pair is too large to compare. */\nexport function diffStat(from: string, to: string): DiffStat {\n const all = ops(lines(from), lines(to))\n if (all === undefined) return { added: lines(to).length, removed: lines(from).length }\n return {\n added: all.filter((op) => op.kind === '+').length,\n removed: all.filter((op) => op.kind === '-').length,\n }\n}\n\n/** `--- a/<name>`, `+++ b/<name>` and the hunks; empty when the texts are equal. */\nexport function unifiedDiff(name: string, from: string, to: string, context = 3): string {\n const a = lines(from)\n const b = lines(to)\n const all = ops(a, b)\n if (all === undefined)\n return `--- a/${name}\\n+++ b/${name}\\n(too large to compare line by line: ${a.length} lines became ${b.length})\\n`\n if (!all.some((op) => op.kind !== ' ')) return ''\n const out = [`--- a/${name}`, `+++ b/${name}`]\n let at = 0\n let aLine = 1\n let bLine = 1\n const positions = all.map((op) => {\n const here = { a: aLine, b: bLine }\n if (op.kind !== '+') aLine += 1\n if (op.kind !== '-') bLine += 1\n return here\n })\n while (at < all.length) {\n while (at < all.length && all[at]?.kind === ' ') at += 1\n if (at >= all.length) break\n const start = Math.max(0, at - context)\n let end = at\n for (let k = at; k < all.length; k += 1) {\n if (all[k]?.kind !== ' ') end = k\n else if (k - end > context * 2) break\n }\n const stop = Math.min(all.length, end + context + 1)\n const slice = all.slice(start, stop)\n const first = positions[start] as { a: number; b: number }\n out.push(\n `@@ -${first.a},${slice.filter((op) => op.kind !== '+').length} +${first.b},${slice.filter((op) => op.kind !== '-').length} @@`,\n )\n for (const op of slice) out.push(`${op.kind}${op.line}`)\n at = stop\n }\n return `${out.join('\\n')}\\n`\n}\n","/**\n * The scaffolds published acrylblends releases shipped, as `acrylblends upgrade` needs them: the template's route module and, where the composition root\n * exports `composeInto` today, `src/app.ts`. Fixtures are verbatim from each release (deduplicated by content hash under `legacy/<template>/<hash>/`, built by\n * `scripts/build-legacy-layouts.mjs` from the release tags). An app's file matches when it equals the released bytes with the app name substituted, or the\n * current template's file (already upgraded); anything else is the app's own and is never replaced.\n */\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { basename, dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { TEMPLATES, type Template, templateDir } from './templates'\nimport { compareVersions } from './version'\n\n/** The app name init substituted for `__APP_NAME__` in the template's files. */\nconst PLACEHOLDER = '__APP_NAME__'\n\n/** The frame row module's path inside an app: a template with it is framed by the host. */\nexport const FRAME_FILE = 'src/frame.ts'\n\n/** One distinct scaffold a release shipped for a template: the content hash the build script gave it, and its files (released bytes, placeholders left). */\nexport interface LegacyVariant {\n readonly id: string\n readonly files: ReadonlyMap<string, string>\n}\n\n/** What the upgrade's read of one scaffold file found. */\nexport type ScaffoldState = 'same' | 'update' | 'add' | 'conflict'\n\nexport interface ScaffoldFile {\n /** Path inside the app, with forward slashes. */\n readonly file: string\n /** The current template's text, which an upgrade writes for `update` and `add`. */\n readonly text: string\n readonly state: ScaffoldState\n}\n\n/** The upgrade's read of an app: its template and what the frame layout would do to its scaffold. */\nexport interface LegacyRead {\n readonly template: Template\n /** `src/frame.ts` first (it has no released predecessor), then the template's own files the frame layout replaces. */\n readonly files: readonly ScaffoldFile[]\n}\n\n/** The `legacy/` fixtures: beside `dist/` in the build and `src/` in a checkout, found by walking up like the templates. */\nexport function legacyRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'legacy')\n if (existsSync(join(candidate, 'layouts.json'))) return candidate\n const parent = dirname(dir)\n if (parent === dir) throw new Error('acrylblends cannot find its legacy layout fixtures')\n dir = parent\n }\n}\n\n/** Whether a template carries the frame row (only the framed templates can be upgraded onto it). `dir` overrides the template folder for tests. */\nexport function carriesFrame(template: Template, dir: string = templateDir(template)): boolean {\n return existsSync(join(dir, FRAME_FILE))\n}\n\n/** A template's file as a scaffolded app of `name` has it, or undefined when the current template has no such file. */\nfunction templateText(template: Template, file: string, name: string): string | undefined {\n const path = join(templateDir(template), file)\n return existsSync(path) ? readFileSync(path, 'utf8').replaceAll(PLACEHOLDER, name) : undefined\n}\n\nfunction read(path: string): string | undefined {\n return existsSync(path) ? readFileSync(path, 'utf8') : undefined\n}\n\nfunction walk(dir: string, base: string, into: Map<string, string>): void {\n for (const entry of readdirSync(dir)) {\n const path = join(dir, entry)\n if (statSync(path).isDirectory()) walk(path, `${base}${entry}/`, into)\n else into.set(`${base}${entry}`, readFileSync(path, 'utf8'))\n }\n}\n\n/** Every distinct scaffold the releases shipped for a template, sorted by hash. Empty when no release shipped this template. */\nexport function legacyVariants(template: Template): LegacyVariant[] {\n const dir = join(legacyRoot(), template)\n if (!existsSync(dir)) return []\n return readdirSync(dir)\n .filter((entry) => statSync(join(dir, entry)).isDirectory())\n .sort()\n .map((id) => {\n const files = new Map<string, string>()\n walk(join(dir, id), '', files)\n return { id, files }\n })\n}\n\n/** The app's name as `init` wrote it into the scaffold (package.json, then the folder). */\nexport function appNameOf(appDir: string): string {\n try {\n const manifest = JSON.parse(readFileSync(join(appDir, 'package.json'), 'utf8')) as {\n name?: unknown\n }\n if (typeof manifest.name === 'string' && manifest.name !== '') return manifest.name\n } catch {\n // The caller already refused an app without a readable package.json; fall through to the folder.\n }\n return basename(appDir)\n}\n\n/** The scaffold files the releases recorded for a template (the union across variants): what a fixture app starts from. */\nexport function legacyScaffold(template: Template): readonly string[] {\n const paths = new Set<string>()\n for (const variant of legacyVariants(template))\n for (const file of variant.files.keys()) paths.add(file)\n return [...paths].sort()\n}\n\n/**\n * The app's entry module when a release before 0.7.8 wrote it: those releases shipped `src/main.ts` importing `./plugins/...` where the app keeps its\n * plugins one folder up (`../plugins/...` since 0.7.8), so the app cannot start as scaffolded and the frame layout cannot be applied on top of it.\n */\nexport function brokenEntry(appDir: string): string | undefined {\n const file = join(appDir, 'src', 'main.ts')\n const text = read(file)\n return text !== undefined && /from '\\.\\/plugins\\//u.test(text) ? 'src/main.ts' : undefined\n}\n\n/** The first release whose scaffold an upgrade can carry forward: 0.7.8 fixed the entry module's plugin imports. */\nconst FIRST_FIXED = '0.7.8'\n\n/**\n * Whether every release that shipped a layout variant is before 0.7.8. Such an app's `src/main.ts` imports `./plugins/...` and cannot start (see\n * {@link brokenEntry}); the upgrade reports it and leaves the frame alone. Exposed for the fixture builders (the gate and the tests).\n */\nexport function variantFromBrokenEra(template: Template, id: string): boolean {\n const versions = releasedVersions()\n const index = JSON.parse(readFileSync(join(legacyRoot(), 'layouts.json'), 'utf8')) as {\n releases?: Array<{ version?: unknown; layouts?: Record<string, unknown> }>\n }\n const users = (index.releases ?? []).filter((release) => release.layouts?.[template] === id)\n if (users.length === 0) return false\n return users.every((release) => {\n const version = typeof release.version === 'string' ? release.version : ''\n return version !== '' && versions.includes(version) && compareVersions(version, FIRST_FIXED) < 0\n })\n}\n\n/** The releases the fixtures cover, oldest first, from the index the build script wrote (for the CLI's message). */\nexport function releasedVersions(): string[] {\n try {\n const index = JSON.parse(readFileSync(join(legacyRoot(), 'layouts.json'), 'utf8')) as {\n releases?: Array<{ version?: unknown }>\n }\n return (index.releases ?? [])\n .map((release) => release.version)\n .filter((version): version is string => typeof version === 'string')\n } catch {\n return []\n }\n}\n\n/** The scaffold a named release shipped for a template, from the index (undefined when that release had no such template, or the index lacks it). */\nexport function releasedVariant(template: Template, version: string): LegacyVariant | undefined {\n try {\n const index = JSON.parse(readFileSync(join(legacyRoot(), 'layouts.json'), 'utf8')) as {\n releases?: Array<{ version?: unknown; layouts?: Record<string, unknown> }>\n }\n const release = (index.releases ?? []).find((entry) => entry.version === version)\n const id = release?.layouts?.[template]\n return typeof id === 'string'\n ? legacyVariants(template).find((variant) => variant.id === id)\n : undefined\n } catch {\n return undefined\n }\n}\n\n/** The release range the fixtures cover, for a sentence: `0.3.0 through 1.1.0`, or `the published releases` when the index cannot be read. */\nexport function releasedRange(): string {\n const versions = releasedVersions()\n const first = versions[0]\n const last = versions.at(-1)\n if (first === undefined || last === undefined) return 'the published releases'\n return first === last ? first : `${first} through ${last}`\n}\n\n/**\n * Which template made this app: the template whose released (or current) scaffold files the app matches most. Undefined when none matches: the app's\n * scaffold was edited, or a release that shipped no template made it, and the upgrade leaves it alone.\n */\nexport function identifyTemplate(appDir: string, name: string): Template | undefined {\n let best: { template: Template; score: number } | undefined\n for (const template of TEMPLATES) {\n const variants = legacyVariants(template)\n if (variants.length === 0) continue\n let score = 0\n for (const variant of variants) {\n let matches = 0\n for (const [file, text] of variant.files)\n if (read(join(appDir, file)) === text.replaceAll(PLACEHOLDER, name)) matches += 1\n score = Math.max(score, matches)\n }\n // An app already upgraded (or scaffolded by this version) matches the current template instead.\n let current = 0\n for (const file of legacyScaffold(template)) {\n const text = templateText(template, file, name)\n if (text !== undefined && read(join(appDir, file)) === text) current += 1\n }\n score = Math.max(score, current)\n if (score > 0 && (best === undefined || score > best.score)) best = { template, score }\n }\n return best?.template\n}\n\n/**\n * What the frame layout would do to the app's scaffold of `template`: `same` when the app already has the current template's file, `update` when it has a\n * released one, `add` when it is missing, `conflict` when the app edited it (the caller refuses without `--force`).\n */\nexport function readLegacy(appDir: string, template: Template, name: string): LegacyRead {\n const variants = legacyVariants(template)\n const files: ScaffoldFile[] = []\n const frame = read(join(appDir, FRAME_FILE))\n if (carriesFrame(template)) {\n const text = templateText(template, FRAME_FILE, name)\n if (text !== undefined)\n files.push({\n file: FRAME_FILE,\n text,\n state: frame === undefined ? 'add' : frame === text ? 'same' : 'conflict',\n })\n }\n for (const file of legacyScaffold(template)) {\n const text = templateText(template, file, name)\n // A file the current template no longer has is left to the app: the upgrade has nothing to write for it.\n if (text === undefined) continue\n const app = read(join(appDir, file))\n const released = variants\n .map((variant) => variant.files.get(file))\n .filter((value): value is string => value !== undefined)\n .map((value) => value.replaceAll(PLACEHOLDER, name))\n files.push({\n file,\n text,\n state:\n app === undefined\n ? 'add'\n : app === text\n ? 'same'\n : released.includes(app)\n ? 'update'\n : 'conflict',\n })\n }\n return { template, files }\n}\n","import {\n cpSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs'\nimport { dirname, join, relative, resolve, sep } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport {\n type ChangedLine,\n ownerChangeLines,\n stripRegistrations,\n withRegistrations,\n} from './app-registration.ts'\nimport { diffStat, unifiedDiff } from './diff'\nimport { type FrameProfileSpecs, frameProfileForApp, frameProfileInput } from './frame-release'\nimport { APP_PLUGINS_FILE, appFrameRows, carriesComposeInto } from './frame-rows'\nimport {\n GENERATED_MARKER,\n type IdeProfileInput,\n type IdeProfileWritePlan,\n planIdeProfileWrite,\n writeIdeProfilePlan,\n} from './ide-profile'\nimport { UsageError } from './init'\nimport type { Io } from './io'\nimport {\n appNameOf,\n brokenEntry,\n carriesFrame,\n identifyTemplate,\n readLegacy,\n releasedRange,\n} from './legacy'\nimport {\n closure,\n copyPlugin,\n LEGACY_WARNING,\n LIBRARY_LIST,\n type LibraryPlugin,\n pluginsFolder,\n readLibrary,\n recordLibraryCopy,\n} from './library'\nimport { hashFolder, readHistory, readLock, recordLock } from './lock'\nimport { appendGitignore, mergeDependencies, mergeImports, mergeTooling } from './project'\nimport { type Template, templateDir } from './templates'\nimport { VERSION } from './version'\n\nexport interface UpgradeOptions {\n /** Library plugins to upgrade; none means every library plugin the app carries. */\n readonly names: readonly string[]\n readonly directory: string | undefined\n /** Show what would change and stop. */\n readonly dryRun: boolean\n /** Replace a file the app edited too (the edited version is saved first). */\n readonly force: boolean\n /** Print the diff of every changed file, not only of the edited ones. */\n readonly diff: boolean\n}\n\n/** Parse `upgrade [plugin...] [--dir <app>] [--dry-run] [--force] [--diff]`. */\nexport function parseUpgradeOptions(argv: readonly string[]): UpgradeOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n return {\n names: parsed.positionals,\n directory: parsed.values.dir,\n dryRun: parsed.values['dry-run'] === true,\n force: parsed.values.force === true,\n diff: parsed.values.diff === true,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n dir: { type: 'string' },\n 'dry-run': { type: 'boolean' },\n force: { type: 'boolean' },\n diff: { type: 'boolean' },\n },\n })\n\ntype FileState = 'same' | 'add' | 'update' | 'conflict' | 'remove' | 'extra'\n\ninterface PluginPlan {\n readonly plugin: LibraryPlugin\n /** The app does not have this plugin at all (a new requirement of one that it has). */\n readonly isNew: boolean\n /** Path inside the plugin folder to what happens to it. */\n readonly files: Map<string, FileState>\n /** The hashes of the files the current library would copy. */\n readonly fresh: Record<string, string>\n}\n\nconst posix = (path: string): string => path.split(sep).join('/')\n\nfunction readText(path: string): string {\n return readFileSync(path, 'utf8')\n}\n\n/** The library copies an app carries: the plugins listed in `plugins/library.json`, or when there is none the folders of `plugins/` that the library knows. */\nfunction carriedCopies(dir: string): string[] {\n const listFile = join(dir, LIBRARY_LIST)\n const listed = existsSync(listFile)\n ? ((JSON.parse(readFileSync(listFile, 'utf8')) as { plugins?: string[] }).plugins ?? [])\n : readdirSync(dir).filter((name) => statSync(join(dir, name)).isDirectory())\n return listed.filter((name) => existsSync(join(dir, name)))\n}\n\n/** The plugins the frame's host layer mounts by path: any framed app carries them, and an app made before the frame gets them here. */\nconst HOST_PLUGINS = ['acryl-web-server', 'acryl-page'] as const\n\n/** A library plugin a scaffold file imports: `from '../plugins/<name>/...'`. */\nconst PLUGIN_IMPORT = /from '\\.\\.\\/plugins\\/([^/']+)\\//gu\n\n/** The profile script the DSH-profile layout used; the host launcher replaced it, so an app upgraded from that layout drops it. */\nconst OLD_PROFILE_SCRIPT = 'scripts/ide-profile.mjs'\n\n/** One file the frame step writes: a template scaffold file, the frame row, or the generated composition row. */\ninterface FrameFileWrite {\n readonly file: string\n readonly text: string\n readonly state: 'add' | 'same' | 'update' | 'conflict'\n}\n\n/**\n * Scaffold files the app owns from its first edit: the release's copy is only the starting point, so an edited (or removed) one is never written, refused\n * over or backed up. A copy the app never edited is still the release's and is updated like any other scaffold file.\n */\nexport const KEPT_BY_THE_APP = new Set(['src/routes.ts'])\n\n/** What the frame row of every template imports from `src/routes.ts`: an app-owned copy without it cannot be shown in the frame. */\nconst FRAME_PREFIX_EXPORT = /\\bexport const FRAME_MOUNT_PREFIX\\b/u\n\n/** What `upgrade` does about the frame for one app: nothing (and why), or a plan computed before anything is written. */\nexport type FrameStep =\n | { readonly kind: 'unidentified' }\n | { readonly kind: 'broken-scaffold'; readonly file: string }\n | { readonly kind: 'no-frame-template'; readonly template: Template }\n | { readonly kind: 'unavailable'; readonly template: Template | undefined }\n | {\n readonly kind: 'planned'\n /** The template whose scaffold the app carries; undefined for an app built on its own (a registry blueprint), whose scaffold is never written. */\n readonly template: Template | undefined\n /** What the overlay's rows and the package.json merge are planned from. */\n readonly input: IdeProfileInput\n readonly profile: IdeProfileWritePlan\n /** The app's scaffold, its frame row and the generated composition row. */\n readonly files: readonly FrameFileWrite[]\n /** App-owned scaffold files left exactly as the app has them (edited or removed), each with whether the frame can mount it. */\n readonly kept: readonly { readonly file: string; readonly mountable: boolean }[]\n /**\n * Library plugins the app does not carry yet and gets: the frame's host plugins, and every plugin the scaffold files written here import (with what\n * they require), so a release whose template mounts a new plugin (the agent template's `home`) leaves an app that starts.\n */\n readonly addedPlugins: readonly string[]\n /** The DSH-profile script of the older layout, removed when it is acrylblends' own. */\n readonly removeProfileScript: boolean\n /** Paths the app edited; refused without `--force`. */\n readonly conflicts: readonly string[]\n /** For a scaffold file with the owner's own lines: the lines, so the refusal names them. */\n readonly conflictLines?: Readonly<Record<string, readonly ChangedLine[]>>\n readonly changed: boolean\n }\n\n/** The state of one generated file that already exists: our marker makes it ours to update, anything else is the app's. */\nfunction generatedState(existing: string | undefined, text: string): FrameFileWrite['state'] {\n if (existing === undefined) return 'add'\n if (existing === text) return 'same'\n return GENERATED_MARKER.test(existing.split('\\n', 1)[0] ?? '') ? 'update' : 'conflict'\n}\n\n/**\n * Plan the frame half of an upgrade: an app made by the previous published release gets the frame-hosted layout (`scripts/ide-host.mjs`, the overlay with\n * the app's rows, `frame/app-plugins.mjs` with its entry isolate, the mount config in its composition root, the host plugins), and the template's own files\n * are replaced only where the app still has the released ones. An app whose scaffold was edited is refused by the caller unless it passes --force; an app\n * of a template without a frame row yet, or one whose scaffold is not this release's, stays standalone and is told so.\n */\nexport function planFrameStep(\n appDir: string,\n library: ReadonlyMap<string, LibraryPlugin>,\n templateDirFor: (template: Template) => string = templateDir,\n): FrameStep {\n const name = appNameOf(appDir)\n // The releases before 0.7.8 wrote an entry module that cannot run (see brokenEntry): nothing the upgrade writes can make it boot.\n const broken = brokenEntry(appDir)\n if (broken !== undefined) return { kind: 'broken-scaffold', file: broken }\n const template = identifyTemplate(appDir, name)\n if (template === undefined) {\n // Not a template's scaffold: an app built on its own (a registry blueprint). Its scaffold is its own and never written; when it already carries the\n // frame row and the composition the frame calls, it gets the rest of the frame (the host's layout, the generated row, the host plugins) all the same.\n if (!ownsFrameRow(appDir)) return { kind: 'unidentified' }\n const specs = frameProfileForApp(appDir)\n if (specs === undefined) return { kind: 'unavailable', template: undefined }\n return planFrameWrites(appDir, library, name, specs, undefined, [], [], undefined)\n }\n if (!carriesFrame(template, templateDirFor(template)))\n return { kind: 'no-frame-template', template }\n const specs = frameProfileForApp(appDir)\n if (specs === undefined) return { kind: 'unavailable', template }\n const released = readLegacy(appDir, template, name)\n // src/routes.ts is the app's own from its first edit: an edited or removed one is never taken as the template's, never refused over, and never\n // overwritten with --force (the backup list takes its files from this list too). An untouched released copy is updated: the frame row needs the new one.\n const ownedByTheApp = (file: { file: string; state: FrameFileWrite['state'] }) =>\n KEPT_BY_THE_APP.has(file.file) && (file.state === 'conflict' || file.state === 'add')\n const kept = released.files.filter(ownedByTheApp).map((file) => {\n const path = join(appDir, file.file)\n return {\n file: file.file,\n mountable: existsSync(path) && FRAME_PREFIX_EXPORT.test(readText(path)),\n }\n })\n const files: FrameFileWrite[] = released.files\n .filter((file) => !ownedByTheApp(file))\n .map((file) => ({\n file: file.file,\n text: file.text,\n state: file.state,\n }))\n // The framework's own `add` registration lines are not the owner's edits: keep them on the app.ts the upgrade writes, and report only the rest.\n const appIndex = files.findIndex((file) => file.file === 'src/app.ts')\n const appFile = appIndex < 0 ? undefined : files[appIndex]\n let conflictLines: Record<string, readonly ChangedLine[]> | undefined\n if (appFile !== undefined && appFile.state === 'conflict') {\n const appTsPath = join(appDir, 'src', 'app.ts')\n const current = existsSync(appTsPath) ? readText(appTsPath) : undefined\n if (current !== undefined) {\n const stripped = stripRegistrations(current, undefined, appFile.text)\n const target = withRegistrations(appFile.text, stripped.edits)\n if (stripped.text === appFile.text) {\n files[appIndex] = {\n file: 'src/app.ts',\n text: target,\n state: target === current ? 'same' : 'update',\n }\n } else {\n conflictLines = { 'src/app.ts': ownerChangeLines(current, target) }\n }\n }\n }\n return planFrameWrites(appDir, library, name, specs, template, files, kept, conflictLines)\n}\n\n/**\n * Whether an app built on its own carries what the frame mounts: its routes row (`src/frame.ts`), a `src/routes.ts` that says where the frame shows it, and\n * the composition the generated row calls (`composeInto` in `src/app.ts`). A registry blueprint made in the frame has all three.\n */\nfunction ownsFrameRow(appDir: string): boolean {\n const at = (file: string) => join(appDir, 'src', file)\n return (\n existsSync(at('frame.ts')) &&\n existsSync(at('routes.ts')) &&\n FRAME_PREFIX_EXPORT.test(readText(at('routes.ts'))) &&\n existsSync(at('app.ts')) &&\n carriesComposeInto(readText(at('app.ts')))\n )\n}\n\n/** The part of the frame plan every framed app shares: the generated composition row, the host plugins, the launcher and overlay, and what conflicts. */\nfunction planFrameWrites(\n appDir: string,\n library: ReadonlyMap<string, LibraryPlugin>,\n name: string,\n specs: FrameProfileSpecs,\n template: Template | undefined,\n files: FrameFileWrite[],\n kept: readonly { readonly file: string; readonly mountable: boolean }[],\n conflictLines: Record<string, readonly ChangedLine[]> | undefined,\n): FrameStep {\n // The composition root as it will be once the scaffold is written: the plan may be about to replace it.\n const plannedAppTs = files.find((file) => file.file === 'src/app.ts')?.text\n const appTs =\n plannedAppTs ??\n (existsSync(join(appDir, 'src', 'app.ts'))\n ? readText(join(appDir, 'src', 'app.ts'))\n : undefined)\n const rows = appFrameRows(appDir, { ...(appTs === undefined ? {} : { appTs }), hasFrame: true })\n if (rows.appPlugins !== undefined) {\n const path = join(appDir, APP_PLUGINS_FILE)\n files.push({\n file: APP_PLUGINS_FILE,\n text: rows.appPlugins,\n state: generatedState(existsSync(path) ? readText(path) : undefined, rows.appPlugins),\n })\n }\n const imported = files\n .filter((file) => file.file.endsWith('.ts'))\n .flatMap((file) => [...file.text.matchAll(PLUGIN_IMPORT)].map((match) => match[1] as string))\n const addedPlugins = closure(\n [...new Set([...HOST_PLUGINS, ...imported])].filter((plugin) => library.has(plugin)),\n library,\n )\n .map((plugin) => plugin.name)\n .filter((plugin) => !existsSync(join(appDir, 'plugins', plugin)))\n const oldScript = join(appDir, OLD_PROFILE_SCRIPT)\n const removeProfileScript =\n existsSync(oldScript) && GENERATED_MARKER.test(readText(oldScript).split('\\n', 1)[0] ?? '')\n const input: IdeProfileInput = frameProfileInput(specs, name, rows.rows)\n const profile = planIdeProfileWrite(appDir, input)\n const conflicts = [\n ...files.filter((file) => file.state === 'conflict').map((file) => file.file),\n ...profile.files.filter((file) => file.state === 'conflict').map((file) => file.file),\n ]\n const changed =\n files.some((file) => file.state !== 'same') ||\n profile.manifestChanged ||\n profile.files.some((file) => file.state !== 'same') ||\n addedPlugins.length > 0 ||\n removeProfileScript\n return {\n kind: 'planned',\n template,\n input,\n profile,\n files,\n kept,\n addedPlugins,\n removeProfileScript,\n conflicts,\n ...(conflictLines === undefined ? {} : { conflictLines }),\n changed,\n }\n}\n\n/** The `frame` line `upgrade` prints: every file it changes, or why it leaves the app alone. */\nexport function frameReport(frame: FrameStep): string {\n if (frame.kind === 'broken-scaffold')\n return `${frame.file} imports './plugins/...', as acrylblends 0.3.0 through 0.7.7 wrote it (the plugins are one folder up; 0.7.8 fixed the paths), so the app cannot start: change those imports to '../plugins/...' and run again`\n if (frame.kind === 'unidentified')\n return `not the scaffold of any published acrylblends release (${releasedRange()}), so it was edited or another version made it: left on the framework alone`\n if (frame.kind === 'no-frame-template')\n return `the ${frame.template} template has no src/frame.ts yet: left standalone`\n if (frame.kind === 'unavailable')\n return 'left on the framework alone: not a desktop app (`acrylblends add desktop` makes it one), or ACRYLBLENDS_FRAME_PACKAGES is none'\n const parts: string[] = []\n for (const file of frame.files)\n if (file.state !== 'same')\n parts.push(`${file.file} ${file.state === 'update' ? 'updated' : 'added'}`)\n for (const file of frame.profile.files)\n if (file.state !== 'same')\n parts.push(`${file.file} ${file.state === 'update' ? 'updated' : 'added'}`)\n if (frame.profile.clientRowsAdded.length > 0)\n parts.push(\n `scripts/ide-host.mjs client rows added (${frame.profile.clientRowsAdded.join(', ')})`,\n )\n for (const plugin of frame.addedPlugins) parts.push(`plugins/${plugin} added`)\n if (frame.removeProfileScript) parts.push(`${OLD_PROFILE_SCRIPT} removed`)\n if (frame.profile.manifestChanged)\n parts.push('package.json merged (dev runs scripts/ide-host.mjs)')\n for (const { file, mountable } of frame.kept)\n if (!mountable)\n parts.push(\n `${file} kept as the app's (edited): it does not export FRAME_MOUNT_PREFIX, so the frame cannot show the app until it does (a fresh app's ${file} shows how)`,\n )\n return parts.length === 0 ? 'up to date' : parts.join(', ')\n}\n\n/**\n * `acrylblends upgrade`: bring the library plugins an app carries (the router, the server, the door, `extensions`, ...) up to the version of this package. Each file is compared with what\n * the app was given: an untouched file is replaced, a file the app edited stops the upgrade (it is shown as a diff and nothing changes) unless `--force`, which saves the edited version\n * under `.acryl/upgrade-backup/` first. Returns the exit code: 0 done or nothing to do, 1 refused or failed.\n */\nexport async function upgrade(options: UpgradeOptions, io: Io): Promise<number> {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n if (!existsSync(join(appDir, 'package.json')) || !existsSync(join(appDir, 'src'))) {\n io.stderr.write(\n `error: ${appDir} is not an app (no package.json and src/); run \"acrylblends init\" first\\n`,\n )\n return 1\n }\n const folder = pluginsFolder(appDir)\n if (folder.legacy) {\n io.stderr.write(\n `${LEGACY_WARNING}\\nerror: upgrade needs the one-folder layout (plugins/); move the plugins first\\n`,\n )\n return 1\n }\n const library = readLibrary()\n const carried = carriedCopies(folder.dir)\n const unknown = carried.filter((name) => !library.has(name))\n for (const name of options.names)\n if (!carried.includes(name)) {\n io.stderr.write(\n `error: the app carries no library plugin \"${name}\" (it carries: ${carried.filter((n) => library.has(n)).join(', ') || 'none'})\\n`,\n )\n return 1\n }\n const selected = (options.names.length > 0 ? [...options.names] : carried).filter((name) =>\n library.has(name),\n )\n // The frame half of the upgrade, planned before anything is written so a refusal leaves the folder exactly as it was.\n const frame = planFrameStep(appDir, library)\n if (frame.kind === 'planned' && frame.conflicts.length > 0 && !options.force) {\n const own = frame.conflictLines?.['src/app.ts'] ?? []\n const detail =\n own.length === 0\n ? ''\n : `\\n src/app.ts keeps the framework's plugin registrations; the app's own lines are ${own.map((line) => `line ${String(line.line)} (${JSON.stringify(line.text)})`).join(', ')}`\n io.stderr.write(\n `error: ${frame.conflicts.join(', ')} in ${appDir} ${frame.conflicts.length === 1 ? 'is' : 'are'} not the previous release's scaffold and acrylblends did not write ${frame.conflicts.length === 1 ? 'it' : 'them'}; nothing was changed.${detail} Move the app's version aside (or merge it into the template's) and run again, or run again with --force (the app's versions are saved under .acryl/upgrade-backup/ first)\\n`,\n )\n return 1\n }\n const frameChanged = frame.kind === 'planned' && frame.changed\n if (selected.length === 0 && !frameChanged) {\n io.stdout.write(\n frame.kind === 'planned'\n ? 'nothing to upgrade: the library plugins and the frame are up to date\\n'\n : `nothing to upgrade: the app carries no library plugins\\n ${'frame'.padEnd(22)} ${frameReport(frame)}\\n`,\n )\n return 0\n }\n\n // What the current library would copy, into a scratch folder inside the app (`.acryl/`, the app's own state folder, is ignored by git).\n const scratch = join(appDir, '.acryl', `upgrade-${process.pid}-${Date.now()}`)\n const lock = readLock(folder.dir)\n const history = readHistory()\n const plans: PluginPlan[] = []\n try {\n mkdirSync(join(scratch, 'plugins'), { recursive: true })\n const wanted = closure(selected, library)\n for (const plugin of wanted) copyPlugin(plugin, scratch)\n for (const plugin of wanted) {\n const here = join(folder.dir, plugin.name)\n const fresh = hashFolder(join(scratch, 'plugins', plugin.name))\n const files = new Map<string, FileState>()\n const isNew = !existsSync(here)\n const mine = isNew ? {} : hashFolder(here)\n const recorded = lock.plugins[plugin.name]?.files ?? {}\n const pristine = (file: string, hash: string): boolean =>\n recorded[file] === hash || (history[plugin.name]?.[file]?.includes(hash) ?? false)\n for (const [file, hash] of Object.entries(fresh)) {\n const have = mine[file]\n files.set(\n file,\n have === undefined\n ? 'add'\n : have === hash\n ? 'same'\n : pristine(file, have)\n ? 'update'\n : 'conflict',\n )\n }\n for (const [file, hash] of Object.entries(mine))\n if (!(file in fresh)) files.set(file, pristine(file, hash) ? 'remove' : 'extra')\n plans.push({ plugin, isNew, files, fresh })\n }\n\n // The report.\n const count = (plan: PluginPlan, state: FileState): string[] =>\n [...plan.files].filter(([, s]) => s === state).map(([file]) => file)\n io.stdout.write(`upgrade: the library plugins of ${appDir} to acrylblends ${VERSION}\\n`)\n let conflicts = 0\n let changes = 0\n for (const plan of plans) {\n const [updated, added, removed, edited, extra] = (\n ['update', 'add', 'remove', 'conflict', 'extra'] as const\n ).map((state) => count(plan, state))\n const label = plan.plugin.name.padEnd(22)\n conflicts += (edited as string[]).length\n if (plan.isNew) {\n changes += 1\n io.stdout.write(` ${label} new (required by a plugin of the app)\\n`)\n continue\n }\n if (\n (updated as string[]).length +\n (added as string[]).length +\n (removed as string[]).length +\n (edited as string[]).length ===\n 0\n ) {\n io.stdout.write(\n ` ${label} up to date${(extra as string[]).length > 0 ? ` (keeps ${(extra as string[]).length} file(s) of its own)` : ''}\\n`,\n )\n continue\n }\n changes += 1\n let plus = 0\n let minus = 0\n for (const file of [...(updated as string[]), ...(edited as string[])]) {\n const stat = diffStat(\n readText(join(folder.dir, plan.plugin.name, file)),\n readText(join(scratch, 'plugins', plan.plugin.name, file)),\n )\n plus += stat.added\n minus += stat.removed\n }\n const parts = [\n (updated as string[]).length > 0\n ? `${(updated as string[]).length} updated (+${plus} -${minus})`\n : '',\n (added as string[]).length > 0 ? `${(added as string[]).length} added` : '',\n (removed as string[]).length > 0 ? `${(removed as string[]).length} removed` : '',\n (edited as string[]).length > 0 ? `${(edited as string[]).length} EDITED HERE` : '',\n (extra as string[]).length > 0 ? `${(extra as string[]).length} of its own kept` : '',\n ].filter(Boolean)\n io.stdout.write(` ${label} ${parts.join(', ')}\\n`)\n }\n for (const name of unknown)\n io.stdout.write(` ${name.padEnd(22)} not in this library: left alone\\n`)\n io.stdout.write(` ${'frame'.padEnd(22)} ${frameReport(frame)}\\n`)\n\n // Diffs: always of the edited files (what would be lost), of every changed file with --diff.\n for (const plan of plans) {\n for (const [file, state] of plan.files) {\n if (\n !(\n state === 'conflict' ||\n (options.diff && (state === 'update' || state === 'add' || state === 'remove'))\n )\n )\n continue\n const name = `plugins/${plan.plugin.name}/${file}`\n const before = state === 'add' ? '' : readText(join(folder.dir, plan.plugin.name, file))\n const after =\n state === 'remove' ? '' : readText(join(scratch, 'plugins', plan.plugin.name, file))\n io.stdout.write(\n `\\n${state === 'conflict' ? 'EDITED HERE, the upgrade would replace it:\\n' : ''}${unifiedDiff(name, before, after)}`,\n )\n }\n }\n if (conflicts > 0 && !options.force) {\n io.stderr.write(\n `\\nerror: ${conflicts} file(s) were edited in this app and the upgrade would replace them; nothing was changed. Review the diffs above, move your changes out of the plugin (a plugin of your own in plugins/<name>/), or run again with --force (the edited versions are saved under .acryl/upgrade-backup/)\\n`,\n )\n return 1\n }\n if (options.dryRun) {\n io.stdout.write(\n `\\n${changes === 0 && !frameChanged ? 'everything is up to date' : 'dry run: nothing was changed'}\\n`,\n )\n return 0\n }\n if (changes === 0 && !frameChanged) {\n io.stdout.write('\\neverything is up to date\\n')\n return 0\n }\n\n // Apply.\n const backup = join(\n appDir,\n '.acryl',\n 'upgrade-backup',\n new Date().toISOString().replace(/[:.]/gu, '-'),\n )\n const addedDependencies: string[] = []\n for (const plan of plans) {\n const here = join(folder.dir, plan.plugin.name)\n if (plan.isNew) {\n // Copied the way `add` copies it: the plugin, and what it needs in package.json.\n mkdirSync(here, { recursive: true })\n cpSync(join(scratch, 'plugins', plan.plugin.name), here, { recursive: true })\n recordLibraryCopy(folder.dir, plan.plugin.name)\n } else {\n for (const [file, state] of plan.files) {\n const target = join(here, file)\n if (state === 'conflict') {\n mkdirSync(dirname(join(backup, plan.plugin.name, file)), { recursive: true })\n cpSync(target, join(backup, plan.plugin.name, file))\n }\n if (state === 'add' || state === 'update' || state === 'conflict') {\n mkdirSync(dirname(target), { recursive: true })\n cpSync(join(scratch, 'plugins', plan.plugin.name, file), target)\n } else if (state === 'remove') rmSync(target, { force: true })\n }\n recordLibraryCopy(folder.dir, plan.plugin.name)\n }\n recordLock(folder.dir, plan.plugin.name, plan.fresh)\n const plugin = plan.plugin\n if (plugin.imports) mergeImports(appDir, plugin.imports, posix(relative(appDir, folder.dir)))\n addedDependencies.push(...mergeDependencies(appDir, plugin.dependencies))\n addedDependencies.push(...mergeTooling(appDir, plugin).devDependencies)\n appendGitignore(appDir, plugin.gitignore)\n }\n // The package the app depends on moves with its plugins.\n const manifestFile = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(manifestFile, 'utf8')) as {\n dependencies?: Record<string, string>\n }\n if (\n manifest.dependencies?.acrylblends !== undefined &&\n manifest.dependencies.acrylblends !== `^${VERSION}`\n ) {\n manifest.dependencies.acrylblends = `^${VERSION}`\n writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\\n`)\n }\n // The frame last: the app's scaffold, the frame row, the generated composition row, the host plugins and the launcher/overlay write as `init` writes\n // them for a fresh app. A file of the app's own (no marker, or not the released template's) was refused above unless --force, which backs it up here.\n let frameBackups = 0\n if (frame.kind === 'planned' && frameChanged) {\n for (const file of [...frame.files, ...frame.profile.files]) {\n if (file.state !== 'conflict') continue\n mkdirSync(dirname(join(backup, file.file)), { recursive: true })\n cpSync(join(appDir, file.file), join(backup, file.file))\n frameBackups += 1\n }\n for (const plugin of frame.addedPlugins) {\n const known = library.get(plugin)\n if (known === undefined) continue\n copyPlugin(known, appDir)\n if (known.imports) mergeImports(appDir, known.imports, 'plugins')\n addedDependencies.push(...mergeDependencies(appDir, known.dependencies))\n addedDependencies.push(...mergeTooling(appDir, known).devDependencies)\n appendGitignore(appDir, known.gitignore)\n recordLibraryCopy(folder.dir, plugin)\n }\n for (const file of frame.files) {\n if (file.state === 'same') continue\n const target = join(appDir, file.file)\n mkdirSync(dirname(target), { recursive: true })\n writeFileSync(target, file.text)\n }\n // Re-planned against the manifest the plugin step and the host plugins left.\n writeIdeProfilePlan(appDir, planIdeProfileWrite(appDir, frame.input))\n if (frame.removeProfileScript) rmSync(join(appDir, OLD_PROFILE_SCRIPT), { force: true })\n }\n const upgradedPlugins = plans\n .filter((p) => [...p.files.values()].some((s) => s !== 'same' && s !== 'extra') || p.isNew)\n .map((p) => p.plugin.name)\n io.stdout.write(\n `\\nupgraded ${[...upgradedPlugins, ...(frameChanged ? ['the frame'] : [])].join(', ')}\\n`,\n )\n if (conflicts + frameBackups > 0)\n io.stdout.write(\n `the ${conflicts + frameBackups} edited file(s) replaced by --force are saved in ${posix(relative(appDir, backup))}/\\n`,\n )\n io.stdout.write(\n `acrylblends is now ^${VERSION} in package.json${addedDependencies.length > 0 ? `; dependencies to install: ${[...new Set(addedDependencies)].join(', ')}` : ''}: run npm install, then npm run check and npm test\\n`,\n )\n return 0\n } finally {\n rmSync(scratch, { recursive: true, force: true })\n }\n}\n","import { add, listPlugins, parseAddOptions } from './add'\nimport { buildClient, parseBuildClientOptions } from './build-client'\nimport { checkDeps, parseCheckDepsOptions } from './check-deps'\nimport { openFrame } from './frame-url'\nimport { init, parseInitOptions, UsageError } from './init'\nimport { type Io, processIo } from './io'\nimport { kvImport, parseKvOptions } from './kv-import'\nimport { runPreflight } from './preflight'\nimport { TEMPLATE_SUMMARY, TEMPLATES } from './templates'\nimport { parseUpgradeOptions, upgrade } from './upgrade'\nimport { VERSION } from './version'\n\nexport { VERSION }\n\nexport const HELP = `acrylblends ${VERSION} - scaffold Cordis apps\n\nUsage:\n acrylblends init [dir] [options] Create a Cordis app in an empty or new directory\n acrylblends add <plugin...> [--dir d] [--suite] Copy library plugins (and what they require) into an app;\n a folder path brings an external plugin into its plugins/;\n --suite also copies the port's conformance suite and vitest (add kv --suite)\n acrylblends upgrade [plugin...] [--dir d] [--dry-run] [--force] [--diff]\n Bring the library plugins an app carries (the router, the server, the door, ...) up to this\n version: untouched files are replaced, an edited file stops it (diff shown) unless --force\n acrylblends plugins List the plugin library\n acrylblends open [dir] Print the address of the frame running this app (the URL npm run dev printed),\n for a page that has to be opened again after its session ended\n acrylblends preflight Check this machine for the workshop: Node, npm, git, a coding agent, a free port\n (and on Windows PowerShell and long paths); one line per check, PASS or the fix\n acrylblends build-client [root] [out] Compile the client modules once for production (src/client to dist/client)\n acrylblends kv import <kv.json> <db> Move a JSON-file kv store into the kv table of a SQLite database [--overwrite] [--table t] [--migrations dir]\n acrylblends check-deps [dir] Fail when app code imports a database driver or opener outside the file named in\n package.json \"acryl\": { \"opener\": \"src/db.ts\" } (a port is imported, a provider is not)\n\nOptions for init:\n --template <name> one of the templates below (asked when not given); --agent means --template agent,\n --no-agent means --template blank\n --name <name> the project name (default: the directory name)\n --bare a plain app, without the ACRYL frame (the frame, its chat and agents and npm run desktop, are the default)\n --desktop the frame and the desktop shell (the default; kept for older scripts)\n --install run npm install after copying\n -y, --yes ask nothing; use defaults (the blank template, no install)\n\n -h, --help show this help\n -v, --version show the version\n\nTemplates (the same list as npm create acrylblends):\n${TEMPLATES.map((name) => ` ${name.padEnd(7)} ${TEMPLATE_SUMMARY[name]}`).join('\\n')}\n`\n\n/**\n * Run the CLI and return the exit code: 0 done, 1 failed or refused, 2 a usage error. Never calls `process.exit`.\n */\nexport async function runCli(argv: readonly string[], io: Io = processIo()): Promise<number> {\n const [command, ...args] = argv\n try {\n if (command === undefined || command === 'help' || command === '-h' || command === '--help') {\n io.stdout.write(HELP)\n return command === undefined ? 2 : 0\n }\n if (command === 'version' || command === '-v' || command === '--version') {\n io.stdout.write(`${VERSION}\\n`)\n return 0\n }\n if (command === 'init') return await init(parseInitOptions(args), io)\n if (command === 'add') return await add(parseAddOptions(args), io)\n if (command === 'upgrade') return await upgrade(parseUpgradeOptions(args), io)\n if (command === 'build-client') return await buildClient(parseBuildClientOptions(args), io)\n if (command === 'kv') return await kvImport(parseKv(args), io)\n if (command === 'check-deps') return checkDeps(parseCheckDeps(args), io)\n if (command === 'plugins') return listPlugins(io)\n if (command === 'open') return await openFrame(args, io)\n if (command === 'preflight') return await runPreflight(io)\n throw new UsageError(`unknown command \"${command}\"`)\n } catch (error) {\n if (error instanceof UsageError) {\n io.stderr.write(`error: ${error.message}\\n\\n${HELP}`)\n return 2\n }\n io.stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n }\n}\n\nfunction parseCheckDeps(args: readonly string[]) {\n try {\n return parseCheckDepsOptions(args)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n}\n\nfunction parseKv(args: readonly string[]) {\n return parseKvOptions(args)\n}\n"],"mappings":"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IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC;CAChD,MAAM,UAAU,QAAQ,QAAQ,QAAQ,CAAC,MAAM,IAAI,IAAI,IAAI,CAAC;CAC5D,MAAM,QAAQ,kBAAkB,KAAK,WAAW;CAChD,IAAI,UAAU,MAAM,OAAO;EAAE,MAAM;EAAa,OAAO,CAAC;CAAE;CAC1D,MAAM,SAAS,CAAC,GAAG,KAAK,GAAG,OAAO;CAClC,OAAO;EACL,MAAM,YAAY,QAAQ,MAAM,IAAI,sBAAsB,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG;EAC3F,OAAO,QAAQ,KAAK,QAAQ,IAAI,IAAI;CACtC;AACF;;;;;;AAOA,SAAS,aAAa,SAAiB,aAA6B;CAClE,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,wBAAwB,QAAQ;EAChC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,oDAAoD,YAAY;EAChE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,uBAAuB,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC,CAAC,WAAW,MAAM,IAAI;EACjF;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,sBAAsB,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC,CAAC,WAAW,OAAO,KAAK;EAClF,qBAAqB,KAAK,UAAU,mBAAmB,MAAM,CAAC;EAC9D,gCAAgC,KAAK,UAAU,oBAAoB;EACnE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,8CAA8C,gBAAgB;EAC9D;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,yBACE,UACA;EACF;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI;AACb;AAKA,SAAS,cAAc,UAAgD;CACrE,OACE,SAAS,MAAM,WAAW,MAAM,OAAO,MAAM,UAAA,oBAA0B,CAAC,EAAE,QAAA;AAG9E;;AAGA,SAAgB,eAAe,SAAyB;CACtD,MAAM,OAAO,QACV,YAAY,CAAC,CACb,WAAW,gBAAgB,GAAG,CAAC,CAC/B,WAAW,aAAa,EAAE;CAC7B,OAAO,eAAe,SAAS,KAAK,QAAQ;AAC9C;;AAGA,MAA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"}
|