@takuhon/cli 0.16.0 → 0.17.0
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/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/activity-command.ts","../src/file-activity-storage.ts","../src/backup.ts","../src/admin-command.ts","../src/dev-command.ts","../src/build-html.ts","../src/html-helpers.ts","../src/site.ts","../src/cv-html.ts","../src/file-asset-storage.ts","../src/file-storage.ts","../src/build-command.ts","../src/export-command.ts","../src/import-command.ts","../src/mcp-command.ts","../src/migrate-command.ts","../src/refresh-admin-command.ts","../src/restore-command.ts","../src/sync-command.ts","../src/validate-command.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * `@takuhon/cli` entry point — the `takuhon` command.\n *\n * Exposes `--version` / `--help`, the local profile commands (`validate`,\n * `migrate`, `restore`, `export`, `import`, `build`, `dev`, `admin`), `sync`\n * (push a local profile to a deployment), `activity` (sync / show the\n * developer-activity snapshot), and a pointer to `create-takuhon` for\n * scaffolding.\n *\n * `main` is pure (returns an exit code, never calls `process.exit`); the only\n * place that exits the process is {@link run}, invoked either when this module\n * is the entry script or by the bare-name `takuhon` package's `bin.mjs`, which\n * imports and calls `run()`. Keeping `process.exit` at that single boundary\n * lets tests import this module without terminating the test runner.\n */\n\nimport { readFileSync, realpathSync } from 'node:fs';\nimport { stdin, stdout } from 'node:process';\nimport { createInterface } from 'node:readline/promises';\nimport { fileURLToPath } from 'node:url';\n\nimport { runActivityShow, runActivitySync } from './activity-command.js';\nimport { runAdmin } from './admin-command.js';\nimport { runBuild } from './build-command.js';\nimport { runDev } from './dev-command.js';\nimport { runExport } from './export-command.js';\nimport { runImport } from './import-command.js';\nimport { runMcp } from './mcp-command.js';\nimport { runMigrate } from './migrate-command.js';\nimport { runRefreshAdmin } from './refresh-admin-command.js';\nimport { runRestore } from './restore-command.js';\nimport { runSync } from './sync-command.js';\nimport { runValidate } from './validate-command.js';\n\n// Source the reported version from package.json (read at runtime relative to\n// this module) so `takuhon --version` can never drift from the published\n// release — there is no hand-maintained version literal to fall out of sync.\nconst pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n};\nconst VERSION = pkg.version;\n\nconst HELP = `takuhon ${VERSION}\n\nTakuhon — open-source portable profile API server.\n\nUsage:\n takuhon --version Show the installed CLI version\n takuhon --help Show this help\n\nCommands:\n takuhon validate [path] Validate a takuhon.json (default: ./takuhon.json)\n takuhon migrate [path] [--to <v>] Forward-migrate a takuhon.json to a newer schema\n version (default target: latest). Backs up first.\n Add --out <file> to write elsewhere, --dry-run to preview.\n takuhon restore --from <backup> Restore a profile from a backup (prompts before\n overwriting; pass --yes to skip).\n takuhon export [path] [--output <f>] Serialise a takuhon.json to stdout (or --output file).\n takuhon import <file> [path] Import an exported profile into a takuhon.json,\n migrating to the current schema version. Backs up first.\n takuhon build [path] [--output <d>] Render a takuhon.json into a static site (HTML +\n JSON-LD, one page per locale). --base-url <url> adds\n absolute canonical/hreflang links.\n takuhon dev [path] [--port <n>] Serve a takuhon.json as a local static preview,\n re-rendered on each request (default port: 4321).\n --base-url <url> adds canonical/hreflang links.\n takuhon admin [path] [--port <n>] Run a local admin server: edit the profile through\n the form UI at /admin (writes takuhon.json, backs up\n first) with a preview at / (default port: 4322). Binds\n 127.0.0.1; prints a per-run token to paste into the form.\n takuhon admin update [path] Refresh a project's admin-dist/ with the admin form UI\n bundled in this @takuhon/cli (use after upgrading the\n CLI). Updates an existing admin-dist/ only.\n takuhon mcp [path] Serve a takuhon.json over the Model Context Protocol on\n stdio (read-only), so an MCP client such as Claude Desktop\n can read the profile. Re-reads the file per request.\n takuhon sync [path] --url <url> Push a takuhon.json to a deployment's admin API\n (PUT <url>/api/admin/profile). Reads the admin token\n from TAKUHON_ADMIN_TOKEN. --if-match <etag> opts into\n optimistic locking; --dry-run previews without sending.\n takuhon activity sync [path] Fetch the GitHub / WakaTime activity configured in\n settings.activity and store it as activity.json beside\n the profile. Secrets come from the environment only:\n TAKUHON_GITHUB_TOKEN (optional) / TAKUHON_WAKATIME_KEY.\n takuhon activity show [path] Print the stored activity snapshot (activity.json).\n\nScaffolding a new profile project:\n npx create-takuhon my-profile\n npx create-takuhon my-profile --license CC-BY-4.0\n`;\n\nasync function main(argv: readonly string[]): Promise<number> {\n const first = argv[0];\n\n if (first === '--version' || first === '-v') {\n process.stdout.write(`${VERSION}\\n`);\n return 0;\n }\n\n if (first === undefined || first === '--help' || first === '-h') {\n process.stdout.write(HELP);\n return 0;\n }\n\n if (first === 'validate') {\n return emit(runValidate(argv.slice(1)));\n }\n\n if (first === 'migrate') {\n return emit(runMigrate(argv.slice(1)));\n }\n\n if (first === 'export') {\n return emit(runExport(argv.slice(1)));\n }\n\n if (first === 'build') {\n return emit(runBuild(argv.slice(1)));\n }\n\n if (first === 'dev') {\n // `dev` runs a long-lived server and streams its own output, so it does not\n // go through `emit` (a one-shot result writer); it returns the exit code\n // directly and resolves only on graceful shutdown.\n return runDev(argv.slice(1));\n }\n\n if (first === 'admin') {\n if (argv[1] === 'update') {\n // One-shot bundle refresh, not the long-lived server: goes through `emit`.\n return emit(await runRefreshAdmin(argv.slice(2)));\n }\n // Long-lived server, like `dev`: returns the exit code directly.\n return runAdmin(argv.slice(1));\n }\n\n if (first === 'mcp') {\n // Long-lived stdio server, like `dev`: returns the exit code directly and\n // resolves on client disconnect / Ctrl-C. stdout is the MCP channel, so it\n // does not go through `emit`.\n return runMcp(argv.slice(1));\n }\n\n if (first === 'import') {\n return emit(runImport(argv.slice(1)));\n }\n\n if (first === 'restore') {\n // Only offer an interactive prompt on a real TTY; otherwise `runRestore`\n // refuses to overwrite without `--yes`, which is the safe default for\n // pipelines.\n const confirm = stdin.isTTY ? promptConfirm : undefined;\n return emit(await runRestore(argv.slice(1), { confirm }));\n }\n\n if (first === 'sync') {\n return emit(await runSync(argv.slice(1)));\n }\n\n if (first === 'activity') {\n const sub = argv[1];\n if (sub === 'sync') {\n return emit(await runActivitySync(argv.slice(2)));\n }\n if (sub === 'show') {\n return emit(await runActivityShow(argv.slice(2)));\n }\n const usage =\n 'Subcommands:\\n' +\n ' takuhon activity sync [path] Fetch and store the activity snapshot\\n' +\n ' takuhon activity show [path] Print the stored activity snapshot\\n' +\n 'Run `takuhon activity sync --help` for details.\\n';\n if (sub === '--help' || sub === '-h') {\n process.stdout.write(usage);\n return 0;\n }\n process.stderr.write(\n sub === undefined\n ? `takuhon: \\`activity\\` requires a subcommand.\\n${usage}`\n : `takuhon: unknown subcommand \\`${sub}\\` for \\`activity\\`.\\n${usage}`,\n );\n return 2;\n }\n\n process.stderr.write(\n `takuhon: unknown command '${first}'\\n` +\n `Run \\`takuhon --help\\` for usage. For scaffolding a new project, use \\`create-takuhon\\`.\\n`,\n );\n return 2;\n}\n\n/** Write a command outcome's streams and return its exit code. */\nfunction emit(outcome: { code: number; stdout: string; stderr: string }): number {\n if (outcome.stdout) process.stdout.write(outcome.stdout);\n if (outcome.stderr) process.stderr.write(outcome.stderr);\n return outcome.code;\n}\n\n/** Interactive [y/N] confirmation used by `restore` on a TTY. */\nasync function promptConfirm(message: string): Promise<boolean> {\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n const answer = await rl.question(`${message} `);\n return /^y(es)?$/i.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n\n/**\n * Process entry point: run {@link main} and exit with its code. This is the\n * only function that calls `process.exit`. Exported so the bare-name\n * `takuhon` package can invoke it after importing this module.\n */\nexport async function run(argv: readonly string[] = process.argv.slice(2)): Promise<void> {\n try {\n process.exit(await main(argv));\n } catch (error) {\n process.stderr.write(`takuhon: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n}\n\n/** True when this module was started directly (`node …/index.js`). */\nfunction isEntrypoint(): boolean {\n const entry = process.argv[1];\n if (entry === undefined) return false;\n try {\n return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));\n } catch {\n return false;\n }\n}\n\nif (isEntrypoint()) {\n void run();\n}\n","/**\n * `takuhon activity sync [path]` — fetch the GitHub / WakaTime activity\n * configured under `settings.activity` and store the snapshot as an\n * `activity.json` beside the profile, plus `takuhon activity show [path]` to\n * print what is stored.\n *\n * `activity` is a subcommand namespace (not a flag on `takuhon sync`): `sync`\n * already means \"push the profile to a deployment\", and the activity sync is a\n * different operation against different storage. Secrets are read from the\n * environment only (`TAKUHON_GITHUB_TOKEN` optional, `TAKUHON_WAKATIME_KEY`\n * required for WakaTime), never from flags or the profile, so they cannot leak\n * into shell history or any stored document.\n *\n * A sync that gathers no data never overwrites a good snapshot: the last-known\n * `activity.json` is kept and the command exits non-zero so a scheduled run\n * surfaces the failure.\n *\n * Exit codes:\n * 0 — snapshot synced and written (individual sources may still have been\n * skipped or failed; those are reported on stderr) / snapshot shown\n * 1 — the sync gathered no data (the last-known snapshot, if any, is kept and\n * nothing is written), or `show` found no stored snapshot\n * 2 — the command could not run: bad arguments, a missing/unreadable/non-JSON\n * profile, or activity is not configured / not enabled in\n * `settings.activity`\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport { fetchActivitySnapshot, isEmptySnapshot, type ActivitySecrets } from '@takuhon/activity';\nimport type { ActivitySettings } from '@takuhon/core';\n\nimport { ACTIVITY_FILENAME, FileActivityStorage } from './file-activity-storage.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\n/** Environment variable carrying the optional GitHub token. */\nconst GITHUB_TOKEN_ENV = 'TAKUHON_GITHUB_TOKEN';\n\n/** Environment variable carrying the WakaTime API key. */\nconst WAKATIME_KEY_ENV = 'TAKUHON_WAKATIME_KEY';\n\nconst SYNC_USAGE = `Usage: takuhon activity sync [path]\n\nFetch the GitHub / WakaTime activity configured under settings.activity in a\ntakuhon.json and store the result as activity.json beside it — the sibling\nsnapshot the renderer reads, kept outside the canonical profile. With no path,\nsyncs ./takuhon.json in the current working directory.\n\nSecrets are read from the environment, never from flags or the profile:\n ${GITHUB_TOKEN_ENV} Optional. Raises the GitHub rate limit and unlocks the\n contribution calendar (which is GraphQL/token-only).\n Languages work without it.\n ${WAKATIME_KEY_ENV} Required to read WakaTime coding time.\n\nA sync that gathers no data keeps the last-known activity.json — a good\nsnapshot is never overwritten with an empty one.\n\nExit codes: 0 = synced, 1 = no data gathered (last-known snapshot kept),\n2 = bad arguments / profile missing / unreadable / not JSON / activity not\nconfigured or not enabled in settings.activity.\n`;\n\nconst SHOW_USAGE = `Usage: takuhon activity show [path]\n\nPrint the stored activity snapshot (the activity.json beside the profile) as\nJSON. With no path, reads beside ./takuhon.json in the current working\ndirectory.\n\nExit codes: 0 = printed, 1 = no (or invalid) snapshot stored, 2 = bad\narguments.\n`;\n\nexport interface ActivityOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies, so tests can stub the network, secrets, and clock. */\nexport interface ActivitySyncDeps {\n /** HTTP client. Defaults to the global `fetch` (Node 22+). */\n fetch?: typeof fetch;\n /** Secret source. Defaults to reading the two environment variables. */\n getSecrets?: () => ActivitySecrets;\n /** Clock for `lastSyncedAt`; injectable for deterministic tests. */\n now?: () => Date;\n}\n\nfunction defaultSecrets(): ActivitySecrets {\n return {\n githubToken: process.env[GITHUB_TOKEN_ENV],\n wakatimeKey: process.env[WAKATIME_KEY_ENV],\n };\n}\n\n/** Parse the single optional positional path; `activity` takes no other options. */\nfunction parsePathArg(\n args: readonly string[],\n sub: 'sync' | 'show',\n): { path: string } | { error: string } {\n let path: string | undefined;\n for (const arg of args) {\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`activity ${sub}\\`.` };\n }\n if (path !== undefined) {\n return { error: `takuhon: \\`activity ${sub}\\` takes at most one path argument.` };\n }\n path = arg;\n }\n return { path: path ?? DEFAULT_PATH };\n}\n\n/** The `activity.json` path as shown to the user, relative to the path they gave. */\nfunction activityDisplayPath(profilePath: string): string {\n return join(dirname(profilePath), ACTIVITY_FILENAME);\n}\n\n/**\n * Read `settings.activity` out of the profile at `path`. The profile is not\n * schema-validated here — `activity sync` only needs the activity config, and\n * an unrelated validation error should not block a metrics refresh — but it\n * must be readable JSON and the config must be present, enabled, and name at\n * least one account to sync.\n */\nfunction readActivityConfig(\n path: string,\n): { config: ActivitySettings } | { outcome: ActivityOutcome } {\n const fail = (code: number, stderr: string): { outcome: ActivityOutcome } => ({\n outcome: { code, stdout: '', stderr },\n });\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return fail(\n 2,\n `takuhon: cannot read '${path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n );\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return fail(2, `takuhon: '${path}' is not valid JSON: ${detail}\\n`);\n }\n\n const settings =\n typeof data === 'object' && data !== null\n ? (data as { settings?: { activity?: ActivitySettings } }).settings\n : undefined;\n const config = settings?.activity;\n\n if (config === undefined) {\n return fail(\n 2,\n `takuhon: '${path}' has no settings.activity; nothing to sync.\\n` +\n `Opt in by adding settings.activity with \"enabled\": true and a github/wakatime username.\\n`,\n );\n }\n if (config.enabled !== true) {\n return fail(\n 2,\n `takuhon: activity is not enabled in '${path}'; nothing to sync.\\n` +\n `Set settings.activity.enabled to true to opt in.\\n`,\n );\n }\n const hasGithub = config.github?.username !== undefined && config.github.username !== '';\n const hasWakatime = config.wakatime?.username !== undefined && config.wakatime.username !== '';\n if (!hasGithub && !hasWakatime) {\n return fail(\n 2,\n `takuhon: settings.activity in '${path}' names no github or wakatime username; nothing to sync.\\n`,\n );\n }\n\n return { config };\n}\n\n/** Remove any literal occurrence of the secrets from a string before it is shown. */\nfunction redactSecrets(text: string, secrets: ActivitySecrets): string {\n let out = text;\n for (const secret of [secrets.githubToken, secrets.wakatimeKey]) {\n if (secret !== undefined && secret !== '') out = out.split(secret).join('***');\n }\n return out;\n}\n\n/**\n * Run `takuhon activity sync` against the arguments that follow the\n * subcommand (i.e. `process.argv.slice(2)` minus the leading\n * `\"activity\", \"sync\"`).\n */\nexport async function runActivitySync(\n args: readonly string[] = [],\n deps: ActivitySyncDeps = {},\n): Promise<ActivityOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: SYNC_USAGE, stderr: '' };\n }\n\n const parsed = parsePathArg(args, 'sync');\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon activity sync --help\\` for usage.\\n`,\n };\n }\n\n const read = readActivityConfig(parsed.path);\n if ('outcome' in read) return read.outcome;\n const { config } = read;\n\n const getSecrets = deps.getSecrets ?? defaultSecrets;\n const secrets = getSecrets();\n\n // A configured source whose secret is absent is skipped silently by the\n // fetcher; surface that here so a scheduled run's log explains the gap.\n const notes: string[] = [];\n const githubUser = config.github?.username;\n if (\n githubUser !== undefined &&\n githubUser !== '' &&\n config.github?.showContributions !== false &&\n !secrets.githubToken\n ) {\n notes.push(\n `takuhon: ${GITHUB_TOKEN_ENV} is not set; skipping GitHub contributions (languages need no token).\\n`,\n );\n }\n const wakatimeUser = config.wakatime?.username;\n if (\n wakatimeUser !== undefined &&\n wakatimeUser !== '' &&\n config.wakatime?.showCodingTime !== false &&\n !secrets.wakatimeKey\n ) {\n notes.push(`takuhon: ${WAKATIME_KEY_ENV} is not set; skipping WakaTime coding time.\\n`);\n }\n\n const snapshot = await fetchActivitySnapshot(config, secrets, {\n fetch: deps.fetch,\n now: deps.now,\n onError: (source, err) => {\n const detail = err instanceof Error ? err.message : String(err);\n notes.push(`takuhon: ${source} failed: ${redactSecrets(detail, secrets)}\\n`);\n },\n });\n\n const storage = new FileActivityStorage(parsed.path);\n const displayPath = activityDisplayPath(parsed.path);\n const stderr = notes.join('');\n\n if (isEmptySnapshot(snapshot)) {\n // Never replace a good snapshot with an empty one: keep the last-known\n // file (if any) and exit non-zero so cron-style callers notice.\n const existing = await storage.getActivitySnapshot();\n const kept =\n existing !== null ? `keeping the last-known snapshot at '${displayPath}'` : 'nothing written';\n return {\n code: 1,\n stdout: '',\n stderr: `${stderr}takuhon: the sync gathered no activity data; ${kept}.\\n`,\n };\n }\n\n await storage.saveActivitySnapshot(snapshot);\n const gathered = (['languages', 'contributions', 'codingTime', 'rank'] as const)\n .filter((field) => snapshot[field] !== undefined)\n .join(', ');\n return { code: 0, stdout: `synced activity -> ${displayPath} (${gathered})\\n`, stderr };\n}\n\n/**\n * Run `takuhon activity show` against the arguments that follow the\n * subcommand.\n */\nexport async function runActivityShow(args: readonly string[] = []): Promise<ActivityOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: SHOW_USAGE, stderr: '' };\n }\n\n const parsed = parsePathArg(args, 'show');\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon activity show --help\\` for usage.\\n`,\n };\n }\n\n const snapshot = await new FileActivityStorage(parsed.path).getActivitySnapshot();\n if (snapshot === null) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: no activity snapshot at '${activityDisplayPath(parsed.path)}'. Run \\`takuhon activity sync\\` first.\\n`,\n };\n }\n return { code: 0, stdout: `${JSON.stringify(snapshot, null, 2)}\\n`, stderr: '' };\n}\n","/**\n * Filesystem-backed {@link ActivityStorage} for the CLI. Persists the synced\n * developer-activity snapshot as an `activity.json` beside the project's\n * `takuhon.json` — the sibling-document layout the design mandates: the\n * snapshot is machine-written, externally sourced, and therefore lives outside\n * the canonical, owner-curated profile.\n *\n * Reads are deliberately forgiving: an absent, unparseable, or malformed file\n * resolves to `null` (never throws), because a missing snapshot is the normal\n * pre-sync / opt-out state and the renderer simply omits the activity section.\n * Writes are last-writer-wins through {@link writeFileAtomic}, so a reader\n * never observes a half-written snapshot.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\n\nimport { isActivitySnapshot, type ActivitySnapshot, type ActivityStorage } from '@takuhon/core';\n\nimport { writeFileAtomic } from './backup.js';\n\n/** Sibling-document filename holding the synced activity snapshot. */\nexport const ACTIVITY_FILENAME = 'activity.json';\n\nfunction isNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'ENOENT';\n}\n\n/** Absolute path of the `activity.json` beside the given profile path. */\nexport function activityPathFor(profilePath: string): string {\n return join(dirname(resolve(profilePath)), ACTIVITY_FILENAME);\n}\n\nfunction readSnapshotFile(path: string): ActivitySnapshot | null {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch (err) {\n if (isNotFound(err)) return null;\n throw err;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // A truncated or corrupt snapshot is treated as absent, not fatal: the\n // next sync rewrites it whole, and the renderer omits the section.\n return null;\n }\n return isActivitySnapshot(parsed) ? parsed : null;\n}\n\n/**\n * Synchronous read of the snapshot beside `profilePath` (or `null`), for the\n * synchronous render pipelines (`takuhon build` / `dev`) that cannot await the\n * {@link ActivityStorage} contract.\n */\nexport function readActivitySnapshotSync(profilePath: string): ActivitySnapshot | null {\n return readSnapshotFile(activityPathFor(profilePath));\n}\n\nexport class FileActivityStorage implements ActivityStorage {\n /** Absolute path of the `activity.json` this storage reads and writes. */\n readonly path: string;\n\n constructor(profilePath: string) {\n this.path = activityPathFor(profilePath);\n }\n\n // The filesystem work is synchronous, but the ActivityStorage contract is\n // async. Each method runs its body inside `Promise.resolve().then(...)` so a\n // synchronous throw becomes a rejected promise rather than an exception the\n // caller must catch outside `await` (same pattern as FileStorage).\n\n getActivitySnapshot(): Promise<ActivitySnapshot | null> {\n return Promise.resolve().then(() => readSnapshotFile(this.path));\n }\n\n saveActivitySnapshot(snapshot: ActivitySnapshot): Promise<void> {\n return Promise.resolve().then(() => {\n writeFileAtomic(this.path, `${JSON.stringify(snapshot, null, 2)}\\n`);\n });\n }\n}\n","/**\n * Local backup helpers shared by `takuhon migrate` and `takuhon restore`.\n *\n * Backups are written to a `.takuhon-backups/` directory co-located with the\n * profile file being mutated, rather than a `~/.takuhon/backups/{slug}/`\n * tree: a local `takuhon.json` operated on by path has no guaranteed slug,\n * and the restore flow documented in operational-lifecycle §4.1 / §4.3 reads\n * and writes `.takuhon-backups/...` relative to the working file. Keeping the\n * backups beside the source also mirrors the Vercel / Static adapter layout\n * (§3.1), which is the closest analogue for a local file workflow.\n *\n * Naming follows §3.1 (`takuhon-backup-v{version}-{timestamp}.json`) and the\n * pre-restore convention of §4.1 (`pre-restore-{timestamp}.json`). Timestamps\n * are ISO 8601 basic format in UTC at second precision; {@link createBackup}\n * implements the §3.3 overwrite guard by falling back to millisecond\n * precision when a same-second name already exists.\n */\n\nimport { mkdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { basename, dirname, join } from 'node:path';\n\n/** Directory (relative to the target file) that holds local backups. */\nexport const BACKUP_DIR_NAME = '.takuhon-backups';\n\n/**\n * Thrown by {@link createBackup} when a backup cannot be written without\n * overwriting an existing file even after disambiguating with milliseconds.\n */\nexport class BackupError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'BackupError';\n }\n}\n\n/**\n * Format `date` as a compact ISO 8601 basic-format UTC timestamp.\n *\n * @example\n * compactTimestamp(new Date('2026-05-11T12:00:00.123Z')) // \"20260511T120000Z\"\n * compactTimestamp(new Date('2026-05-11T12:00:00.123Z'), true) // \"20260511T120000.123Z\"\n */\nexport function compactTimestamp(date: Date, withMillis = false): string {\n const iso = date.toISOString(); // e.g. \"2026-05-11T12:00:00.123Z\"\n const trimmed = withMillis ? iso : iso.replace(/\\.\\d{3}Z$/, 'Z');\n // Drop the extended-format separators; the millisecond `.` (if kept) stays.\n return trimmed.replace(/[-:]/g, '');\n}\n\n/** Build the migration pre-backup filename for a given source `version`. */\nexport function migrateBackupName(version: string, date: Date, withMillis = false): string {\n return `takuhon-backup-v${version}-${compactTimestamp(date, withMillis)}.json`;\n}\n\n/** Build the pre-restore backup filename (operational-lifecycle §4.1). */\nexport function preRestoreName(date: Date, withMillis = false): string {\n return `pre-restore-${compactTimestamp(date, withMillis)}.json`;\n}\n\n/** Build the pre-import backup filename (operational-lifecycle §5.3 step 3). */\nexport function preImportName(date: Date, withMillis = false): string {\n return `pre-import-${compactTimestamp(date, withMillis)}.json`;\n}\n\n/** Build the pre-save backup filename for a `takuhon admin` write. */\nexport function preAdminSaveName(date: Date, withMillis = false): string {\n return `pre-admin-${compactTimestamp(date, withMillis)}.json`;\n}\n\n/** Resolve the `.takuhon-backups/` directory beside `targetPath`. */\nexport function backupDirFor(targetPath: string): string {\n return join(dirname(targetPath), BACKUP_DIR_NAME);\n}\n\n/**\n * Write `content` into `.takuhon-backups/` beside `targetPath`, returning the\n * path actually written.\n *\n * `name(withMillis)` yields the second-precision filename when `false` and a\n * millisecond-precision variant when `true`. The directory is created if\n * absent and writes use the `wx` flag so an existing file is never\n * overwritten (operational-lifecycle §3.3): a same-second collision retries\n * once at millisecond precision, and a further collision throws\n * {@link BackupError}.\n */\nexport function createBackup(params: {\n targetPath: string;\n content: string;\n name: (withMillis: boolean) => string;\n}): string {\n const dir = backupDirFor(params.targetPath);\n mkdirSync(dir, { recursive: true });\n\n const primary = join(dir, params.name(false));\n try {\n writeFileSync(primary, params.content, { flag: 'wx' });\n return primary;\n } catch (error) {\n if (!isAlreadyExists(error)) throw error;\n }\n\n const fallback = join(dir, params.name(true));\n try {\n writeFileSync(fallback, params.content, { flag: 'wx' });\n return fallback;\n } catch (error) {\n if (isAlreadyExists(error)) {\n throw new BackupError(\n `backup target already exists and could not be disambiguated: ${fallback}`,\n { cause: error },\n );\n }\n throw error;\n }\n}\n\nfunction isAlreadyExists(error: unknown): boolean {\n return (\n typeof error === 'object' && error !== null && (error as { code?: unknown }).code === 'EEXIST'\n );\n}\n\n/**\n * Write `content` to `target` atomically: stage it in a sibling temp file and\n * `rename` it into place. A `rename` within a directory is atomic, so a reader\n * never observes a half-written profile and an interrupted write (crash,\n * ENOSPC) cannot truncate the existing file — it is replaced whole or not at\n * all. The temp file is removed on failure. Pairs with the backup-before-write\n * ordering in the migrate / restore commands.\n */\nexport function writeFileAtomic(target: string, content: string): void {\n const tmp = join(dirname(target), `.${basename(target)}.${process.pid}.tmp`);\n try {\n writeFileSync(tmp, content, 'utf8');\n renameSync(tmp, target);\n } catch (error) {\n rmSync(tmp, { force: true });\n throw error;\n }\n}\n","/**\n * `takuhon admin [path] [--port <n>] [--base-url <url>]` — run a local admin\n * server (Spec §14.1 Phase 5 \"local Admin\"): the React admin form SPA at\n * `/admin`, the canonical admin API at `/api/admin/*` backed by the project's\n * `takuhon.json`, and the static HTML preview at `/`, all on a loopback port.\n *\n * It reuses `@takuhon/api`'s {@link createAdminApiApp} (validation, `If-Match`\n * optimistic locking, RFC 7807 errors) over a filesystem {@link FileStorage},\n * and serves the same admin bundle shipped in this package (the one\n * `create-takuhon` copies into scaffolded projects). Editing in the form writes\n * `takuhon.json`; reloading `/` re-renders it, closing the edit → preview loop.\n *\n * Image uploads work locally too: `POST /api/admin/assets` stores into an\n * `assets/` directory beside `takuhon.json` via {@link FileTakuhonAssetStorage},\n * and `GET /assets/*` serves them back (with `nosniff`), so the local pipeline\n * matches the Cloudflare adapter's R2 + delivery proxy.\n *\n * Authentication reuses the Bearer-token path: a fresh random token is minted\n * per run, the server binds `127.0.0.1`, and the token is printed for the\n * operator to paste into the SPA's sign-in form. Nothing is persisted.\n *\n * Like `takuhon dev`, the long-running server splits testable wiring\n * ({@link createAdminApp}, served via `app.fetch`) from the I/O lifecycle\n * ({@link runAdmin}).\n *\n * Exit codes:\n * 0 — served, then stopped cleanly (Ctrl-C), or `--help`\n * 2 — bad arguments, or the port was already in use\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { extname, join, resolve, sep } from 'node:path';\n\nimport { serve } from '@hono/node-server';\nimport {\n adminAssetSecurityHeaders,\n createAdminApiApp,\n noopAuditLogger,\n noopCachePurger,\n} from '@takuhon/api';\nimport { Hono } from 'hono';\n\nimport { handleRequest, loadSiteState } from './dev-command.js';\nimport { FileTakuhonAssetStorage } from './file-asset-storage.js';\nimport { FileStorage } from './file-storage.js';\nimport { resolveAdminBundleDir } from './scaffold/index.js';\n\nconst DEFAULT_PATH = 'takuhon.json';\nconst DEFAULT_PORT = 4322;\n\nconst USAGE = `Usage: takuhon admin [path] [--port <n>] [--base-url <url>]\n\nRun a local admin server: the form editor at /admin, the admin API at\n/api/admin/*, and a preview at /. With no path, edits ./takuhon.json. A fresh\nadmin token is printed each run; paste it into the sign-in form. The server\nbinds 127.0.0.1 only. Stop with Ctrl-C.\n\nOptions:\n --port <n> Port to listen on (default: ${DEFAULT_PORT}).\n --base-url <url> Site origin for the preview's canonical / hreflang links.\n\nExit codes: 0 = served then stopped, 2 = bad arguments / port in use.\n`;\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n '.woff2': 'font/woff2',\n '.map': 'application/json; charset=utf-8',\n};\n\nfunction contentTypeFor(file: string): string {\n return CONTENT_TYPES[extname(file).toLowerCase()] ?? 'application/octet-stream';\n}\n\nfunction isNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'ENOENT';\n}\n\n/**\n * Headers for the admin bundle over loopback http. Mirrors the production\n * `adminAssetSecurityHeaders()` (strict `script-src 'self'` CSP, nosniff, frame\n * DENY, no-store) but drops `strict-transport-security` (meaningless over http)\n * and `upgrade-insecure-requests` (would try to upgrade localhost subresources\n * to https and fail).\n */\nfunction localAdminHeaders(): Record<string, string> {\n const headers: Record<string, string> = { ...adminAssetSecurityHeaders() };\n delete headers['strict-transport-security'];\n const csp = headers['content-security-policy'];\n if (csp !== undefined) {\n headers['content-security-policy'] = csp\n .split(';')\n .map((directive) => directive.trim())\n .filter((directive) => directive !== '' && directive !== 'upgrade-insecure-requests')\n .join('; ');\n }\n return headers;\n}\n\nconst plain = (status: number, body: string): Response =>\n new Response(body, { status, headers: { 'content-type': 'text/plain; charset=utf-8' } });\n\n/** Escape a string for safe inclusion in a double-quoted HTML attribute. */\nfunction escapeHtmlAttr(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\n/**\n * Inject the per-run token into the served `index.html` as a `<meta>` tag so\n * the loopback SPA can auto-authenticate instead of asking the operator to\n * paste the token. This is done at response time on the local server only — the\n * shipped bundle files (and therefore the public Cloudflare deployment, which\n * serves them verbatim) never carry the tag, so the public sign-in gate is\n * unaffected. A `<meta>` is CSP-safe under the strict `script-src 'self'`\n * policy (it is not script), unlike an inline bootstrap `<script>`.\n */\nfunction injectLocalToken(html: string, token: string): string {\n const meta = `<meta name=\"takuhon-local-token\" content=\"${escapeHtmlAttr(token)}\" />`;\n if (html.includes('</head>')) return html.replace('</head>', `${meta}</head>`);\n // No <head> (e.g. a minimal test fixture): the browser still hoists a leading\n // <meta> into the document head during parsing, so querySelector finds it.\n return meta + html;\n}\n\n/**\n * Serve a file from the admin bundle for an `/admin*` request. Mirrors the\n * Cloudflare adapter's `serveAdminSpa`: strip the `/admin` prefix, map `/admin`\n * and `/admin/` to `index.html`, fall back to `index.html` for extension-less\n * paths (SPA routes), and 404 for missing assets. Guards against path traversal.\n * The `index.html` response gets the per-run token injected (see\n * {@link injectLocalToken}); other assets are served byte-for-byte.\n */\nfunction serveAdminBundle(\n bundleDir: string,\n method: string,\n pathname: string,\n token: string,\n): Response {\n if (method !== 'GET' && method !== 'HEAD') return plain(405, 'Method Not Allowed\\n');\n\n const rest = pathname.slice('/admin'.length);\n let rel: string;\n try {\n rel = decodeURIComponent(rest).replace(/^\\/+/, '');\n } catch {\n rel = '';\n }\n if (rel === '') rel = 'index.html';\n\n const root = resolve(bundleDir);\n let full = resolve(root, rel);\n if (full !== root && !full.startsWith(root + sep)) return plain(403, 'Forbidden\\n');\n\n let body: Buffer;\n try {\n body = readFileSync(full);\n } catch (err) {\n if (!isNotFound(err)) throw err;\n // SPA semantics: extension-less paths fall back to index.html; a missing\n // asset (e.g. /admin/assets/x.js) is a genuine 404.\n if (extname(rel) !== '') return plain(404, 'Not Found\\n');\n full = join(root, 'index.html');\n try {\n body = readFileSync(full);\n } catch {\n return plain(404, 'Not Found\\n');\n }\n }\n\n const contentType = contentTypeFor(full);\n const headers = new Headers(localAdminHeaders());\n headers.set('content-type', contentType);\n\n // The SPA entry document carries the per-run token so the loopback UI can\n // skip the sign-in form; every other asset is served unchanged.\n if (contentType.startsWith('text/html')) {\n const injected = injectLocalToken(body.toString('utf8'), token);\n return new Response(method === 'HEAD' ? null : injected, { status: 200, headers });\n }\n\n return new Response(method === 'HEAD' ? null : body, { status: 200, headers });\n}\n\n/**\n * Serve an uploaded asset from the project's `assets/` directory for a\n * `GET`/`HEAD /assets/*` request — the local counterpart of the Cloudflare\n * adapter's R2 delivery proxy. Forces `X-Content-Type-Options: nosniff`\n * (`security.md` §4.7) and a long-lived immutable cache (keys are unique). The\n * traversal guard lives in {@link FileTakuhonAssetStorage.readForServing}.\n */\nfunction serveLocalAsset(\n assetStorage: FileTakuhonAssetStorage,\n method: string,\n pathname: string,\n): Response {\n if (method !== 'GET' && method !== 'HEAD') return plain(405, 'Method Not Allowed\\n');\n let key: string;\n try {\n key = decodeURIComponent(pathname.slice(1)); // drop the leading '/', → 'assets/...'\n } catch {\n return plain(400, 'Bad Request\\n');\n }\n const asset = assetStorage.readForServing(key);\n if (asset === null) return plain(404, 'Not Found\\n');\n const headers = new Headers();\n headers.set('content-type', asset.contentType);\n headers.set('x-content-type-options', 'nosniff');\n headers.set('cache-control', 'public, max-age=31536000, immutable');\n return new Response(method === 'HEAD' ? null : asset.bytes, { status: 200, headers });\n}\n\nexport interface CreateAdminAppOptions {\n /** Path to the project's takuhon.json. */\n readonly path: string;\n /** Bearer token the SPA must present (minted per run by {@link runAdmin}). */\n readonly token: string;\n /** Optional preview origin for canonical / hreflang links. */\n readonly baseUrl?: string;\n /** Admin bundle directory; defaults to the bundle shipped in this package. */\n readonly bundleDir?: string;\n /**\n * Origin used to build absolute asset `publicUrl`s (the loopback origin,\n * passed by {@link runAdmin}). When omitted, `publicUrl` is the relative\n * `/assets/...` path, which the same-origin SPA / preview can still fetch.\n */\n readonly assetBaseUrl?: string;\n}\n\n/**\n * Build the local admin Hono app. Pure (no listening socket) so tests can\n * exercise it through `app.fetch(new Request(...))`.\n */\nexport function createAdminApp(opts: CreateAdminAppOptions): Hono {\n const bundleDir = opts.bundleDir ?? resolveAdminBundleDir();\n const storage = new FileStorage(opts.path);\n // Uploads land in `assets/` beside takuhon.json and are served below at\n // /assets/*, mirroring the Cloudflare adapter's R2 + GET /assets/* pairing.\n const assetStorage = new FileTakuhonAssetStorage(opts.path, { publicBaseUrl: opts.assetBaseUrl });\n const app = new Hono();\n\n app.route(\n '/api/admin',\n createAdminApiApp({\n storage,\n assetStorage,\n getAdminToken: () => opts.token,\n // Loopback, same-origin SPA: no Origin allowlist needed (empty = skip).\n getAdminOrigins: () => [],\n cachePurger: noopCachePurger,\n auditLogger: noopAuditLogger,\n }),\n );\n\n // Unmatched `/api/admin/*` paths stay inside the mounted admin app (its\n // bearer middleware still gates them — an unknown path is a 401, never a\n // fall-through to the public preview), so only non-admin paths reach here.\n app.all('*', (c) => {\n const { method, path } = c.req;\n if (path === '/admin' || path.startsWith('/admin/')) {\n return serveAdminBundle(bundleDir, method, path, opts.token);\n }\n if (path.startsWith('/assets/')) {\n return serveLocalAsset(assetStorage, method, path);\n }\n const state = loadSiteState(opts.path, opts.baseUrl);\n const res = handleRequest(method, path, state);\n return new Response(method === 'HEAD' ? null : res.body, {\n status: res.status,\n headers: { 'content-type': res.contentType },\n });\n });\n\n return app;\n}\n\n/** Injectable sinks + token so tests run without a TTY or randomness. */\nexport interface AdminDeps {\n stdout?: (text: string) => void;\n stderr?: (text: string) => void;\n /** Override the per-run token (tests); defaults to 32 random bytes. */\n token?: string;\n}\n\ninterface ParsedArgs {\n path: string;\n port: number;\n baseUrl?: string;\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let portRaw: string | undefined;\n let baseUrl: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n if (arg === '--port' || arg === '--base-url') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--port') portRaw = value;\n else baseUrl = value;\n i++;\n continue;\n }\n if (arg.startsWith('--port=')) {\n portRaw = arg.slice('--port='.length);\n if (portRaw === '') return { error: 'takuhon: `--port` requires a value.' };\n continue;\n }\n if (arg.startsWith('--base-url=')) {\n baseUrl = arg.slice('--base-url='.length);\n if (baseUrl === '') return { error: 'takuhon: `--base-url` requires a value.' };\n continue;\n }\n if (arg.startsWith('-')) return { error: `takuhon: unknown option \\`${arg}\\` for \\`admin\\`.` };\n if (path !== undefined) return { error: 'takuhon: `admin` takes at most one path argument.' };\n path = arg;\n }\n\n let port = DEFAULT_PORT;\n if (portRaw !== undefined) {\n if (!/^\\d+$/.test(portRaw)) {\n return {\n error: `takuhon: \\`--port\\` must be an integer between 1 and 65535 (got \\`${portRaw}\\`).`,\n };\n }\n const n = Number(portRaw);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n return {\n error: `takuhon: \\`--port\\` must be an integer between 1 and 65535 (got \\`${portRaw}\\`).`,\n };\n }\n port = n;\n }\n\n if (baseUrl !== undefined && !isHttpUrl(baseUrl)) {\n return { error: 'takuhon: `--base-url` must be an absolute http(s) URL.' };\n }\n\n return { path: path ?? DEFAULT_PATH, port, baseUrl: baseUrl?.replace(/\\/+$/, '') };\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\n/**\n * Run `takuhon admin`. Resolves with the process exit code: it stays pending\n * while the server runs and resolves on graceful shutdown (Ctrl-C). The source\n * file may be absent — the form's empty-state import flow handles that — so\n * there is no missing-file fast-fail.\n */\nexport async function runAdmin(\n args: readonly string[] = [],\n deps: AdminDeps = {},\n): Promise<number> {\n const out = deps.stdout ?? ((text: string) => void process.stdout.write(text));\n const err = deps.stderr ?? ((text: string) => void process.stderr.write(text));\n\n if (args[0] === '--help' || args[0] === '-h') {\n out(USAGE);\n return 0;\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n err(`${parsed.error}\\nRun \\`takuhon admin --help\\` for usage.\\n`);\n return 2;\n }\n\n const token = deps.token ?? randomBytes(32).toString('base64url');\n const app = createAdminApp({\n path: parsed.path,\n token,\n baseUrl: parsed.baseUrl,\n assetBaseUrl: `http://127.0.0.1:${String(parsed.port)}`,\n });\n\n return await new Promise<number>((resolvePromise) => {\n let closing = false;\n\n // Signal handlers are registered only once listening succeeds (below), so a\n // bind failure (EADDRINUSE) leaves none attached — mirroring `runDev`.\n function shutdown(): void {\n if (closing) return;\n closing = true;\n process.removeListener('SIGINT', shutdown);\n process.removeListener('SIGTERM', shutdown);\n server.close(() => resolvePromise(0));\n }\n\n const server = serve({ fetch: app.fetch, hostname: '127.0.0.1', port: parsed.port }, () => {\n const origin = `http://127.0.0.1:${parsed.port}`;\n out(`takuhon admin: editing ${parsed.path} at ${origin}/ (Ctrl-C to stop)\\n`);\n out(` Admin UI: ${origin}/admin\\n`);\n out(` Admin token: ${token}\\n`);\n out(` Paste the token into the sign-in form; it is valid for this run only.\\n`);\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n });\n\n server.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EADDRINUSE') {\n err(`takuhon: port ${parsed.port} is already in use; pass --port <n> to choose another.\\n`);\n } else {\n err(`takuhon: ${error.message}\\n`);\n }\n resolvePromise(2);\n });\n });\n}\n","/**\n * `takuhon dev [path] [--port <n>] [--base-url <url>]` — serve a `takuhon.json`\n * as a local static preview (Spec §14.1 Phase 1 \"edit → preview\").\n *\n * This is the in-memory companion of `takuhon build`: it renders the same\n * per-locale surface (via the shared {@link generateSite}) but serves it over\n * `node:http` instead of writing to disk — no bundler, no Hono/Vite/React, no\n * remote/secret coupling. The source is re-read and re-rendered on every\n * request, so editing `takuhon.json` and reloading the browser shows changes\n * with no watch machinery; an invalid file is served as a live error page so it\n * can be fixed in place.\n *\n * A long-running server does not fit the pure `runXxx → {code,stdout,stderr}`\n * shape of the other commands, so the testable logic is split from the I/O:\n * {@link loadSiteState} (source → route map), {@link resolveRoute},\n * {@link contentType}, and {@link handleRequest} are pure; {@link createDevServer}\n * and {@link runDev} are the thin `node:http` wrapper around them.\n *\n * Exit codes:\n * 0 — served, then stopped cleanly (Ctrl-C), or `--help`\n * 2 — the command could not run: bad arguments, a missing/unreadable file,\n * or the port was already in use\n */\n\nimport { readFileSync } from 'node:fs';\nimport { createServer, type Server } from 'node:http';\n\nimport { applyPublicPrivacyFilter, normalize, validate } from '@takuhon/core';\n\nimport { escapeHtml } from './build-html.js';\nimport { readActivitySnapshotSync } from './file-activity-storage.js';\nimport { generateSite } from './site.js';\n\nconst DEFAULT_PATH = 'takuhon.json';\nconst DEFAULT_PORT = 4321;\n\nconst USAGE = `Usage: takuhon dev [path] [--port <n>] [--base-url <url>]\n\nServe a takuhon.json as a local static preview (one page per locale, plus a\nprint-ready CV page at /cv) — the same surface \\`takuhon build\\` produces. With\nno path, serves ./takuhon.json. The file is re-read and re-rendered on every\nrequest, so edit it and reload the browser to see changes. Stop with Ctrl-C.\n\nOptions:\n --port <n> Port to listen on (default: ${DEFAULT_PORT}).\n --base-url <url> Site origin (e.g. https://me.example). Enables absolute\n canonical and hreflang links; without it those are omitted.\n\nThe public privacy filter is applied (meta.privacy is honoured). An invalid\ntakuhon.json is served as an error page so you can fix it and reload.\n\nExit codes: 0 = served then stopped, 2 = bad arguments / file missing /\nunreadable / port in use.\n`;\n\n/** Injectable output sinks so tests can capture streams without a real TTY. */\nexport interface DevDeps {\n stdout?: (text: string) => void;\n stderr?: (text: string) => void;\n}\n\ninterface ParsedArgs {\n path: string;\n port: number;\n baseUrl?: string;\n}\n\n/**\n * The current render state of the source file. Recomputed per request so edits\n * are reflected on reload; a read/parse/validate failure becomes a served error\n * page rather than crashing the server.\n */\nexport type SiteState =\n | { readonly ok: true; readonly pages: ReadonlyMap<string, string> }\n | { readonly ok: false; readonly status: number; readonly message: string };\n\n/** A fully-resolved HTTP response, independent of the `node:http` socket. */\nexport interface DevResponse {\n readonly status: number;\n readonly contentType: string;\n readonly body: string;\n}\n\n/**\n * Read, validate, and render `path` into a route→HTML map. Any failure (missing\n * file, bad JSON, invalid profile) yields a 500 state carrying a human-readable\n * message — validation errors are formatted with their JSON Pointers, mirroring\n * `takuhon build`. The public privacy filter is applied, exactly as `build` does.\n */\nexport function loadSiteState(path: string, baseUrl?: string): SiteState {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return { ok: false, status: 500, message: `cannot read '${path}'.` };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { ok: false, status: 500, message: `'${path}' is not valid JSON: ${detail}` };\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n ok: false,\n status: 500,\n message: `'${path}' is not a valid takuhon profile:\\n${lines.join('\\n')}`,\n };\n }\n\n const filtered = applyPublicPrivacyFilter(normalize(result.data));\n // Re-read the synced activity snapshot alongside the profile on every load,\n // so a fresh `takuhon activity sync` shows up on the next request. Only read\n // when the owner opted in; generateSite re-checks the gate.\n const activitySnapshot =\n filtered.settings.activity?.enabled === true ? readActivitySnapshotSync(path) : null;\n // `dev` always renders the CV page too (at /cv), so it is previewable without\n // a flag; `takuhon build` gates the same output behind `--cv`.\n const pages = new Map(\n generateSite(filtered, { baseUrl, activitySnapshot, cv: true }).map((p) => [p.route, p.html]),\n );\n return { ok: true, pages };\n}\n\n/**\n * Normalize a request path to a site route key. A trailing `index.html` is\n * stripped and a trailing slash is added, so `/ja`, `/ja/`, and\n * `/ja/index.html` all map to `/ja/` (and `/`, ``, `/index.html` to `/`).\n */\nexport function resolveRoute(urlPath: string): string {\n let p = urlPath;\n try {\n p = decodeURIComponent(urlPath);\n } catch {\n // Keep the raw path on malformed percent-encoding; it will simply 404.\n }\n p = p.replace(/\\/index\\.html$/, '/');\n if (p === '' || p === '/') return '/';\n if (!p.startsWith('/')) p = `/${p}`;\n if (!p.endsWith('/')) p = `${p}/`;\n return p;\n}\n\n/**\n * Content-type for a served route. The static surface is HTML-only (the build\n * emits one HTML page per locale and no assets/JSON), so this is constant; it is\n * a named function to keep the response shape ready should the surface grow.\n */\nexport function contentType(_route: string): string {\n return 'text/html; charset=utf-8';\n}\n\n/** Resolve a request to a response against the current {@link SiteState}. Pure. */\nexport function handleRequest(method: string, urlPath: string, state: SiteState): DevResponse {\n if (method !== 'GET' && method !== 'HEAD') {\n return { status: 405, contentType: 'text/plain; charset=utf-8', body: 'Method Not Allowed\\n' };\n }\n if (!state.ok) {\n return {\n status: state.status,\n contentType: contentType('/'),\n body: renderErrorPage(state.message),\n };\n }\n const route = resolveRoute(urlPath);\n const html = state.pages.get(route);\n if (html === undefined) {\n return {\n status: 404,\n contentType: contentType(route),\n body: renderNotFoundPage(route, [...state.pages.keys()]),\n };\n }\n return { status: 200, contentType: contentType(route), body: html };\n}\n\n/**\n * Create the preview server. Each request recomputes the site state (so edits\n * are live on reload) and delegates to the pure {@link handleRequest}. Not\n * started here — the caller binds a port. Importing this module has no side\n * effects.\n */\nexport function createDevServer(opts: { path: string; baseUrl?: string }): Server {\n return createServer((req, res) => {\n const method = req.method ?? 'GET';\n const state = loadSiteState(opts.path, opts.baseUrl);\n const response = handleRequest(method, pathnameOf(req.url ?? '/'), state);\n res.writeHead(response.status, { 'Content-Type': response.contentType });\n if (method === 'HEAD') res.end();\n else res.end(response.body);\n });\n}\n\n/**\n * Run `takuhon dev`. Resolves with the process exit code: it stays pending while\n * the server runs and resolves on graceful shutdown (Ctrl-C). Argument and\n * missing-file errors return before any port is bound.\n */\nexport async function runDev(args: readonly string[] = [], deps: DevDeps = {}): Promise<number> {\n const out = deps.stdout ?? ((text: string) => void process.stdout.write(text));\n const err = deps.stderr ?? ((text: string) => void process.stderr.write(text));\n\n if (args[0] === '--help' || args[0] === '-h') {\n out(USAGE);\n return 0;\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n err(`${parsed.error}\\nRun \\`takuhon dev --help\\` for usage.\\n`);\n return 2;\n }\n\n // Fast-fail only when the source is missing/unreadable — there is nothing to\n // preview. A present-but-invalid file is served as a live error page instead,\n // so it can be fixed without restarting the server.\n try {\n readFileSync(parsed.path, 'utf8');\n } catch {\n err(\n `takuhon: cannot read '${parsed.path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n );\n return 2;\n }\n\n const server = createDevServer({ path: parsed.path, baseUrl: parsed.baseUrl });\n\n return await new Promise<number>((resolve) => {\n let closing = false;\n const shutdown = (): void => {\n if (closing) return;\n closing = true;\n process.removeListener('SIGINT', shutdown);\n process.removeListener('SIGTERM', shutdown);\n server.close(() => resolve(0));\n // Drop keep-alive sockets so close() does not hang (Node >= 18.2).\n server.closeAllConnections();\n };\n\n server.once('error', (error: NodeJS.ErrnoException) => {\n // The signal handlers are only registered once listening starts, so a\n // bind failure (e.g. EADDRINUSE) has none to detach.\n if (error.code === 'EADDRINUSE') {\n err(`takuhon: port ${parsed.port} is already in use; pass --port <n> to choose another.\\n`);\n } else {\n err(`takuhon: ${error.message}\\n`);\n }\n resolve(2);\n });\n\n // Bind to loopback only: a local preview must not expose draft profile\n // content (or the dev error pages) to the rest of the network.\n server.listen(parsed.port, '127.0.0.1', () => {\n out(\n `takuhon dev: serving ${parsed.path} at http://localhost:${parsed.port}/ (Ctrl-C to stop)\\n`,\n );\n // Surface a current validation problem up front; the browser shows it too.\n const state = loadSiteState(parsed.path, parsed.baseUrl);\n if (!state.ok) {\n err(\n `takuhon dev: ${parsed.path} is not a valid profile yet; the preview will show the error until it is fixed.\\n`,\n );\n }\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n });\n });\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let portRaw: string | undefined;\n let baseUrl: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--port' || arg === '--base-url') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--port') portRaw = value;\n else baseUrl = value;\n i++;\n continue;\n }\n if (arg.startsWith('--port=')) {\n const value = arg.slice('--port='.length);\n if (value === '') return { error: 'takuhon: `--port` requires a value.' };\n portRaw = value;\n continue;\n }\n if (arg.startsWith('--base-url=')) {\n const value = arg.slice('--base-url='.length);\n if (value === '') return { error: 'takuhon: `--base-url` requires a value.' };\n baseUrl = value;\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`dev\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `dev` takes at most one path argument.' };\n }\n path = arg;\n }\n\n let port = DEFAULT_PORT;\n if (portRaw !== undefined) {\n const parsedPort = parsePort(portRaw);\n if (parsedPort === undefined) {\n return {\n error: `takuhon: \\`--port\\` must be an integer between 1 and 65535 (got \\`${portRaw}\\`).`,\n };\n }\n port = parsedPort;\n }\n\n if (baseUrl !== undefined && !isHttpUrl(baseUrl)) {\n return { error: 'takuhon: `--base-url` must be an absolute http(s) URL.' };\n }\n\n return {\n path: path ?? DEFAULT_PATH,\n port,\n // Drop any trailing slash so URL joins are predictable.\n baseUrl: baseUrl?.replace(/\\/+$/, ''),\n };\n}\n\nfunction parsePort(value: string): number | undefined {\n if (!/^\\d+$/.test(value)) return undefined;\n const n = Number(value);\n return Number.isInteger(n) && n >= 1 && n <= 65535 ? n : undefined;\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\n/** Extract the path portion of a request URL, dropping any query/fragment. */\nfunction pathnameOf(url: string): string {\n try {\n return new URL(url, 'http://localhost').pathname;\n } catch {\n return url;\n }\n}\n\n/** Minimal HTML document used by the dev-only error and 404 pages. */\nfunction devPage(title: string, body: string): string {\n return (\n `<!DOCTYPE html>\\n<html lang=\"en\">\\n<head>\\n` +\n `<meta charset=\"utf-8\">\\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\\n` +\n `<title>${escapeHtml(title)}</title>\\n` +\n `<style>body{margin:0;font:16px/1.6 system-ui,-apple-system,sans-serif;color:#1a1a1a}` +\n `main{max-width:42rem;margin:2rem auto;padding:0 1.25rem}` +\n `pre{background:#f6f6f6;padding:1rem;border-radius:.4rem;overflow:auto;white-space:pre-wrap}` +\n `code{background:#f2f2f2;padding:.1rem .3rem;border-radius:.2rem}</style>\\n` +\n `</head>\\n<body>\\n<main>\\n${body}\\n</main>\\n</body>\\n</html>\\n`\n );\n}\n\nfunction renderErrorPage(message: string): string {\n return devPage(\n 'takuhon dev — error',\n `<h1>takuhon dev</h1>\\n<p>The profile could not be rendered:</p>\\n` +\n `<pre>${escapeHtml(message)}</pre>\\n<p>Fix the file and reload.</p>`,\n );\n}\n\nfunction renderNotFoundPage(route: string, routes: readonly string[]): string {\n const links = routes\n .map((r) => `<li><a href=\"${escapeHtml(r)}\">${escapeHtml(r)}</a></li>`)\n .join('');\n return devPage(\n 'takuhon dev — 404',\n `<h1>404</h1>\\n<p>No page for <code>${escapeHtml(route)}</code>.</p>\\n` +\n `<p>Available pages:</p>\\n<ul>${links}</ul>`,\n );\n}\n","/**\n * Pure HTML rendering for `takuhon build`.\n *\n * {@link renderProfileHtml} turns one locale-resolved {@link LocalizedTakuhon}\n * into a complete, self-contained static HTML document: semantic markup for\n * every profile section, an inline stylesheet, optional Schema.org JSON-LD,\n * and the `<head>` metadata (`<title>`, description, canonical, hreflang\n * alternates) the caller supplies.\n *\n * This is a deliberately separate, simpler surface from the React\n * `@takuhon/ui` (which is delivered as CSS-Modules components needing a\n * bundler). It reuses only `@takuhon/core` and has no DOM/browser dependency,\n * so it renders in plain Node and is unit-testable as a pure string function.\n *\n * Security: every piece of profile-derived text is escaped before it reaches\n * the markup ({@link escapeHtml}), and the JSON-LD payload is `<`/`>`/`&`\n * unicode-escaped so it cannot break out of its `<script>` element.\n */\n\nimport { generateJsonLd, renderActivitySvg } from '@takuhon/core';\nimport type { ActivitySnapshot, LocalizedTakuhon } from '@takuhon/core';\n\nimport { dateRange, escapeHtml, nonEmpty, safeUrl } from './html-helpers.js';\n\n// Re-exported for existing importers (e.g. dev-command, tests) that pull\n// `escapeHtml` from this module; the implementation now lives in html-helpers.\nexport { escapeHtml } from './html-helpers.js';\n\ntype LocalizedProfile = LocalizedTakuhon['profile'];\n\n/** One entry in the human-facing locale switcher. */\nexport interface LocaleLink {\n locale: string;\n href: string;\n current: boolean;\n}\n\n/** One `<link rel=\"alternate\" hreflang>` entry. */\nexport interface Alternate {\n hreflang: string;\n href: string;\n}\n\nexport interface RenderInput {\n /** The locale-resolved document to render. */\n localized: LocalizedTakuhon;\n /** Absolute canonical URL for this page (only when `--base-url` was given). */\n canonicalUrl?: string;\n /** hreflang alternates (empty when no base URL is available). */\n alternates: readonly Alternate[];\n /** Human locale switcher links (rendered only when more than one locale). */\n localeNav: readonly LocaleLink[];\n /** Whether to emit Schema.org JSON-LD (mirrors `settings.enableJsonLd`). */\n jsonLd: boolean;\n /**\n * Synced developer-activity snapshot, rendered as a self-owned inline SVG\n * section. The caller gates it on `settings.activity.enabled`; an absent (or\n * metric-less) snapshot omits the section entirely.\n */\n activitySnapshot?: ActivitySnapshot;\n}\n\n/** Unicode-escape `<`, `>`, `&` so a JSON-LD payload cannot break out of `<script>`. */\nfunction escapeJsonLd(json: string): string {\n return json.replace(/</g, '\\\\u003c').replace(/>/g, '\\\\u003e').replace(/&/g, '\\\\u0026');\n}\n\nconst CSS = `:root{--fg:#1a1a1a;--muted:#666;--accent:#0b5fff;--line:#e5e5e5}\n*{box-sizing:border-box}\nbody{margin:0;color:var(--fg);font:16px/1.6 system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif;background:#fff}\nmain{max-width:42rem;margin:0 auto;padding:2rem 1.25rem}\na{color:var(--accent)}\nh1{font-size:1.9rem;margin:.2rem 0}\nh2{font-size:1.15rem;margin:2rem 0 .75rem;padding-bottom:.3rem;border-bottom:1px solid var(--line)}\nh3{font-size:1rem;margin:0}\nheader .avatar{width:96px;height:96px;border-radius:50%;object-fit:cover}\n.tagline{font-size:1.1rem;color:var(--muted);margin:.2rem 0}\n.location{color:var(--muted);margin:.2rem 0}\n.bio{margin:.75rem 0}\nul{padding:0;margin:0;list-style:none}\n.entries>li{margin:0 0 1.1rem}\n.sub{margin:.1rem 0;font-weight:600}\n.meta{margin:.1rem 0;color:var(--muted);font-size:.9rem}\n.links{display:flex;flex-wrap:wrap;gap:.5rem 1rem;margin:.75rem 0}\n.skills,.tags{display:flex;flex-wrap:wrap;gap:.4rem}\n.skills>li,.tags>li{background:#f2f2f2;border-radius:1rem;padding:.15rem .6rem;font-size:.85rem}\n.rec{margin:0 0 1.1rem}\n.rec blockquote{margin:0;padding-left:.9rem;border-left:3px solid var(--line)}\n.rec figcaption{color:var(--muted);font-size:.9rem;margin-top:.3rem}\nnav.locales{display:flex;gap:.75rem;margin-bottom:1rem;font-size:.9rem}\n.activity svg{max-width:100%;height:auto}\nfooter.powered{max-width:42rem;margin:0 auto;padding:1.5rem 1.25rem;color:var(--muted);font-size:.85rem}`;\n\ninterface EntryView {\n heading: string;\n sub?: string;\n dates?: string;\n body?: string;\n url?: string;\n tags?: readonly string[];\n}\n\nfunction renderEntry(entry: EntryView): string {\n const href = entry.url ? safeUrl(entry.url) : undefined;\n const heading = href\n ? `<a href=\"${escapeHtml(href)}\">${escapeHtml(entry.heading)}</a>`\n : escapeHtml(entry.heading);\n const parts = [`<h3>${heading}</h3>`];\n if (entry.sub) parts.push(`<p class=\"sub\">${escapeHtml(entry.sub)}</p>`);\n if (entry.dates) parts.push(`<p class=\"meta\">${escapeHtml(entry.dates)}</p>`);\n if (entry.body) parts.push(`<p>${escapeHtml(entry.body)}</p>`);\n if (entry.tags && entry.tags.length > 0) {\n parts.push(\n `<ul class=\"tags\">${entry.tags.map((t) => `<li>${escapeHtml(t)}</li>`).join('')}</ul>`,\n );\n }\n return `<li>${parts.join('')}</li>`;\n}\n\n/** Render a `<section>` of entries, or `''` when there are none. */\nfunction entryList(title: string, entries: readonly EntryView[]): string {\n if (entries.length === 0) return '';\n return `<section><h2>${escapeHtml(title)}</h2><ul class=\"entries\">${entries\n .map(renderEntry)\n .join('')}</ul></section>`;\n}\n\nfunction renderHeader(p: LocalizedProfile): string {\n const parts: string[] = [];\n const avatarSrc = p.avatar?.url ? safeUrl(p.avatar.url) : undefined;\n if (avatarSrc) {\n parts.push(\n `<img class=\"avatar\" src=\"${escapeHtml(avatarSrc)}\" alt=\"${escapeHtml(p.avatar?.alt ?? '')}\">`,\n );\n }\n parts.push(`<h1>${escapeHtml(p.displayName)}</h1>`);\n if (p.tagline) parts.push(`<p class=\"tagline\">${escapeHtml(p.tagline)}</p>`);\n if (p.location?.display) parts.push(`<p class=\"location\">${escapeHtml(p.location.display)}</p>`);\n if (p.bio) parts.push(`<p class=\"bio\">${escapeHtml(p.bio)}</p>`);\n return `<header>${parts.join('')}</header>`;\n}\n\nfunction renderLinks(links: LocalizedTakuhon['links']): string {\n if (links.length === 0) return '';\n const items = links\n .map((l) => {\n const text = escapeHtml(l.label ?? l.url);\n const href = safeUrl(l.url);\n return href ? `<li><a href=\"${escapeHtml(href)}\">${text}</a></li>` : `<li>${text}</li>`;\n })\n .join('');\n return `<nav aria-label=\"Links\"><ul class=\"links\">${items}</ul></nav>`;\n}\n\nfunction renderSkills(skills: LocalizedTakuhon['skills']): string {\n if (skills.length === 0) return '';\n const items = skills.map((s) => `<li>${escapeHtml(s.label)}</li>`).join('');\n return `<section><h2>Skills</h2><ul class=\"skills\">${items}</ul></section>`;\n}\n\nfunction renderLanguages(languages: LocalizedTakuhon['languages']): string {\n if (languages.length === 0) return '';\n const items = languages\n .map((l) => `<li>${escapeHtml(`${l.displayName ?? l.language} — ${l.proficiency}`)}</li>`)\n .join('');\n return `<section><h2>Languages</h2><ul class=\"entries\">${items}</ul></section>`;\n}\n\nfunction renderRecommendations(recs: LocalizedTakuhon['recommendations']): string {\n if (recs.length === 0) return '';\n const items = recs\n .map((r) => {\n const authorHref = r.author.url ? safeUrl(r.author.url) : undefined;\n const name = authorHref\n ? `<a href=\"${escapeHtml(authorHref)}\">${escapeHtml(r.author.name)}</a>`\n : escapeHtml(r.author.name);\n const caption = [name, r.author.headline ? escapeHtml(r.author.headline) : '']\n .filter(Boolean)\n .join(', ');\n const rel = r.relationship ? ` (${escapeHtml(r.relationship)})` : '';\n return `<figure class=\"rec\"><blockquote>${escapeHtml(r.body)}</blockquote><figcaption>— ${caption}${rel}</figcaption></figure>`;\n })\n .join('');\n return `<section><h2>Recommendations</h2>${items}</section>`;\n}\n\nfunction renderContact(contact: LocalizedTakuhon['contact']): string {\n const items: string[] = [];\n if (contact.email) {\n items.push(\n `<li><a href=\"mailto:${escapeHtml(contact.email)}\">${escapeHtml(contact.email)}</a></li>`,\n );\n }\n const formHref = contact.formUrl ? safeUrl(contact.formUrl) : undefined;\n if (formHref) {\n items.push(`<li><a href=\"${escapeHtml(formHref)}\">Contact form</a></li>`);\n }\n if (items.length === 0) return '';\n return `<section><h2>Contact</h2><ul class=\"entries\">${items.join('')}</ul></section>`;\n}\n\n/**\n * Render the developer-activity section from the synced snapshot, or `''`\n * when there is none (or it carries no metric data). The SVG is generated by\n * `@takuhon/core` from stored numbers only — no external badge image — so the\n * page works under an `img-src 'self'` CSP.\n */\nfunction renderActivity(snapshot: ActivitySnapshot | undefined): string {\n if (!snapshot) return '';\n const svg = renderActivitySvg(snapshot);\n if (svg === '') return '';\n return `<section class=\"activity\"><h2>Activity</h2>${svg}</section>`;\n}\n\nfunction renderJsonLdScript(data: LocalizedTakuhon): string {\n const payload = JSON.stringify(generateJsonLd(data));\n return `<script type=\"application/ld+json\">${escapeJsonLd(payload)}</script>`;\n}\n\nfunction renderLocaleNav(localeNav: readonly LocaleLink[]): string {\n const items = localeNav\n .map((l) =>\n l.current\n ? `<span aria-current=\"true\">${escapeHtml(l.locale)}</span>`\n : `<a href=\"${escapeHtml(l.href)}\">${escapeHtml(l.locale)}</a>`,\n )\n .join('');\n return `<nav class=\"locales\" aria-label=\"Language\">${items}</nav>`;\n}\n\n/** Render a complete static HTML document for one locale-resolved profile. */\nexport function renderProfileHtml(input: RenderInput): string {\n const d = input.localized;\n const p = d.profile;\n const description = p.tagline ?? p.bio ?? '';\n\n const head = [\n '<meta charset=\"utf-8\">',\n '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">',\n `<title>${escapeHtml(p.displayName)}</title>`,\n description\n ? `<meta name=\"description\" content=\"${escapeHtml(description.slice(0, 300))}\">`\n : '',\n input.canonicalUrl ? `<link rel=\"canonical\" href=\"${escapeHtml(input.canonicalUrl)}\">` : '',\n ...input.alternates.map(\n (a) =>\n `<link rel=\"alternate\" hreflang=\"${escapeHtml(a.hreflang)}\" href=\"${escapeHtml(a.href)}\">`,\n ),\n input.jsonLd ? renderJsonLdScript(d) : '',\n `<style>${CSS}</style>`,\n ]\n .filter(Boolean)\n .join('\\n ');\n\n const body = [\n input.localeNav.length > 1 ? renderLocaleNav(input.localeNav) : '',\n renderHeader(p),\n renderLinks(d.links),\n entryList(\n 'Experience',\n d.careers.map((c) => ({\n heading: c.role,\n sub: c.organization,\n dates: dateRange(c.startDate, c.endDate, c.isCurrent),\n body: c.description,\n url: c.url,\n })),\n ),\n entryList(\n 'Projects',\n d.projects.map((x) => ({\n heading: x.title,\n dates: dateRange(x.startDate, x.endDate),\n body: x.description,\n url: x.url,\n tags: x.tags,\n })),\n ),\n renderSkills(d.skills),\n renderActivity(input.activitySnapshot),\n entryList(\n 'Education',\n d.education.map((e) => {\n const degree = nonEmpty([e.degree, e.fieldOfStudy], ', ');\n return {\n heading: degree ?? e.institution,\n sub: degree ? e.institution : undefined,\n dates: dateRange(e.startDate, e.endDate, e.isCurrent),\n body: e.description,\n url: e.url,\n };\n }),\n ),\n entryList(\n 'Certifications',\n d.certifications.map((c) => ({\n heading: c.title,\n sub: c.issuingOrganization,\n dates: dateRange(c.issueDate, c.expirationDate),\n url: c.url,\n })),\n ),\n entryList(\n 'Publications',\n d.publications.map((x) => ({\n heading: x.title,\n sub: nonEmpty([x.publisher, x.coAuthors?.join(', ')], ' · '),\n dates: dateRange(x.date),\n body: x.description,\n url: x.url ?? (x.doi ? `https://doi.org/${x.doi}` : undefined),\n })),\n ),\n entryList(\n 'Honors & awards',\n d.honors.map((x) => ({\n heading: x.title,\n sub: x.issuer,\n dates: dateRange(x.date),\n body: x.description,\n url: x.url,\n })),\n ),\n entryList(\n 'Memberships',\n d.memberships.map((x) => ({\n heading: x.role ?? x.organization,\n sub: x.role ? x.organization : undefined,\n dates: dateRange(x.startDate, x.endDate, x.isCurrent),\n body: x.description,\n url: x.url,\n })),\n ),\n entryList(\n 'Volunteering',\n d.volunteering.map((x) => ({\n heading: x.role,\n sub: nonEmpty([x.organization, x.cause], ' · '),\n dates: dateRange(x.startDate, x.endDate, x.isCurrent),\n body: x.description,\n url: x.url,\n })),\n ),\n entryList(\n 'Courses',\n d.courses.map((x) => ({\n heading: x.title,\n sub: x.provider,\n dates: dateRange(x.completionDate),\n body: x.description,\n url: x.certificateUrl,\n })),\n ),\n entryList(\n 'Patents',\n d.patents.map((x) => ({\n heading: x.title,\n sub: nonEmpty([x.patentNumber, x.office, x.status, x.coInventors?.join(', ')], ' · '),\n dates: dateRange(x.filingDate ?? x.grantDate),\n body: x.description,\n url: x.url,\n })),\n ),\n entryList(\n 'Test scores',\n d.testScores.map((x) => ({\n heading: `${x.title}: ${x.score}`,\n dates: dateRange(x.date),\n body: x.description,\n url: x.url,\n })),\n ),\n renderLanguages(d.languages),\n renderRecommendations(d.recommendations),\n renderContact(d.contact),\n ]\n .filter(Boolean)\n .join('\\n');\n\n const footer =\n d.settings.showPoweredBy === true ? '<footer class=\"powered\">Powered by takuhon</footer>' : '';\n\n return (\n `<!DOCTYPE html>\\n<html lang=\"${escapeHtml(d.resolvedLocale)}\">\\n<head>\\n ${head}\\n</head>\\n` +\n `<body>\\n<main>\\n${body}\\n</main>\\n${footer ? `${footer}\\n` : ''}</body>\\n</html>\\n`\n );\n}\n","/**\n * Pure HTML helpers shared by the static-site renderer ({@link\n * import('./build-html.js')}) and the CV renderer ({@link\n * import('./cv-html.js')}). All are string-in / string-out with no I/O, so they\n * stay unit-testable and keep both renderers' escaping behavior identical.\n */\n\n/** Escape text for use in HTML element content or double/single-quoted attributes. */\nexport function escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Return `url` only when its scheme is safe to place in an `href`/`src`, else\n * `undefined`. Relative, protocol-relative, fragment, and query URLs (no\n * scheme) are allowed; among absolute URLs only `http:`, `https:`, and\n * `mailto:` are. This blocks `javascript:`, `data:`, `vbscript:`, etc. — the\n * schema validates only a generic URI, so a hostile document could otherwise\n * smuggle an executable scheme into the generated page.\n */\nexport function safeUrl(url: string): string | undefined {\n const trimmed = url.trim();\n const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(trimmed)?.[1]?.toLowerCase();\n if (scheme === undefined) return trimmed; // relative / protocol-relative / fragment\n return scheme === 'http' || scheme === 'https' || scheme === 'mailto' ? trimmed : undefined;\n}\n\n/** Format a YearMonth range; `null` end or `isCurrent` renders as \"Present\". */\nexport function dateRange(start?: string, end?: string | null, isCurrent?: boolean): string {\n const left = start ?? '';\n const right = isCurrent === true || end === null ? 'Present' : (end ?? '');\n if (left && right) return `${left} – ${right}`;\n return left || right;\n}\n\n/** Join the non-empty values with `separator`, or `undefined` when none remain. */\nexport function nonEmpty(\n values: readonly (string | undefined)[],\n separator: string,\n): string | undefined {\n const joined = values\n .filter((v): v is string => typeof v === 'string' && v.length > 0)\n .join(separator);\n return joined.length > 0 ? joined : undefined;\n}\n","/**\n * Shared static-site generation for `takuhon build` and `takuhon dev`.\n *\n * {@link generateSite} turns one validated, normalized, privacy-filtered profile\n * into the full set of pages the static surface exposes: one per available\n * locale. `build` writes each page's {@link SitePage.file} to disk; `dev` serves\n * each page's {@link SitePage.route} from memory. Keeping the per-locale loop,\n * the locale switcher links, and the canonical/hreflang logic here means both\n * commands share a single source of truth for the site's structure — the only\n * difference between them is disk write vs. in-memory serve.\n *\n * This reuses `@takuhon/core` only (validate/normalize/resolve happen upstream;\n * this module just resolves each locale and renders), so it stays bundler-free\n * and unit-testable as a pure function.\n */\n\nimport type { ActivitySnapshot, NormalizedTakuhon } from '@takuhon/core';\nimport { deriveCv, resolveLocale } from '@takuhon/core';\n\nimport { renderProfileHtml, type Alternate, type LocaleLink } from './build-html.js';\nimport { renderCvHtml } from './cv-html.js';\n\n/** One generated page: a serve route, a relative output file, and its HTML. */\nexport interface SitePage {\n /** URL path used by `dev` (default locale at `/`, others at `/<locale>/`). */\n readonly route: string;\n /** Output path used by `build`, relative to the output dir. */\n readonly file: string;\n /** Rendered HTML document. */\n readonly html: string;\n}\n\nexport interface GenerateOptions {\n /**\n * Site origin (e.g. `https://me.example`). When set, pages carry absolute\n * canonical + hreflang links; when absent those are omitted (the human locale\n * switcher is always relative either way).\n */\n readonly baseUrl?: string;\n /**\n * Synced developer-activity snapshot (the `activity.json` beside the\n * profile). Rendered as an inline-SVG section only while\n * `settings.activity.enabled` is true — the same opt-in gate the public\n * `GET /api/activity` applies — so disabling the feature drops the section\n * even if a stale snapshot remains on disk. The snapshot is locale-agnostic\n * and shared by every page.\n */\n readonly activitySnapshot?: ActivitySnapshot | null;\n /**\n * Also emit a print-ready CV/résumé page per locale (the default locale at\n * `cv.html` / route `/cv/`, others at `<locale>/cv.html` / `/<locale>/cv/`).\n * `takuhon build` gates this behind `--cv`; `takuhon dev` always enables it so\n * the page is previewable. Off by default, so a plain build is unchanged.\n */\n readonly cv?: boolean;\n}\n\n/**\n * Generate every page for a profile: the default locale first, then the rest,\n * de-duplicated. Schema.org JSON-LD is emitted unless `settings.enableJsonLd`\n * is explicitly `false`.\n */\nexport function generateSite(\n profile: NormalizedTakuhon,\n options: GenerateOptions = {},\n): SitePage[] {\n const { baseUrl } = options;\n const defaultLocale = profile.settings.defaultLocale;\n // Default locale first, then the rest, de-duplicated.\n const locales = [...new Set([defaultLocale, ...profile.settings.availableLocales])];\n const jsonLd = profile.settings.enableJsonLd !== false;\n const activitySnapshot =\n profile.settings.activity?.enabled === true\n ? (options.activitySnapshot ?? undefined)\n : undefined;\n\n const pages: SitePage[] = [];\n for (const locale of locales) {\n const localized = resolveLocale(profile, locale);\n const isDefault = locale === defaultLocale;\n\n const localeNav: LocaleLink[] = locales.map((to) => ({\n locale: to,\n href: localeHref(locale, to, defaultLocale),\n current: to === locale,\n }));\n const canonicalUrl = baseUrl ? absoluteUrl(baseUrl, locale, defaultLocale) : undefined;\n const alternates: Alternate[] = baseUrl ? buildAlternates(baseUrl, locales, defaultLocale) : [];\n\n const html = renderProfileHtml({\n localized,\n canonicalUrl,\n alternates,\n localeNav,\n jsonLd,\n activitySnapshot,\n });\n pages.push({\n route: isDefault ? '/' : `/${locale}/`,\n file: isDefault ? 'index.html' : `${locale}/index.html`,\n html,\n });\n\n if (options.cv === true) {\n pages.push({\n route: isDefault ? '/cv/' : `/${locale}/cv/`,\n file: isDefault ? 'cv.html' : `${locale}/cv.html`,\n html: renderCvHtml(deriveCv(localized)),\n });\n }\n }\n return pages;\n}\n\n/** Absolute URL for a locale's page (default locale lives at the site root). */\nfunction absoluteUrl(baseUrl: string, locale: string, defaultLocale: string): string {\n return locale === defaultLocale ? `${baseUrl}/` : `${baseUrl}/${locale}/`;\n}\n\n/** hreflang alternates for every locale plus an `x-default` pointing at the default. */\nfunction buildAlternates(\n baseUrl: string,\n locales: readonly string[],\n defaultLocale: string,\n): Alternate[] {\n const alternates: Alternate[] = locales.map((locale) => ({\n hreflang: locale,\n href: absoluteUrl(baseUrl, locale, defaultLocale),\n }));\n alternates.push({\n hreflang: 'x-default',\n href: absoluteUrl(baseUrl, defaultLocale, defaultLocale),\n });\n return alternates;\n}\n\n/**\n * Depth-correct relative link from the page for `from` to the page for `to`,\n * for the human locale switcher. Always relative (independent of `baseUrl`)\n * so the switcher works regardless of where the site is hosted: the default\n * locale lives at the root, every other locale one directory deep.\n */\nfunction localeHref(from: string, to: string, defaultLocale: string): string {\n const fromRoot = from === defaultLocale;\n const toRoot = to === defaultLocale;\n if (fromRoot) return toRoot ? './' : `${to}/`;\n return toRoot ? '../' : `../${to}/`;\n}\n","/**\n * Pure HTML rendering of a {@link CvDocument} for `takuhon build --cv`.\n *\n * {@link renderCvHtml} turns one locale-resolved CV (from `@takuhon/core`'s\n * {@link deriveCv}) into a complete, self-contained static HTML résumé: an\n * inline stylesheet sized for A4 with an `@media print` block, so the page\n * reads well on screen and the browser's \"Save as PDF\" produces a clean,\n * single-column résumé. No external resources are referenced, so the page works\n * under a strict `img-src 'self'` / no-network policy (the same self-owned\n * stance the profile page and the activity card take).\n *\n * Security: every CV-derived string is escaped before it reaches the markup\n * ({@link escapeHtml}), and any URL is scheme-checked ({@link safeUrl}) so a\n * hostile document cannot smuggle a `javascript:` href into the résumé.\n */\n\nimport type { CvDocument, CvSection, LocalizedLanguage } from '@takuhon/core';\n\nimport { dateRange, escapeHtml, nonEmpty, safeUrl } from './html-helpers.js';\n\n/** Heading shown for each section, by `kind`. Plain English (CV chrome). */\nconst SECTION_TITLES: Record<CvSection['kind'], string> = {\n experience: 'Experience',\n education: 'Education',\n skills: 'Skills',\n certifications: 'Certifications',\n publications: 'Publications',\n honors: 'Honors & Awards',\n courses: 'Courses',\n patents: 'Patents',\n languages: 'Languages',\n volunteering: 'Volunteering',\n memberships: 'Memberships',\n};\n\n// A4-sized, single-column résumé. The screen view is centered on a neutral\n// backdrop; the print view drops the backdrop/margins so the page fills the\n// sheet, and `break-inside: avoid` keeps entries from splitting across pages.\nconst CSS = `:root{--fg:#1a1a1a;--muted:#555;--accent:#0b5fff;--line:#d9d9d9}\n*{box-sizing:border-box}\nbody{margin:0;background:#f3f4f6;color:var(--fg);font:13px/1.5 system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif}\nmain{background:#fff;width:210mm;min-height:297mm;margin:1.5rem auto;padding:18mm 16mm;box-shadow:0 1px 6px rgba(0,0,0,.15)}\nh1{font-size:1.7rem;margin:0}\n.tagline{font-size:1.05rem;color:var(--muted);margin:.15rem 0 0}\n.contact{color:var(--muted);font-size:.85rem;margin:.4rem 0 0;display:flex;flex-wrap:wrap;gap:.25rem 1rem}\n.contact a{color:var(--accent)}\n.bio{margin:.75rem 0 0}\nheader{border-bottom:2px solid var(--fg);padding-bottom:.6rem;margin-bottom:.4rem}\nsection{margin-top:1.1rem}\nh2{font-size:.95rem;text-transform:uppercase;letter-spacing:.05em;color:var(--accent);border-bottom:1px solid var(--line);margin:0 0 .5rem;padding-bottom:.15rem}\nul{padding:0;margin:0;list-style:none}\n.entry{margin:0 0 .7rem;break-inside:avoid}\n.entry .row{display:flex;justify-content:space-between;gap:1rem;align-items:baseline}\n.entry h3{font-size:.95rem;margin:0}\n.entry .dates{color:var(--muted);font-size:.8rem;white-space:nowrap}\n.entry .sub{color:var(--muted);margin:.05rem 0}\n.entry p{margin:.2rem 0 0}\n.entry a{color:var(--accent)}\n.chips{display:flex;flex-wrap:wrap;gap:.3rem}\n.chips li{border:1px solid var(--line);border-radius:1rem;padding:.05rem .55rem;font-size:.8rem}\n@media print{\n body{background:#fff}\n main{width:auto;min-height:0;margin:0;padding:0;box-shadow:none}\n a{color:var(--fg)}\n}\n@page{size:A4;margin:14mm}`;\n\n/** One résumé entry: a heading row (title + dates), an optional sub line, body. */\ninterface CvEntryView {\n heading: string;\n url?: string;\n dates?: string;\n sub?: string;\n body?: string;\n}\n\nfunction renderEntry(entry: CvEntryView): string {\n const href = entry.url ? safeUrl(entry.url) : undefined;\n const title = href\n ? `<a href=\"${escapeHtml(href)}\">${escapeHtml(entry.heading)}</a>`\n : escapeHtml(entry.heading);\n const dates = entry.dates ? `<span class=\"dates\">${escapeHtml(entry.dates)}</span>` : '';\n const parts = [`<div class=\"row\"><h3>${title}</h3>${dates}</div>`];\n if (entry.sub) parts.push(`<p class=\"sub\">${escapeHtml(entry.sub)}</p>`);\n if (entry.body) parts.push(`<p>${escapeHtml(entry.body)}</p>`);\n return `<li class=\"entry\">${parts.join('')}</li>`;\n}\n\nfunction entryListSection(title: string, entries: readonly CvEntryView[]): string {\n return `<section><h2>${escapeHtml(title)}</h2><ul>${entries.map(renderEntry).join('')}</ul></section>`;\n}\n\n/** Skills / languages render as compact chip rows rather than dated entries. */\nfunction chipSection(title: string, labels: readonly string[]): string {\n const chips = labels.map((l) => `<li>${escapeHtml(l)}</li>`).join('');\n return `<section><h2>${escapeHtml(title)}</h2><ul class=\"chips\">${chips}</ul></section>`;\n}\n\nfunction languageLabel(l: LocalizedLanguage): string {\n return `${l.displayName ?? l.language} — ${l.proficiency}`;\n}\n\n/** Render one CV section to its `<section>` markup, dispatching on `kind`. */\nfunction renderSection(section: CvSection): string {\n const title = SECTION_TITLES[section.kind];\n switch (section.kind) {\n case 'experience':\n return entryListSection(\n title,\n section.entries.map((c) => ({\n heading: c.role,\n sub: c.organization,\n dates: dateRange(c.startDate, c.endDate, c.isCurrent),\n body: c.description,\n url: c.url,\n })),\n );\n case 'education':\n return entryListSection(\n title,\n section.entries.map((e) => {\n const degree = nonEmpty([e.degree, e.fieldOfStudy], ', ');\n return {\n heading: degree ?? e.institution,\n sub: degree ? e.institution : undefined,\n dates: dateRange(e.startDate, e.endDate, e.isCurrent),\n body: e.description,\n url: e.url,\n };\n }),\n );\n case 'skills':\n return chipSection(\n title,\n section.entries.map((s) => s.label),\n );\n case 'languages':\n return chipSection(title, section.entries.map(languageLabel));\n case 'certifications':\n return entryListSection(\n title,\n section.entries.map((c) => ({\n heading: c.title,\n sub: c.issuingOrganization,\n dates: dateRange(c.issueDate, c.expirationDate),\n url: c.url,\n })),\n );\n case 'publications':\n return entryListSection(\n title,\n section.entries.map((x) => ({\n heading: x.title,\n sub: nonEmpty([x.publisher, x.coAuthors?.join(', ')], ' · '),\n dates: dateRange(x.date),\n body: x.description,\n url: x.url ?? (x.doi ? `https://doi.org/${x.doi}` : undefined),\n })),\n );\n case 'honors':\n return entryListSection(\n title,\n section.entries.map((x) => ({\n heading: x.title,\n sub: x.issuer,\n dates: dateRange(x.date),\n body: x.description,\n url: x.url,\n })),\n );\n case 'courses':\n return entryListSection(\n title,\n section.entries.map((x) => ({\n heading: x.title,\n sub: x.provider,\n dates: dateRange(x.completionDate),\n body: x.description,\n url: x.certificateUrl,\n })),\n );\n case 'patents':\n return entryListSection(\n title,\n section.entries.map((x) => ({\n heading: x.title,\n sub: nonEmpty([x.patentNumber, x.office, x.status], ' · '),\n dates: dateRange(x.filingDate ?? x.grantDate),\n body: x.description,\n url: x.url,\n })),\n );\n case 'volunteering':\n return entryListSection(\n title,\n section.entries.map((x) => ({\n heading: x.role,\n sub: nonEmpty([x.organization, x.cause], ' · '),\n dates: dateRange(x.startDate, x.endDate, x.isCurrent),\n body: x.description,\n url: x.url,\n })),\n );\n case 'memberships':\n return entryListSection(\n title,\n section.entries.map((x) => ({\n heading: x.role ?? x.organization,\n sub: x.role ? x.organization : undefined,\n dates: dateRange(x.startDate, x.endDate, x.isCurrent),\n body: x.description,\n url: x.url,\n })),\n );\n }\n}\n\nfunction renderHeader(cv: CvDocument): string {\n const h = cv.header;\n const parts = [`<h1>${escapeHtml(h.displayName)}</h1>`];\n if (h.tagline) parts.push(`<p class=\"tagline\">${escapeHtml(h.tagline)}</p>`);\n\n const contact: string[] = [];\n if (h.location) contact.push(`<span>${escapeHtml(h.location)}</span>`);\n if (h.email) {\n contact.push(`<a href=\"mailto:${escapeHtml(h.email)}\">${escapeHtml(h.email)}</a>`);\n }\n const formHref = h.formUrl ? safeUrl(h.formUrl) : undefined;\n if (formHref) contact.push(`<a href=\"${escapeHtml(formHref)}\">Contact</a>`);\n if (contact.length > 0) parts.push(`<div class=\"contact\">${contact.join('')}</div>`);\n\n if (h.bio) parts.push(`<p class=\"bio\">${escapeHtml(h.bio)}</p>`);\n return `<header>${parts.join('')}</header>`;\n}\n\n/** Render a complete static HTML résumé document for one locale-resolved CV. */\nexport function renderCvHtml(cv: CvDocument): string {\n const title = `${cv.header.displayName} — CV`;\n const body = [renderHeader(cv), ...cv.sections.map(renderSection)].join('\\n');\n return (\n `<!DOCTYPE html>\\n<html lang=\"${escapeHtml(cv.resolvedLocale)}\">\\n<head>\\n` +\n ` <meta charset=\"utf-8\">\\n` +\n ` <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\\n` +\n ` <title>${escapeHtml(title)}</title>\\n` +\n ` <style>${CSS}</style>\\n</head>\\n` +\n `<body>\\n<main>\\n${body}\\n</main>\\n</body>\\n</html>\\n`\n );\n}\n","/**\n * Filesystem-backed {@link TakuhonAssetStorage} for the local admin server\n * (`takuhon admin`). Stores uploaded images in an `assets/` directory beside\n * the project's `takuhon.json`, so a locally-edited profile gets the same asset\n * pipeline the Cloudflare adapter provides over R2.\n *\n * Like the R2 adapter, the bytes arrive already validated and metadata-stripped\n * by `@takuhon/api`'s admin app, so this class only persists them and mints the\n * key / URL. Object keys follow `assets/{timestamp}-{shortHash}.{ext}`\n * (`security.md` §4.6) — identical to {@link R2TakuhonAssetStorage} so a profile\n * edited locally and later deployed keeps working relative `/assets/...` URLs.\n * Dimensions are read back header-only via {@link readImageInfo} (no decode).\n *\n * Delivery is the local server's job (`GET /assets/*`), so {@link readForServing}\n * exposes the bytes + content-type for that route while keeping all path\n * resolution (and the traversal guard) in one place.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport { basename, dirname, extname, join, resolve, sep } from 'node:path';\n\nimport {\n ACCEPTED_IMAGE_MIME_TYPES,\n detectImageMime,\n IMAGE_EXTENSIONS,\n NotFoundError,\n readImageInfo,\n type AcceptedImageMime,\n type AssetOptions,\n type AssetRecord,\n type TakuhonAssetStorage,\n} from '@takuhon/core';\n\n/** Directory (beside `takuhon.json`) holding uploaded assets; the key prefix. */\nconst ASSET_DIRNAME = 'assets';\nconst ASSET_KEY_PREFIX = `${ASSET_DIRNAME}/`;\n\n/** Extension used when the bytes are not one of the accepted image types. */\nconst DEFAULT_EXTENSION = 'bin';\n\n/** Reverse of {@link IMAGE_EXTENSIONS}: file extension → MIME, for delivery. */\nconst MIME_BY_EXTENSION = new Map<string, string>(\n Object.entries(IMAGE_EXTENSIONS).map(([mime, ext]) => [ext, mime]),\n);\n\n/** Narrow an arbitrary content-type string to an accepted image MIME, or null. */\nfunction asAcceptedMime(value: string | undefined): AcceptedImageMime | null {\n if (value === undefined) return null;\n return (ACCEPTED_IMAGE_MIME_TYPES as readonly string[]).includes(value)\n ? (value as AcceptedImageMime)\n : null;\n}\n\n/** Content-type for a stored asset filename, by extension. */\nfunction contentTypeForFile(name: string): string {\n const ext = extname(name).slice(1).toLowerCase();\n return MIME_BY_EXTENSION.get(ext) ?? 'application/octet-stream';\n}\n\n/** Seconds since the Unix epoch, the leading object-key component. */\nfunction timestampSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/** Four hex characters of randomness, defeating key enumeration (§4.7). */\nfunction shortHash(): string {\n return randomBytes(2).toString('hex');\n}\n\nfunction isNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'ENOENT';\n}\n\n/**\n * Write `bytes` to `target` atomically: stage in a sibling temp file and\n * `rename` into place, so a reader (e.g. the SPA fetching the just-returned\n * `publicUrl`) never observes a half-written asset. Binary counterpart of\n * `writeFileAtomic` in `backup.ts`.\n */\nfunction writeFileAtomicBinary(target: string, bytes: Uint8Array): void {\n const tmp = join(dirname(target), `.${basename(target)}.${String(process.pid)}.tmp`);\n try {\n writeFileSync(tmp, bytes);\n renameSync(tmp, target);\n } catch (error) {\n rmSync(tmp, { force: true });\n throw error;\n }\n}\n\nexport class FileTakuhonAssetStorage implements TakuhonAssetStorage {\n private readonly profilePath: string;\n private readonly publicBaseUrl: string;\n\n constructor(profilePath: string, options: { publicBaseUrl?: string } = {}) {\n this.profilePath = profilePath;\n this.publicBaseUrl = options.publicBaseUrl ?? '';\n }\n\n // Genuinely async (awaits the Blob), so no `Promise.resolve().then()` wrap is\n // needed here — unlike the synchronous methods below.\n async putAsset(file: File | Blob, options?: AssetOptions): Promise<AssetRecord> {\n const bytes = new Uint8Array(await file.arrayBuffer());\n // Trust the API-validated content-type when present; otherwise authenticate\n // from the bytes so the adapter is self-sufficient for direct callers.\n const mime = asAcceptedMime(options?.contentType) ?? detectImageMime(bytes);\n const ext = mime !== null ? IMAGE_EXTENSIONS[mime] : DEFAULT_EXTENSION;\n const contentType = mime ?? options?.contentType ?? file.type ?? 'application/octet-stream';\n\n const key = `${ASSET_KEY_PREFIX}${String(timestampSeconds())}-${shortHash()}.${ext}`;\n const full = join(this.projectDir, key);\n mkdirSync(dirname(full), { recursive: true });\n writeFileAtomicBinary(full, bytes);\n\n const info = mime !== null ? readImageInfo(bytes, mime) : null;\n const url = `/${key}`;\n return {\n id: key,\n url,\n publicUrl: this.absoluteUrl(url),\n mimeType: contentType,\n size: bytes.length,\n width: info?.width,\n height: info?.height,\n createdAt: new Date().toISOString(),\n };\n }\n\n getPublicUrl(assetId: string): Promise<string> {\n return Promise.resolve().then(() => {\n const full = this.resolveKey(assetId);\n if (full === null || !existsSync(full)) {\n throw new NotFoundError(`No asset is stored for \"${assetId}\".`);\n }\n return this.absoluteUrl(`/${assetId}`);\n });\n }\n\n deleteAsset(assetId: string): Promise<void> {\n return Promise.resolve().then(() => {\n const full = this.resolveKey(assetId);\n // Idempotent: a missing file (or an out-of-bounds key) is a no-op.\n if (full !== null) rmSync(full, { force: true });\n });\n }\n\n listAssets(): Promise<AssetRecord[]> {\n return Promise.resolve().then(() => {\n const root = join(this.projectDir, ASSET_DIRNAME);\n let names: string[];\n try {\n names = readdirSync(root);\n } catch (err) {\n if (isNotFound(err)) return [];\n throw err;\n }\n return names\n .filter((name) => statSync(join(root, name)).isFile())\n .sort()\n .map((name) => {\n const key = `${ASSET_KEY_PREFIX}${name}`;\n const url = `/${key}`;\n return {\n id: key,\n url,\n publicUrl: this.absoluteUrl(url),\n mimeType: contentTypeForFile(name),\n size: statSync(join(root, name)).size,\n createdAt: statSync(join(root, name)).mtime.toISOString(),\n };\n });\n });\n }\n\n /**\n * Local-server helper (not part of {@link TakuhonAssetStorage}): read an asset\n * for static delivery, or `null` when it is absent or the key escapes the\n * `assets/` directory. The traversal guard lives here so the delivery route\n * and the persistence methods share one notion of \"where assets live\".\n */\n readForServing(key: string): { bytes: Buffer; contentType: string } | null {\n const full = this.resolveKey(key);\n if (full === null) return null;\n let bytes: Buffer;\n try {\n bytes = readFileSync(full);\n } catch (err) {\n if (isNotFound(err)) return null;\n throw err;\n }\n return { bytes, contentType: contentTypeForFile(full) };\n }\n\n private get projectDir(): string {\n return dirname(resolve(this.profilePath));\n }\n\n /** Resolve an `assets/...` key to an absolute path, or null if out of bounds. */\n private resolveKey(key: string): string | null {\n const root = resolve(this.projectDir, ASSET_DIRNAME);\n const full = resolve(this.projectDir, key);\n if (full !== root && !full.startsWith(root + sep)) return null;\n return full;\n }\n\n private absoluteUrl(path: string): string {\n return this.publicBaseUrl !== '' ? `${this.publicBaseUrl}${path}` : path;\n }\n}\n","/**\n * Filesystem-backed {@link TakuhonStorage} for the local admin server\n * (`takuhon admin`). Persists the single profile document as the project's\n * `takuhon.json` so the form editor writes the same file the rest of the CLI\n * (build / dev / export / sync) reads.\n *\n * The opaque `version` is the SHA-256 of the file's bytes. This is stateless\n * (no sidecar metadata) and doubles as external-edit detection: if the file is\n * changed on disk between the editor's load and save, the hashes differ and the\n * `If-Match` precondition fails with a {@link ConflictError} — surfaced to the\n * SPA as a 409 conflict. Writes go through {@link writeFileAtomic} after a\n * backup, mirroring `import` / `migrate` / `restore`.\n */\n\nimport { createHash } from 'node:crypto';\nimport { readFileSync, rmSync } from 'node:fs';\n\nimport { ConflictError, NotFoundError, type Takuhon, type TakuhonStorage } from '@takuhon/core';\n\nimport { createBackup, preAdminSaveName, writeFileAtomic } from './backup.js';\n\nfunction sha256Hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\nfunction isNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'ENOENT';\n}\n\n/** Read the file's bytes, or `undefined` when it does not exist. */\nfunction readMaybe(path: string): string | undefined {\n try {\n return readFileSync(path, 'utf8');\n } catch (err) {\n if (isNotFound(err)) return undefined;\n throw err;\n }\n}\n\nexport interface FileStorageDeps {\n /** Clock for backup timestamps; injectable for deterministic tests. */\n now?: () => Date;\n}\n\nexport class FileStorage implements TakuhonStorage {\n private readonly now: () => Date;\n\n constructor(\n private readonly path: string,\n deps: FileStorageDeps = {},\n ) {\n this.now = deps.now ?? (() => new Date());\n }\n\n // The filesystem work is synchronous, but the TakuhonStorage contract is\n // async. Each method runs its body inside `Promise.resolve().then(...)` so a\n // synchronous throw becomes a rejected promise (e.g. NotFoundError →\n // ConflictError mapping in the admin API) rather than an exception the caller\n // must catch outside `await`.\n\n getProfile(): Promise<{ data: Takuhon; version: string }> {\n return Promise.resolve().then(() => {\n const raw = readMaybe(this.path);\n if (raw === undefined) {\n // No path in the message: it is serialized into the admin API response,\n // and the SPA maps 404 to its empty-state import flow regardless.\n throw new NotFoundError('No profile is stored yet.');\n }\n let data: Takuhon;\n try {\n data = JSON.parse(raw) as Takuhon;\n } catch (err) {\n // Surfaced by the admin API as a 500; the form's job is to fix invalid\n // *schema*, but unparseable JSON cannot be loaded into the editor. Keep\n // the host path out of the message (it goes in `cause` for local logs).\n throw new Error('The profile file is not valid JSON.', { cause: err });\n }\n return { data, version: sha256Hex(raw) };\n });\n }\n\n saveProfile(data: Takuhon, ifMatch?: string): Promise<{ version: string }> {\n return Promise.resolve().then(() => {\n const current = readMaybe(this.path);\n\n if (ifMatch !== undefined) {\n const currentVersion = current === undefined ? undefined : sha256Hex(current);\n if (currentVersion !== ifMatch) {\n throw new ConflictError(\n `If-Match preconditioned on version \"${ifMatch}\" but current is \"${currentVersion ?? 'absent'}\".`,\n { currentVersion },\n );\n }\n }\n\n // Back up the file we are about to overwrite (none on first write).\n if (current !== undefined) {\n const stamp = this.now();\n createBackup({\n targetPath: this.path,\n content: current,\n name: (withMillis) => preAdminSaveName(stamp, withMillis),\n });\n }\n\n const content = `${JSON.stringify(data, null, 2)}\\n`;\n writeFileAtomic(this.path, content);\n return { version: sha256Hex(content) };\n });\n }\n\n deleteProfile(): Promise<void> {\n return Promise.resolve().then(() => {\n const current = readMaybe(this.path);\n if (current === undefined) return;\n const stamp = this.now();\n createBackup({\n targetPath: this.path,\n content: current,\n name: (withMillis) => preAdminSaveName(stamp, withMillis),\n });\n rmSync(this.path, { force: true });\n });\n }\n}\n","/**\n * `takuhon build [path] [--output <dir>] [--base-url <url>]` — render a\n * `takuhon.json` into a deployable static site (Spec §13 Static Edition).\n *\n * Mirrors the other command runners: {@link runBuild} is a pure function that\n * reads the source itself and returns its output as strings plus an exit code.\n * The render pipeline reuses `@takuhon/core` only: validate → normalize →\n * `applyPublicPrivacyFilter` (so the static site honours `meta.privacy` exactly\n * like the live API) → for each available locale `resolveLocale` →\n * {@link renderProfileHtml}. The default locale is written to `<dir>/index.html`\n * and every other locale to `<dir>/<locale>/index.html`.\n *\n * Canonical / hreflang links require absolute URLs, so they are emitted only\n * when `--base-url` is supplied; the human locale switcher always uses\n * depth-correct relative links. Asset files are referenced by URL as-is and\n * never copied (out of scope; assets are remote per the schema). Writes are\n * atomic via {@link writeFileAtomic}.\n *\n * Exit codes:\n * 0 — site generated (or `--help`)\n * 1 — the source was read but is not a valid takuhon profile\n * 2 — the command could not run: bad arguments, a missing/unreadable file,\n * a non-JSON file, or a failed write\n */\n\nimport { mkdirSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport {\n DARK_PALETTE,\n applyPublicPrivacyFilter,\n normalize,\n renderActivitySvg,\n validate,\n} from '@takuhon/core';\n\nimport { writeFileAtomic } from './backup.js';\nimport { readActivitySnapshotSync } from './file-activity-storage.js';\nimport { generateSite } from './site.js';\n\nconst DEFAULT_PATH = 'takuhon.json';\nconst DEFAULT_OUTPUT = 'dist';\n\nconst USAGE = `Usage: takuhon build [path] [--output <dir>] [--base-url <url>] [--cv]\n\nRender a takuhon.json into a static site (one HTML page per locale, with\nbuild-time Schema.org JSON-LD). With no path, builds ./takuhon.json.\n\nOptions:\n --output <dir> Output directory (default: ${DEFAULT_OUTPUT}). The default\n locale is written to <dir>/index.html and each other locale\n to <dir>/<locale>/index.html.\n --base-url <url> Site origin (e.g. https://me.example). Enables absolute\n canonical and hreflang links; without it those are omitted.\n --cv Also emit a print-ready CV/résumé page per locale\n (<dir>/cv.html and <dir>/<locale>/cv.html). Open it and use\n the browser's \"Save as PDF\" to produce a résumé PDF.\n\nThe public privacy filter is applied (meta.privacy is honoured). Asset URLs are\nreferenced as-is and are not copied. The output directory is written into, not\ncleaned — use a dedicated/empty directory so stale pages do not linger.\n\nWhen settings.activity.enabled is true and an activity.json sits beside the\nprofile, the activity card is also written as activity.svg and\nactivity-dark.svg for embedding as a GitHub README badge.\n\nExit codes: 0 = built, 1 = source is not a valid profile,\n2 = bad arguments / file missing / unreadable / not JSON / write failed.\n`;\n\nexport interface BuildOutcome {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\ninterface ParsedArgs {\n path: string;\n output: string;\n baseUrl?: string;\n cv: boolean;\n}\n\n/**\n * Run `takuhon build` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"build\"`).\n */\nexport function runBuild(args: readonly string[] = []): BuildOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon build --help\\` for usage.\\n`,\n };\n }\n\n return buildSite(parsed);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let output: string | undefined;\n let baseUrl: string | undefined;\n let cv = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--cv') {\n cv = true;\n continue;\n }\n if (arg === '--output' || arg === '--base-url') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--output') output = value;\n else baseUrl = value;\n i++;\n continue;\n }\n if (arg.startsWith('--output=')) {\n const value = arg.slice('--output='.length);\n if (value === '') return { error: 'takuhon: `--output` requires a value.' };\n output = value;\n continue;\n }\n if (arg.startsWith('--base-url=')) {\n const value = arg.slice('--base-url='.length);\n if (value === '') return { error: 'takuhon: `--base-url` requires a value.' };\n baseUrl = value;\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`build\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `build` takes at most one path argument.' };\n }\n path = arg;\n }\n\n if (baseUrl !== undefined && !isHttpUrl(baseUrl)) {\n return { error: 'takuhon: `--base-url` must be an absolute http(s) URL.' };\n }\n\n return {\n path: path ?? DEFAULT_PATH,\n output: output ?? DEFAULT_OUTPUT,\n // Drop any trailing slash so URL joins are predictable.\n baseUrl: baseUrl?.replace(/\\/+$/, ''),\n cv,\n };\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\nfunction buildSite(parsed: ParsedArgs): BuildOutcome {\n const { path, output, baseUrl, cv } = parsed;\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: cannot read '${path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${path}' is not valid JSON: ${detail}\\n` };\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${path}' is not a valid takuhon profile; refusing to build:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n const filtered = applyPublicPrivacyFilter(normalize(result.data));\n // The synced developer-activity snapshot (activity.json beside the profile)\n // is read only when the owner opted in; generateSite re-checks the gate.\n const activitySnapshot =\n filtered.settings.activity?.enabled === true ? readActivitySnapshotSync(path) : null;\n\n const written: string[] = [];\n const assets: string[] = [];\n try {\n for (const page of generateSite(filtered, { baseUrl, activitySnapshot, cv })) {\n const outFile = join(output, page.file);\n mkdirSync(dirname(outFile), { recursive: true });\n writeFileAtomic(outFile, page.html);\n written.push(outFile);\n }\n // Activity badges (hosting mode B): when the owner opted in and the synced\n // snapshot carries metrics, emit standalone light + dark SVG cards beside\n // the pages so they can be embedded as a README badge (the dynamic\n // counterpart is the Cloudflare adapter's GET /activity.svg). A metric-less\n // snapshot renders to '' and writes nothing.\n if (activitySnapshot) {\n const variants: readonly (readonly [string, string])[] = [\n ['activity.svg', renderActivitySvg(activitySnapshot)],\n ['activity-dark.svg', renderActivitySvg(activitySnapshot, { palette: DARK_PALETTE })],\n ];\n for (const [file, svg] of variants) {\n if (svg === '') continue;\n const outFile = join(output, file);\n mkdirSync(dirname(outFile), { recursive: true });\n writeFileAtomic(outFile, svg);\n assets.push(outFile);\n }\n }\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: failed to write the site: ${detail}\\n` };\n }\n\n const summary = [...written, ...assets].map((w) => ` ${w}`).join('\\n');\n const assetNote =\n assets.length > 0 ? ` and ${assets.length} asset${assets.length === 1 ? '' : 's'}` : '';\n return {\n code: 0,\n stdout: `built ${written.length} page${written.length === 1 ? '' : 's'}${assetNote} from ${path}:\\n${summary}\\n`,\n stderr: '',\n };\n}\n","/**\n * `takuhon export [path] [--output <file>]` — serialise a `takuhon.json` into\n * its transport form for transfer or archival.\n *\n * Mirrors the other command runners: {@link runExport} is a pure function that\n * reads the source itself but returns its output as strings plus an exit code.\n * It validates the source first (an export should be a valid transport\n * artifact), then delegates to core's `exportTakuhon`, which deep-clones and\n * refreshes `meta.updatedAt` (the round-trip exception in\n * operational-lifecycle §5.1). The source file is only read, never modified.\n *\n * Output defaults to stdout so the command composes in a pipeline; `--output`\n * writes a file atomically instead. Asset embedding (`--embed-assets`) is a\n * remote-storage concern and is deferred — it is rejected with a clear message\n * rather than silently ignored.\n *\n * Exit codes:\n * 0 — exported (to stdout or `--output`)\n * 1 — the source was read but is not a valid takuhon profile\n * 2 — the command could not run: bad arguments, a missing/unreadable file,\n * a file that is not valid JSON, or a failed `--output` write\n */\n\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { exportTakuhon, validate } from '@takuhon/core';\nimport type { Takuhon } from '@takuhon/core';\n\nimport { writeFileAtomic } from './backup.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon export [path] [--output <file>]\n\nSerialise a takuhon.json into its transport form and print it to stdout, or\nwrite it to a file with --output. With no path, exports ./takuhon.json in the\ncurrent working directory.\n\nOptions:\n --output <file> Write the export to <file> instead of stdout (atomic).\n\nExit codes: 0 = exported, 1 = source is not a valid profile,\n2 = bad arguments / file missing / unreadable / not JSON / write failed.\n`;\n\nexport interface ExportOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\ninterface ParsedArgs {\n path: string;\n output?: string;\n}\n\n/**\n * Run `takuhon export` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"export\"`).\n */\nexport function runExport(args: readonly string[] = []): ExportOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon export --help\\` for usage.\\n`,\n };\n }\n\n return exportFile(parsed);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let output: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--embed-assets') {\n return {\n error:\n 'takuhon: --embed-assets is not supported yet; assets are remote and asset embedding is deferred.',\n };\n }\n if (arg === '--output') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: 'takuhon: `--output` requires a value.' };\n }\n output = value;\n i++;\n continue;\n }\n if (arg.startsWith('--output=')) {\n const value = arg.slice('--output='.length);\n if (value === '') {\n return { error: 'takuhon: `--output` requires a value.' };\n }\n output = value;\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`export\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `export` takes at most one path argument.' };\n }\n path = arg;\n }\n\n return { path: path ?? DEFAULT_PATH, output };\n}\n\nfunction exportFile(parsed: ParsedArgs): ExportOutcome {\n const { path, output } = parsed;\n\n // Export produces a separate transport artifact; refuse to write it over the\n // source (which would mutate the source via the refreshed meta.updatedAt).\n if (output !== undefined && resolve(output) === resolve(path)) {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: --output '${output}' is the source file; export writes a separate artifact.\\n` +\n 'Omit --output to print to stdout, or choose a different file.\\n',\n };\n }\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: cannot read '${path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${path}' is not valid JSON: ${detail}\\n` };\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${path}' is not a valid takuhon profile; refusing to export:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n // Export the validated source faithfully — only meta.updatedAt is refreshed\n // (operational-lifecycle §5.1). We pass the parsed document rather than\n // `result.data` so `validate`'s coercion of absent optional arrays into `[]`\n // does not silently rewrite the export.\n const content = `${JSON.stringify(exportTakuhon(data as Takuhon), null, 2)}\\n`;\n\n if (output === undefined) {\n // No --output: the serialised profile is the command's stdout.\n return { code: 0, stdout: content, stderr: '' };\n }\n\n try {\n writeFileAtomic(output, content);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: failed to write '${output}': ${detail}\\n` };\n }\n\n return { code: 0, stdout: `exported ${path} -> ${output}\\n`, stderr: '' };\n}\n","/**\n * `takuhon import <file> [path]` — load a previously exported profile into a\n * local `takuhon.json` (operational-lifecycle §5.3 / §17.2).\n *\n * Like the other command runners this returns its output as strings plus an\n * exit code. The flow follows §5.3: read the input, migrate it forward to the\n * current schema version if it is older (core's `importTakuhon` deliberately\n * does *not* auto-migrate, so the CLI composes `migrateTakuhon` +\n * `importTakuhon`), validate the result, back up the current profile, then\n * write the imported document. Unlike `restore` (a verbatim reproduction),\n * `import` writes the migrated-and-validated document re-serialised in\n * canonical form.\n *\n * Import overwrites the target, but a backup is taken first, so — matching the\n * spec, which mandates a confirmation prompt only for `restore` — there is no\n * interactive prompt. Asset expansion and edge-cache purge (§5.3 steps 5-6)\n * are remote concerns and do not apply to this local-file command.\n *\n * Exit codes:\n * 0 — imported\n * 1 — the input was read but cannot be imported (missing/unusable\n * schemaVersion, no migration path to the current version, or the\n * result failed validation)\n * 2 — the command could not run: bad arguments, a missing/unreadable input\n * or target, a non-JSON input, or a failed write\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport {\n ImportError,\n MigrationError,\n SCHEMA_VERSION,\n importTakuhon,\n migrateTakuhon,\n} from '@takuhon/core';\nimport type { Takuhon } from '@takuhon/core';\n\nimport { BackupError, createBackup, preImportName, writeFileAtomic } from './backup.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon import <file> [path]\n\nLoad a previously exported profile from <file> into a local takuhon.json,\nmigrating it to the current schema version if needed. With no path, writes\n./takuhon.json in the current working directory.\n\nThe current profile (if any) is backed up to\n.takuhon-backups/pre-import-<timestamp>.json before being overwritten.\n\nExit codes: 0 = imported, 1 = input cannot be imported (invalid / unsupported\nversion), 2 = bad arguments / file missing / unreadable / not JSON / write failed.\n`;\n\nexport interface ImportOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies, so tests can pin the backup timestamp. */\nexport interface ImportDeps {\n /** Clock used for the pre-import backup filename. Defaults to `() => new Date()`. */\n now?: () => Date;\n}\n\ninterface ParsedArgs {\n file: string;\n path: string;\n}\n\n/**\n * Run `takuhon import` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"import\"`).\n */\nexport function runImport(args: readonly string[] = [], deps: ImportDeps = {}): ImportOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon import --help\\` for usage.\\n`,\n };\n }\n\n const now = deps.now ?? (() => new Date());\n return importFile(parsed, now);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n const positionals: string[] = [];\n\n for (const arg of args) {\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`import\\`.` };\n }\n positionals.push(arg);\n }\n\n if (positionals.length === 0) {\n return { error: 'takuhon: `import` requires an input <file>.' };\n }\n if (positionals.length > 2) {\n return { error: 'takuhon: `import` takes at most an input <file> and a target path.' };\n }\n\n return { file: positionals[0]!, path: positionals[1] ?? DEFAULT_PATH };\n}\n\nfunction importFile(parsed: ParsedArgs, now: () => Date): ImportOutcome {\n const { file, path } = parsed;\n\n let raw: string;\n try {\n raw = readFileSync(file, 'utf8');\n } catch {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read '${file}'.\\n` };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${file}' is not valid JSON: ${detail}\\n` };\n }\n\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${file}' is not a takuhon profile (expected a JSON object).\\n`,\n };\n }\n\n const source = (data as { schemaVersion?: unknown }).schemaVersion;\n if (typeof source !== 'string' || source.length === 0) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${file}' has no usable schemaVersion; cannot import.\\n`,\n };\n }\n\n // Bring the input up to the current schema version (§5.3 step 2). core's\n // importTakuhon does not auto-migrate, so we migrate first when needed.\n let candidate: unknown = data;\n if (source !== SCHEMA_VERSION) {\n try {\n candidate = migrateTakuhon(data as Takuhon, SCHEMA_VERSION);\n } catch (error) {\n if (error instanceof MigrationError) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: cannot import '${file}': ${error.message}.\\n`,\n };\n }\n throw error;\n }\n }\n\n let imported: Takuhon;\n try {\n imported = importTakuhon(candidate as Takuhon);\n } catch (error) {\n if (error instanceof ImportError) {\n const lines = (error.errors ?? []).map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n const detail = lines.length > 0 ? `:\\n${lines.join('\\n')}` : '.';\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${file}' is not a valid takuhon profile; refusing to import${detail}\\n`,\n };\n }\n throw error;\n }\n\n // Back up the current profile before overwriting it (§5.3 step 3). A missing\n // target is fine — there is nothing to preserve on a fresh import.\n let currentRaw: string | undefined;\n try {\n currentRaw = readFileSync(path, 'utf8');\n } catch (error) {\n if (!isNotFound(error)) {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read current profile '${path}'.\\n` };\n }\n }\n\n let savedPath: string | undefined;\n if (currentRaw !== undefined) {\n // One timestamp for the backup name (createBackup may call the builder\n // twice on a same-second collision).\n const stamp = now();\n try {\n savedPath = createBackup({\n targetPath: path,\n content: currentRaw,\n name: (withMillis) => preImportName(stamp, withMillis),\n });\n } catch (error) {\n const detail = error instanceof BackupError ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: refusing to import into '${path}' — pre-import backup failed: ${detail}\\n`,\n };\n }\n }\n\n try {\n writeFileAtomic(path, `${JSON.stringify(imported, null, 2)}\\n`);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: failed to write '${path}': ${detail}\\n` };\n }\n\n const lines = [`imported ${file} -> ${path} (schemaVersion ${imported.schemaVersion})`];\n if (savedPath !== undefined) {\n lines.push(` previous profile saved to ${savedPath}`);\n }\n return { code: 0, stdout: `${lines.join('\\n')}\\n`, stderr: '' };\n}\n\nfunction isNotFound(error: unknown): boolean {\n return (\n typeof error === 'object' && error !== null && (error as { code?: unknown }).code === 'ENOENT'\n );\n}\n","/**\n * `takuhon mcp [path]` — serve a local takuhon.json over the Model Context\n * Protocol on stdio, so an MCP client (Claude Desktop, …) can read the profile.\n *\n * It connects the transport-agnostic server from `@takuhon/mcp` to a\n * `StdioServerTransport`. The profile is exposed read-only — the same surface as\n * the public HTTP API (`get_profile` / `get_section` / `get_jsonld` /\n * `list_locales` tools; `takuhon://profile` / `takuhon://schema` resources),\n * with the public privacy filter applied. The file is re-read and re-validated\n * on every request, so edits are reflected without restarting.\n *\n * stdio note: stdin/stdout carry the MCP JSON-RPC stream, so this command writes\n * NOTHING to stdout — all diagnostics go to stderr.\n *\n * Like `dev` / `admin`, this is a long-lived process: {@link runMcp} resolves\n * with the exit code only when the client disconnects (stdin EOF) or on a\n * signal.\n *\n * Exit codes:\n * 0 — served, then the client disconnected / Ctrl-C; or `--help`\n * 2 — could not start: bad arguments, or the file is missing / unreadable /\n * not a valid takuhon profile\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { validate, type Takuhon } from '@takuhon/core';\nimport { createTakuhonMcpServer } from '@takuhon/mcp';\n\nconst DEFAULT_PATH = 'takuhon.json';\nconst SERVER_NAME = 'takuhon';\n\nconst USAGE = `Usage: takuhon mcp [path]\n\nServe a takuhon.json over the Model Context Protocol on stdio, so an MCP client\n(e.g. Claude Desktop) can read the profile read-only. With no path, serves\n./takuhon.json. The file is re-read on every request, so edits are reflected\nwithout restarting.\n\nExposes (read-only, the same surface as the public API, privacy filter applied):\n tools: get_profile, get_section, get_jsonld, list_locales\n resources: takuhon://profile, takuhon://schema\n\nstdin/stdout carry the protocol stream; diagnostics go to stderr. The server\nruns until the client disconnects or you press Ctrl-C.\n\nExample Claude Desktop config (claude_desktop_config.json):\n {\n \"mcpServers\": {\n \"my-profile\": { \"command\": \"takuhon\", \"args\": [\"mcp\", \"/path/to/takuhon.json\"] }\n }\n }\n\nExit codes: 0 = served then stopped, 2 = bad arguments / file missing /\nunreadable / invalid.\n`;\n\n/** Injectable seams so tests can capture output and supply a transport. */\nexport interface McpDeps {\n /**\n * Sink for `--help` only. The MCP session never writes to stdout (that is the\n * protocol channel); this exists solely so the help path is testable.\n */\n stdout?: (text: string) => void;\n stderr?: (text: string) => void;\n /** Transport factory; defaults to stdio. Tests inject an in-memory transport. */\n createTransport?: () => Transport;\n}\n\n/** The CLI version, advertised to MCP clients as the server version. */\nconst VERSION = readCliVersion();\n\ninterface ParsedArgs {\n path: string;\n}\n\n/**\n * Read, parse, and validate a takuhon.json. Throws an `Error` with a\n * human-readable message (validation errors carry their JSON Pointers) on any\n * failure — the caller turns it into a startup error or a tool error result.\n */\nexport function loadMcpProfile(path: string): Takuhon {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n throw new Error(`cannot read '${path}'.`);\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n throw new Error(`'${path}' is not valid JSON: ${detail}`, { cause: error });\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n throw new Error(`'${path}' is not a valid takuhon profile:\\n${lines.join('\\n')}`);\n }\n return result.data;\n}\n\n/**\n * Run `takuhon mcp`. Resolves with the process exit code: it stays pending while\n * the server runs and resolves on client disconnect (stdin EOF) or a signal.\n * Argument and load errors return before any transport is connected.\n */\nexport async function runMcp(args: readonly string[] = [], deps: McpDeps = {}): Promise<number> {\n const err = deps.stderr ?? ((text: string) => void process.stderr.write(text));\n\n if (args[0] === '--help' || args[0] === '-h') {\n // --help is the one thing that goes to stdout: it is an ordinary CLI help\n // request, not an active MCP session, so stdout is safe here.\n const out = deps.stdout ?? ((text: string) => void process.stdout.write(text));\n out(USAGE);\n return 0;\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n err(`${parsed.error}\\nRun \\`takuhon mcp --help\\` for usage.\\n`);\n return 2;\n }\n\n // Fast-fail with a clear message if the profile cannot be loaded now: an MCP\n // client launching a broken server would otherwise see only opaque tool\n // errors. A file that later becomes invalid is handled per-request below.\n try {\n loadMcpProfile(parsed.path);\n } catch (error) {\n err(`takuhon: ${error instanceof Error ? error.message : String(error)}\\n`);\n return 2;\n }\n\n const server = createTakuhonMcpServer({\n // Re-read per request so edits are reflected; if the file later becomes\n // invalid, the load failure surfaces to the client as a tool error result.\n loadProfile: () => loadMcpProfile(parsed.path),\n name: SERVER_NAME,\n version: VERSION,\n });\n\n const transport = (deps.createTransport ?? (() => new StdioServerTransport()))();\n await server.connect(transport);\n err(`takuhon mcp: serving ${parsed.path} (read-only) on stdio\\n`);\n\n return await new Promise<number>((resolve) => {\n let done = false;\n function finish(): void {\n if (done) return;\n done = true;\n process.removeListener('SIGINT', shutdown);\n process.removeListener('SIGTERM', shutdown);\n resolve(0);\n }\n function shutdown(): void {\n void server.close();\n finish();\n }\n // The client disconnecting closes the transport (stdin EOF), which the SDK\n // surfaces as the server's `onclose`; that ends the run.\n server.server.onclose = finish;\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n });\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n for (const arg of args) {\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`mcp\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `mcp` takes at most one path argument.' };\n }\n path = arg;\n }\n return { path: path ?? DEFAULT_PATH };\n}\n\n/** Read this CLI's version from its package.json (same source as `--version`). */\nfunction readCliVersion(): string {\n try {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n };\n return pkg.version;\n } catch {\n return '0.0.0';\n }\n}\n","/**\n * `takuhon migrate [path] [--to <version>] [--out <file>] [--dry-run]` —\n * forward-migrate a `takuhon.json` to a newer schema version.\n *\n * Mirrors `validate-command.ts`: {@link runMigrate} is a pure,\n * side-effect-light function that reads/writes files itself but returns its\n * output as strings plus an exit code, leaving stdout/stderr writes and\n * `process.exit` to the entry layer. The source version is read from the\n * file's own `schemaVersion` (the file is the source of truth), so the\n * command takes only `--to`; the default target is the latest schema this\n * build of `@takuhon/core` ships ({@link SCHEMA_VERSION}).\n *\n * The transform is intentionally minimal — `migrateTakuhon` then a re-`validate`\n * gate, with no `meta.updatedAt` touch and no `normalize` pass — so a\n * migration stays lossless (operational-lifecycle §2.6) and its diff is\n * limited to the schema-shape change. Before an in-place write the original\n * bytes are backed up beside the file (§3.1 / §3.3). Migrations are\n * forward-only (§2.4); downgrades surface as a clear error pointing at\n * `takuhon restore`.\n *\n * Exit codes:\n * 0 — migrated, already at the target (no-op), `--dry-run`, or `--help`\n * 1 — the document was read but cannot be migrated to the target (no\n * forward path / downgrade / unusable schemaVersion) or the migrated\n * result unexpectedly failed re-validation\n * 2 — the command could not run: bad arguments, an out-of-window `--to`, a\n * missing/unreadable file, or a file that is not valid JSON\n */\n\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport {\n MigrationError,\n SCHEMA_VERSION,\n SUPPORTED_SCHEMA_VERSIONS,\n migrateTakuhon,\n validate,\n} from '@takuhon/core';\nimport type { Takuhon } from '@takuhon/core';\n\nimport {\n BackupError,\n backupDirFor,\n createBackup,\n migrateBackupName,\n writeFileAtomic,\n} from './backup.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon migrate [path] [--to <version>] [--out <file>] [--dry-run]\n\nForward-migrate a takuhon.json to a newer schema version. The source version\nis read from the file's own schemaVersion. With no path, migrates\n./takuhon.json in the current working directory.\n\nOptions:\n --to <version> Target schema version (default: ${SCHEMA_VERSION}).\n One of: ${SUPPORTED_SCHEMA_VERSIONS.join(', ')}.\n --out <file> Write the result to <file> instead of in place. The source\n file is left unchanged and no backup is created.\n --dry-run Report the planned migration and backup path; write nothing.\n\nBefore an in-place write the current file is backed up to .takuhon-backups/\nbeside it. Migrations are forward-only; to move to an older schema, restore\nfrom a backup with \\`takuhon restore\\`.\n\nExit codes: 0 = migrated / already current / dry-run, 1 = cannot migrate,\n2 = bad arguments / file missing / unreadable / not JSON.\n`;\n\nexport interface MigrateOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies, so tests can pin the backup timestamp. */\nexport interface MigrateDeps {\n /** Clock used for backup filenames. Defaults to `() => new Date()`. */\n now?: () => Date;\n}\n\ninterface ParsedArgs {\n path: string;\n to: string;\n out?: string;\n dryRun: boolean;\n}\n\n/**\n * Run `takuhon migrate` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"migrate\"`). Never throws\n * and never writes to the process streams.\n */\nexport function runMigrate(args: readonly string[] = [], deps: MigrateDeps = {}): MigrateOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon migrate --help\\` for usage.\\n`,\n };\n }\n\n const now = deps.now ?? (() => new Date());\n return migrateFile(parsed, now);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let to: string | undefined;\n let out: string | undefined;\n let dryRun = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--dry-run') {\n dryRun = true;\n continue;\n }\n if (arg === '--to' || arg === '--out') {\n const value = args[i + 1];\n if (value === undefined || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--to') to = value;\n else out = value;\n i++;\n continue;\n }\n if (arg.startsWith('--to=')) {\n to = arg.slice('--to='.length);\n continue;\n }\n if (arg.startsWith('--out=')) {\n out = arg.slice('--out='.length);\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`migrate\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `migrate` takes at most one path argument.' };\n }\n path = arg;\n }\n\n const target = to ?? SCHEMA_VERSION;\n if (!(SUPPORTED_SCHEMA_VERSIONS as readonly string[]).includes(target)) {\n return {\n error: `takuhon: unsupported --to version \"${target}\". Supported: ${SUPPORTED_SCHEMA_VERSIONS.join(', ')}.`,\n };\n }\n\n return { path: path ?? DEFAULT_PATH, to: target, out, dryRun };\n}\n\nfunction migrateFile(parsed: ParsedArgs, now: () => Date): MigrateOutcome {\n const { path, to: target, out, dryRun } = parsed;\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: cannot read '${path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${path}' is not valid JSON: ${detail}\\n` };\n }\n\n const source = (data as { schemaVersion?: unknown }).schemaVersion;\n if (typeof source !== 'string' || source.length === 0) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${path}' has no usable schemaVersion; cannot determine what to migrate from.\\n`,\n };\n }\n\n if (source === target) {\n return {\n code: 0,\n stdout: `${path}: already at schemaVersion ${target}; nothing to do.\\n`,\n stderr: '',\n };\n }\n\n // Back up the original bytes only for a genuine in-place write. `--out` to a\n // different name leaves the source untouched, so a backup would be redundant.\n // The write is atomic (write-temp-then-rename), which replaces the target\n // *name* rather than following it, so even an `--out` symlink that points at\n // the source cannot clobber the source unbacked — it just replaces the link.\n const writeTarget = out ?? path;\n const inPlace = resolve(writeTarget) === resolve(path);\n\n let migrated: Takuhon;\n try {\n migrated = migrateTakuhon(data as Takuhon, target);\n } catch (error) {\n if (error instanceof MigrationError) {\n return {\n code: 1,\n stdout: '',\n stderr:\n `takuhon: cannot migrate '${path}': ${error.message}.\\n` +\n 'Migrations are forward-only; to move to an older schema, restore from a backup with `takuhon restore`.\\n',\n };\n }\n throw error;\n }\n\n // Re-validate as a structural safety gate. The bundled validator accepts the\n // entire supported window, so a non-latest `--to` still passes here;\n // `migrateTakuhon` is what guarantees the result carries the requested target\n // shape (it stops the chain at `target`). This catches only a migration that\n // produced something the schema rejects outright.\n const revalidated = validate(migrated);\n if (!revalidated.ok) {\n const lines = revalidated.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: migrated '${path}' to ${target} but the result failed validation:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n // One timestamp per run, shared by the dry-run preview and the real backup so\n // the reported and actual backup paths cannot drift.\n const stamp = now();\n\n // `--dry-run` previews only after the migration is proven feasible above, so\n // an impossible migration (e.g. a downgrade) still reports exit 1 rather than\n // a misleading \"would migrate\".\n if (dryRun) {\n const lines = [`${path}: would migrate ${source} -> ${target}`, ` write: ${writeTarget}`];\n if (inPlace) {\n lines.push(` backup: ${backupDirFor(path)}/${migrateBackupName(source, stamp)}`);\n } else {\n lines.push(' (source left unchanged; no backup created)');\n }\n return { code: 0, stdout: `${lines.join('\\n')}\\n`, stderr: '' };\n }\n\n let backupPath: string | undefined;\n if (inPlace) {\n try {\n backupPath = createBackup({\n targetPath: path,\n content: raw,\n name: (withMillis) => migrateBackupName(source, stamp, withMillis),\n });\n } catch (error) {\n const detail = error instanceof BackupError ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: refusing to migrate '${path}' — backup failed: ${detail}\\n`,\n };\n }\n }\n\n try {\n writeFileAtomic(writeTarget, `${JSON.stringify(migrated, null, 2)}\\n`);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: failed to write '${writeTarget}': ${detail}\\n`,\n };\n }\n\n const lines = [`migrated ${path}: ${source} -> ${target}`];\n if (inPlace) {\n lines.push(` backup: ${backupPath}`);\n } else {\n lines.push(` wrote: ${writeTarget} (source left unchanged; no backup created)`);\n }\n return { code: 0, stdout: `${lines.join('\\n')}\\n`, stderr: '' };\n}\n","/**\n * `takuhon admin update [path]` — refresh a scaffolded project's `admin-dist/`\n * with the admin form-UI bundle shipped in this `@takuhon/cli`.\n *\n * `create-takuhon` copies the admin SPA into a new project's `admin-dist/` at\n * scaffold time, where it is then frozen (see `scaffold/index.ts`). When the\n * CLI is upgraded to a release that carries a newer admin UI, this command\n * replaces the project's `admin-dist/` with that newer bundle, so the form UI\n * can be updated without re-scaffolding (which would clobber the rest of the\n * project). It only refreshes an existing `admin-dist/`; creating one is\n * `create-takuhon`'s job.\n *\n * Argument handling is a pure function returning streams plus an exit code,\n * like the other one-shot commands; `index.ts` writes the streams and exits.\n *\n * Exit codes:\n * 0 — refreshed (or `--help`)\n * 2 — bad arguments, the path is not a takuhon project, the project has no\n * `admin-dist/` to refresh, or the bundle copy failed (operational\n * errors)\n */\n\nimport { readFileSync } from 'node:fs';\nimport { cp, mkdtemp, readdir, rename, rm, stat } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nimport { resolveAdminBundleDir } from './scaffold/index.js';\nimport { ADMIN_DIST_DIRNAME } from './scaffold/wrangler-toml.js';\n\n/** Marker file that identifies a directory as a takuhon project. */\nconst PROFILE_FILENAME = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon admin update [path]\n\nRefresh a project's ${ADMIN_DIST_DIRNAME}/ with the admin form-UI bundle shipped in\nthis @takuhon/cli. Use it after upgrading @takuhon/cli to pick up a newer admin\nUI without re-scaffolding. With no path, refreshes the project in the current\ndirectory. The project must already have an ${ADMIN_DIST_DIRNAME}/ (created by\ncreate-takuhon); this command updates it, it does not create one.\n\nExit codes: 0 = refreshed, 2 = bad arguments / not a takuhon project /\nno ${ADMIN_DIST_DIRNAME}/ to refresh / copy failed.\n`;\n\nexport interface RefreshAdminOptions {\n /**\n * Source bundle directory. Defaults to the bundle shipped in this package\n * ({@link resolveAdminBundleDir}); overridable for tests.\n */\n readonly bundleDir?: string;\n}\n\nexport interface RefreshAdminOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/**\n * Run `takuhon admin update` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"admin\" \"update\"`).\n *\n * Handles `--help` / `-h`, rejects unknown flags and extra positionals, then\n * replaces the project's `admin-dist/` with the shipped bundle. Never throws\n * and never writes to the process streams — the caller renders the returned\n * `stdout` / `stderr` and exits with `code`.\n */\nexport async function runRefreshAdmin(\n args: readonly string[] = [],\n opts: RefreshAdminOptions = {},\n): Promise<RefreshAdminOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const unknownFlag = args.find((arg) => arg.startsWith('-'));\n if (unknownFlag !== undefined) {\n return usageError(`unknown option '${unknownFlag}'.`);\n }\n\n if (args.length > 1) {\n return usageError('`admin update` takes at most one path argument.');\n }\n\n const displayDir = args[0] ?? '.';\n const projectDir = resolve(args[0] ?? '.');\n\n // Guard 1: a takuhon.json marks this as a takuhon project, so we never\n // clobber an unrelated admin-dist/ in some other kind of directory.\n try {\n await stat(join(projectDir, PROFILE_FILENAME));\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: '${displayDir}' is not a takuhon project (no ${PROFILE_FILENAME} found).\\n` +\n `Run \\`takuhon admin update\\` from a project created by create-takuhon.\\n`,\n };\n }\n\n // Guard 2: refresh updates an existing bundle; it does not install one.\n const adminDist = join(projectDir, ADMIN_DIST_DIRNAME);\n const adminDistStat = await stat(adminDist).catch(() => undefined);\n if (adminDistStat === undefined) {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: '${displayDir}' has no ${ADMIN_DIST_DIRNAME}/ directory to refresh.\\n` +\n `This project was not scaffolded with the admin form UI; create a new project ` +\n `with create-takuhon to add it.\\n`,\n };\n }\n if (!adminDistStat.isDirectory()) {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: '${join(displayDir, ADMIN_DIST_DIRNAME)}' exists but is not a directory.\\n`,\n };\n }\n\n const bundleDir = opts.bundleDir ?? resolveAdminBundleDir();\n\n let fileCount: number;\n let staged: string | undefined;\n try {\n fileCount = (await readdir(bundleDir, { recursive: true, withFileTypes: true })).filter(\n (entry) => entry.isFile(),\n ).length;\n // Stage the new bundle in a temp directory alongside admin-dist/, then swap\n // it in with a rename. A mid-copy failure leaves the staging dir (cleaned up\n // below), never a half-written admin-dist/, and the rename also drops any\n // stale files the previous bundle had that the new one does not.\n staged = await mkdtemp(join(projectDir, `.${ADMIN_DIST_DIRNAME}-`));\n await cp(bundleDir, staged, { recursive: true });\n await rm(adminDist, { recursive: true, force: true });\n await rename(staged, adminDist);\n staged = undefined;\n } catch (err) {\n if (staged !== undefined) {\n await rm(staged, { recursive: true, force: true }).catch(() => undefined);\n }\n // The bundle ships inside @takuhon/cli, so a copy failure usually means a\n // broken or incomplete install. Surface a sanitized message — the raw error\n // would embed the absolute node_modules path of this package.\n const code = isNodeErrnoException(err) ? ` (${err.code})` : '';\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: failed to refresh ${ADMIN_DIST_DIRNAME}/ from @takuhon/cli${code}. ` +\n `Reinstall or upgrade @takuhon/cli and try again.\\n`,\n };\n }\n\n const plural = fileCount === 1 ? '' : 's';\n return {\n code: 0,\n stdout: `Refreshed ${ADMIN_DIST_DIRNAME}/ from @takuhon/cli@${readVersion()} (${fileCount} file${plural}).\\n`,\n stderr: '',\n };\n}\n\nfunction usageError(message: string): RefreshAdminOutcome {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: ${message}\\nRun \\`takuhon admin update --help\\` for usage.\\n`,\n };\n}\n\n/**\n * The reported version is sourced from package.json at runtime (relative to\n * this module) so it can never drift from the bundle actually shipped in this\n * `@takuhon/cli`.\n */\nfunction readVersion(): string {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n };\n return pkg.version;\n}\n\nfunction isNodeErrnoException(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && typeof (err as NodeJS.ErrnoException).code === 'string';\n}\n","/**\n * `takuhon restore --from <backup> [path] [--yes]` — overwrite a profile with\n * a previously saved backup (operational-lifecycle §4.1 / §4.3).\n *\n * Like the other command runners this returns its output as strings plus an\n * exit code rather than touching the process streams. It is `async` because\n * the confirmation prompt is interactive: the entry layer injects a `confirm`\n * callback that reads from the terminal, while tests inject a deterministic\n * predicate (or pass `--yes` to skip the prompt entirely).\n *\n * Restore is destructive, so the flow is deliberately careful:\n * 1. read and schema-`validate` the backup — never restore an invalid file;\n * 2. confirm (unless `--yes`); a non-interactive run with neither `--yes`\n * nor an injected `confirm` refuses rather than overwriting silently;\n * 3. back up the current profile to `.takuhon-backups/pre-restore-…json`;\n * 4. write the backup's bytes to the target verbatim (a faithful\n * reproduction; `validate` is only the gate, not a rewrite).\n *\n * Edge-cache purge (§4.1 step 4) is a remote-adapter concern and does not\n * apply to this local-file command.\n *\n * Exit codes:\n * 0 — restored, aborted at the prompt, or `--help`\n * 1 — the backup was read but failed schema validation\n * 2 — the command could not run: bad arguments, a missing `--from`, an\n * unreadable backup or current profile, a non-JSON backup, or a refusal\n * to overwrite without confirmation\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { validate } from '@takuhon/core';\nimport type { Takuhon } from '@takuhon/core';\n\nimport {\n BackupError,\n backupDirFor,\n createBackup,\n preRestoreName,\n writeFileAtomic,\n} from './backup.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon restore --from <backup> [path] [--yes]\n\nOverwrite a profile with a previously saved backup. With no path, restores\n./takuhon.json in the current working directory.\n\nOptions:\n --from <backup> Backup file to restore from (required).\n --yes, -y Skip the confirmation prompt.\n\nThe backup is schema-validated first, and the current profile is saved to\n.takuhon-backups/pre-restore-<timestamp>.json before being overwritten.\n\nExit codes: 0 = restored / aborted, 1 = backup failed validation,\n2 = bad arguments / file missing / unreadable / not JSON / unconfirmed.\n`;\n\nexport interface RestoreOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies for deterministic tests and interactive prompting. */\nexport interface RestoreDeps {\n /** Clock used for the pre-restore backup filename. Defaults to `() => new Date()`. */\n now?: () => Date;\n /**\n * Confirmation prompt. Receives the rendered message and resolves to the\n * user's decision. Omitted in non-interactive contexts, where restore then\n * refuses unless `--yes` was passed.\n */\n confirm?: (message: string) => boolean | Promise<boolean>;\n}\n\ninterface ParsedArgs {\n from: string;\n path: string;\n yes: boolean;\n}\n\n/**\n * Run `takuhon restore` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"restore\"`).\n */\nexport async function runRestore(\n args: readonly string[] = [],\n deps: RestoreDeps = {},\n): Promise<RestoreOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon restore --help\\` for usage.\\n`,\n };\n }\n\n const now = deps.now ?? (() => new Date());\n return restoreFile(parsed, now, deps.confirm);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let from: string | undefined;\n let path: string | undefined;\n let yes = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--yes' || arg === '-y') {\n yes = true;\n continue;\n }\n if (arg === '--from') {\n const value = args[i + 1];\n if (value === undefined || value.startsWith('-')) {\n return { error: 'takuhon: `--from` requires a value.' };\n }\n from = value;\n i++;\n continue;\n }\n if (arg.startsWith('--from=')) {\n from = arg.slice('--from='.length);\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`restore\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `restore` takes at most one path argument.' };\n }\n path = arg;\n }\n\n if (from === undefined || from.length === 0) {\n return { error: 'takuhon: `restore` requires `--from <backup>`.' };\n }\n\n return { from, path: path ?? DEFAULT_PATH, yes };\n}\n\nasync function restoreFile(\n parsed: ParsedArgs,\n now: () => Date,\n confirm: RestoreDeps['confirm'],\n): Promise<RestoreOutcome> {\n const { from, path, yes } = parsed;\n\n let backupRaw: string;\n try {\n backupRaw = readFileSync(from, 'utf8');\n } catch {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read backup '${from}'.\\n` };\n }\n\n let backupData: unknown;\n try {\n backupData = JSON.parse(backupRaw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: backup '${from}' is not valid JSON: ${detail}\\n`,\n };\n }\n\n const result = validate(backupData);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: backup '${from}' is not a valid takuhon profile; refusing to restore:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n // Read the current profile up front: its existence shapes the confirmation\n // message and decides whether a pre-restore backup is needed.\n let currentRaw: string | undefined;\n try {\n currentRaw = readFileSync(path, 'utf8');\n } catch (error) {\n if (!isNotFound(error)) {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read current profile '${path}'.\\n` };\n }\n }\n\n // One timestamp per run, shared by the prompt preview and the actual\n // pre-restore backup so the reported and written paths cannot drift.\n const stamp = now();\n const preRestorePath = `${backupDirFor(path)}/${preRestoreName(stamp)}`;\n\n if (!yes) {\n if (!confirm) {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: refusing to overwrite '${path}' without confirmation.\\n` +\n 'Re-run interactively, or pass `--yes` to skip the prompt.\\n',\n };\n }\n const decided = await confirm(\n confirmationMessage(path, from, result.data, currentRaw, preRestorePath),\n );\n if (!decided) {\n return { code: 0, stdout: 'Aborted; no changes made.\\n', stderr: '' };\n }\n }\n\n let savedPath: string | undefined;\n if (currentRaw !== undefined) {\n try {\n savedPath = createBackup({\n targetPath: path,\n content: currentRaw,\n name: (withMillis) => preRestoreName(stamp, withMillis),\n });\n } catch (error) {\n const detail = error instanceof BackupError ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: refusing to restore '${path}' — pre-restore backup failed: ${detail}\\n`,\n };\n }\n }\n\n // Write the backup's bytes verbatim: restore is a faithful reproduction of\n // the saved state, and `validate` above was only the gate.\n try {\n writeFileAtomic(path, backupRaw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: failed to write '${path}': ${detail}\\n` };\n }\n\n const lines = [`restored ${path} from ${from}`];\n if (savedPath !== undefined) {\n lines.push(` previous profile saved to ${savedPath}`);\n }\n return { code: 0, stdout: `${lines.join('\\n')}\\n`, stderr: '' };\n}\n\nfunction confirmationMessage(\n path: string,\n from: string,\n data: Takuhon,\n currentRaw: string | undefined,\n preRestorePath: string,\n): string {\n const when = typeof data.meta.updatedAt === 'string' ? ` (from ${data.meta.updatedAt})` : '';\n const preNote =\n currentRaw !== undefined\n ? `Your current profile will be saved as ${preRestorePath}.`\n : `(no existing profile at ${path} to preserve)`;\n return (\n `This will overwrite the profile at ${path} with the backup ${from}${when}.\\n` +\n `${preNote}\\n` +\n 'Continue? [y/N]'\n );\n}\n\nfunction isNotFound(error: unknown): boolean {\n return (\n typeof error === 'object' && error !== null && (error as { code?: unknown }).code === 'ENOENT'\n );\n}\n","/**\n * `takuhon sync [path] --url <base-url> [--if-match <etag>] [--dry-run]` —\n * push a local `takuhon.json` to a deployed takuhon instance.\n *\n * `sync` is the secondary update path in Spec §9.6: it mirrors the local\n * profile into the deployment's store by calling the same admin write\n * endpoint the Admin UI uses — `PUT <base-url>/api/admin/profile` — so it\n * reuses the server's schema validation, optimistic locking, audit logging,\n * and edge-cache purge. The local file is the source of truth: by default the\n * push is unconditional (a mirror). Pass `--if-match <etag>` to opt into\n * optimistic locking (the server rejects the write with 409 if the stored\n * version no longer matches).\n *\n * The admin bearer token is read from the `TAKUHON_ADMIN_TOKEN` environment\n * variable, never a flag, so it cannot leak into shell history; the name\n * matches the Worker secret. The document is sent as-is after a local schema\n * check — `sync` does not migrate (run `takuhon migrate` first if the file is\n * an older schema version).\n *\n * Exit codes:\n * 0 — synced (or, with --dry-run, the payload is valid and ready to send)\n * 1 — the local file failed validation, or the remote refused the write for\n * content reasons (422 validation, 409 If-Match conflict)\n * 2 — the command could not run: bad arguments, a missing/unreadable/non-JSON\n * file, an unset token, an auth failure (401/403), a network error, or\n * any other non-success response\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { validate } from '@takuhon/core';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\n/** Admin write endpoint, appended to the supplied `--url` origin. */\nconst ADMIN_PROFILE_PATH = '/api/admin/profile';\n\n/** Environment variable carrying the deployment's admin bearer token. */\nconst TOKEN_ENV = 'TAKUHON_ADMIN_TOKEN';\n\nconst USAGE = `Usage: takuhon sync [path] --url <base-url> [--if-match <etag>] [--dry-run]\n\nPush a local takuhon.json to a deployed takuhon instance by calling its admin\nwrite endpoint (PUT <base-url>/api/admin/profile). With no path, syncs\n./takuhon.json in the current working directory.\n\nThe local file is the source of truth: by default the push is unconditional\n(a mirror). Pass --if-match <etag> to opt into optimistic locking (the server\nreturns 409 if the stored version no longer matches). The document is sent\nas-is after a local schema check; run \\`takuhon migrate\\` first if it is an\nolder schema version.\n\nThe admin bearer token is read from the ${TOKEN_ENV} environment variable, e.g.:\n ${TOKEN_ENV}=... takuhon sync --url https://me.example\n\nOptions:\n --url <base-url> Required. Absolute http(s) origin of the deployment.\n --if-match <etag> Send If-Match for optimistic locking (opt-in).\n --dry-run Validate locally and report what would be sent; no request.\n\nExit codes: 0 = synced (or dry-run ok), 1 = local invalid / remote refused\n(422 / 409), 2 = bad arguments / file missing / unreadable / not JSON / token\nunset / auth failure / network error / other non-success response.\n`;\n\nexport interface SyncOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies, so tests can stub the network and the token source. */\nexport interface SyncDeps {\n /** HTTP client. Defaults to the global `fetch` (Node 22+). */\n fetch?: typeof fetch;\n /** Admin token source. Defaults to reading `TAKUHON_ADMIN_TOKEN`. */\n getToken?: () => string | undefined;\n}\n\ninterface ParsedArgs {\n path: string;\n url: string;\n ifMatch?: string;\n dryRun: boolean;\n}\n\n/**\n * Run `takuhon sync` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"sync\"`).\n */\nexport async function runSync(\n args: readonly string[] = [],\n deps: SyncDeps = {},\n): Promise<SyncOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon sync --help\\` for usage.\\n`,\n };\n }\n\n return syncProfile(parsed, deps);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let url: string | undefined;\n let ifMatch: string | undefined;\n let dryRun = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--dry-run') {\n dryRun = true;\n continue;\n }\n if (arg === '--url' || arg === '--if-match') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--url') url = value;\n else ifMatch = value;\n i++;\n continue;\n }\n if (arg.startsWith('--url=')) {\n const value = arg.slice('--url='.length);\n if (value === '') return { error: 'takuhon: `--url` requires a value.' };\n url = value;\n continue;\n }\n if (arg.startsWith('--if-match=')) {\n const value = arg.slice('--if-match='.length);\n if (value === '') return { error: 'takuhon: `--if-match` requires a value.' };\n ifMatch = value;\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`sync\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `sync` takes at most one path argument.' };\n }\n path = arg;\n }\n\n if (url === undefined) {\n return { error: 'takuhon: `sync` requires `--url <base-url>`.' };\n }\n const base = parseOrigin(url);\n if ('error' in base) return base;\n\n return { path: path ?? DEFAULT_PATH, url: base.origin, ifMatch, dryRun };\n}\n\n/**\n * Validate `--url` and reduce it to a bare `http(s)` origin. We reject any URL\n * carrying a path, query, fragment, or credentials: the admin endpoint is\n * appended to the origin, and a non-origin URL would both misplace that suffix\n * and risk sending the bearer token to an unintended target.\n */\nfunction parseOrigin(value: string): { origin: string } | { error: string } {\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n return { error: 'takuhon: `--url` must be an absolute http(s) URL.' };\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return { error: 'takuhon: `--url` must be an absolute http(s) URL.' };\n }\n if (\n parsed.username !== '' ||\n parsed.password !== '' ||\n parsed.search !== '' ||\n parsed.hash !== '' ||\n (parsed.pathname !== '/' && parsed.pathname !== '')\n ) {\n return {\n error:\n \"takuhon: `--url` must be the deployment's origin (e.g. https://me.example), \" +\n 'with no path, query, or credentials.',\n };\n }\n return { origin: parsed.origin };\n}\n\n/** Remove any literal occurrence of a secret from a string before it is shown. */\nfunction redact(text: string, secret: string): string {\n return secret === '' ? text : text.split(secret).join('***');\n}\n\n/** Strip RFC 7232 double-quote delimiters so the value can be re-wrapped cleanly. */\nfunction unquoteETag(raw: string): string {\n const trimmed = raw.trim();\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"') && trimmed.length >= 2) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\nasync function syncProfile(parsed: ParsedArgs, deps: SyncDeps): Promise<SyncOutcome> {\n const { path, url, ifMatch, dryRun } = parsed;\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read '${path}'.\\n` };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${path}' is not valid JSON: ${detail}\\n` };\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${path}' is not a valid takuhon profile; refusing to sync:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n const endpoint = `${url}${ADMIN_PROFILE_PATH}`;\n const body = `${JSON.stringify(result.data)}\\n`;\n const bytes = Buffer.byteLength(body, 'utf8');\n\n if (dryRun) {\n const lock = ifMatch !== undefined ? `, If-Match \"${unquoteETag(ifMatch)}\"` : '';\n return {\n code: 0,\n stdout: `would sync ${path} -> ${endpoint} (${String(bytes)} bytes${lock})\\n--dry-run: nothing sent.\\n`,\n stderr: '',\n };\n }\n\n const getToken = deps.getToken ?? (() => process.env[TOKEN_ENV]);\n const token = getToken();\n if (token === undefined || token === '') {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: ${TOKEN_ENV} is not set; sync needs the deployment's admin token.\\n` +\n `Set it for this command only, e.g.:\\n` +\n ` ${TOKEN_ENV}=... takuhon sync --url ${url}\\n`,\n };\n }\n\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n authorization: `Bearer ${token}`,\n };\n if (ifMatch !== undefined) {\n headers['if-match'] = `\"${unquoteETag(ifMatch)}\"`;\n }\n\n const fetchImpl = deps.fetch ?? fetch;\n let res: Response;\n try {\n res = await fetchImpl(endpoint, { method: 'PUT', headers, body });\n } catch (error) {\n const detail = redact(error instanceof Error ? error.message : String(error), token);\n return { code: 2, stdout: '', stderr: `takuhon: could not reach ${endpoint}: ${detail}\\n` };\n }\n\n return interpretResponse(res, { path, url, endpoint });\n}\n\ninterface Target {\n path: string;\n url: string;\n endpoint: string;\n}\n\nasync function interpretResponse(res: Response, target: Target): Promise<SyncOutcome> {\n const { path, url, endpoint } = target;\n\n if (res.ok) {\n const version = await readVersion(res);\n if (version === undefined) {\n // A 2xx without the admin API's `{ meta: { version } }` envelope means we\n // did not actually reach a takuhon admin endpoint (a proxy login page, a\n // static host, a wrong path). Do not claim success.\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: unexpected response from ${endpoint} (HTTP ${String(res.status)} but no meta.version); is --url a takuhon deployment?\\n`,\n };\n }\n return { code: 0, stdout: `synced ${path} -> ${url} (version ${version})\\n`, stderr: '' };\n }\n\n const problem = await readProblem(res);\n const status = res.status;\n\n if (status === 409) {\n const current = problem?.currentVersion;\n const hint =\n current !== undefined\n ? `remote is at version ${current}. Re-sync against it, or drop --if-match to overwrite.`\n : 'the stored version no longer matches --if-match. Re-sync, or drop --if-match to overwrite.';\n return { code: 1, stdout: '', stderr: `takuhon: sync conflict (409): ${hint}\\n` };\n }\n\n if (status === 422) {\n const errors = Array.isArray(problem?.errors) ? problem.errors : [];\n const lines = errors.map((e) => ` ${e.path}: ${e.message}`);\n const detail = lines.length > 0 ? `:\\n${lines.join('\\n')}` : '.';\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: the deployment rejected the profile (422)${detail}\\n`,\n };\n }\n\n if (status === 401 || status === 403) {\n const reason =\n status === 401\n ? `unauthorized (401); check ${TOKEN_ENV}`\n : 'forbidden (403); the request origin may not be allowed';\n return { code: 2, stdout: '', stderr: `takuhon: ${reason}.\\n` };\n }\n\n const detail = problem?.detail ?? res.statusText ?? '';\n const tail = detail ? `: ${detail}` : '.';\n return { code: 2, stdout: '', stderr: `takuhon: sync failed (${String(status)})${tail}\\n` };\n}\n\ninterface ProblemBody {\n detail?: string;\n currentVersion?: string;\n errors?: { path: string; message: string }[];\n}\n\n/** Best-effort parse of the success body's `meta.version`. */\nasync function readVersion(res: Response): Promise<string | undefined> {\n try {\n const parsed = (await res.json()) as { meta?: { version?: unknown } };\n const version = parsed.meta?.version;\n return typeof version === 'string' ? version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort parse of an RFC 7807 problem body; undefined if it is not JSON. */\nasync function readProblem(res: Response): Promise<ProblemBody | undefined> {\n let text: string;\n try {\n text = await res.text();\n } catch {\n return undefined;\n }\n try {\n return JSON.parse(text) as ProblemBody;\n } catch {\n return undefined;\n }\n}\n","/**\n * `takuhon validate [path]` — validate a `takuhon.json` against `@takuhon/core`.\n *\n * All argument handling and the validation itself live here as pure,\n * side-effect-free functions so the whole command is unit-testable: `index.ts`\n * runs `process.exit(main(...))` at module top level and is therefore not\n * import-safe. `runValidate` reads the target file itself but returns its\n * output as strings plus an exit code, leaving the actual stdout/stderr writes\n * and `process.exit` to the caller.\n *\n * Exit codes:\n * 0 — the document is valid (or `--help` was requested)\n * 1 — the document was read and parsed but failed schema validation\n * 2 — the command could not run: bad arguments, a missing/unreadable file,\n * or a file that is not valid JSON (operational errors, distinct from an\n * invalid-but-readable document)\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { validate } from '@takuhon/core';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon validate [path]\n\nValidate a takuhon.json against the takuhon schema. With no path, validates\n./takuhon.json in the current working directory.\n\nExit codes: 0 = valid, 1 = invalid, 2 = file missing / unreadable / not JSON.\n`;\n\nexport interface ValidateOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/**\n * Run `takuhon validate` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"validate\"`).\n *\n * Handles `--help` / `-h` and rejects extra positionals, then validates the\n * `takuhon.json` at the single optional path argument (default\n * `./takuhon.json`). Never throws and never writes to the process streams —\n * the caller renders the returned `stdout` / `stderr` and exits with `code`.\n */\nexport function runValidate(args: readonly string[] = []): ValidateOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n if (args.length > 1) {\n return {\n code: 2,\n stdout: '',\n stderr:\n 'takuhon: `validate` takes at most one path argument.\\n' +\n 'Run `takuhon validate --help` for usage.\\n',\n };\n }\n\n return validateFile(args[0]);\n}\n\n/** Read, parse, and schema-validate the profile at `pathArg` (default `./takuhon.json`). */\nfunction validateFile(pathArg?: string): ValidateOutcome {\n const target = pathArg ?? DEFAULT_PATH;\n\n let raw: string;\n try {\n raw = readFileSync(target, 'utf8');\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: cannot read '${target}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: '${target}' is not valid JSON: ${detail}\\n`,\n };\n }\n\n const result = validate(data);\n if (result.ok) {\n return {\n code: 0,\n stdout: `${target}: valid (schemaVersion ${result.data.schemaVersion}).\\n`,\n stderr: '',\n };\n }\n\n const count = result.errors.length;\n const lines = result.errors.map((error) => ` ${error.pointer || '/'}: ${error.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `${target}: invalid (${count} error${count === 1 ? '' : 's'}):\\n${lines.join('\\n')}\\n`,\n };\n}\n"],"mappings":"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","names":["readFileSync","readFileSync","dirname","join","dirname","join","join","dirname","join","dirname","readFileSync","randomBytes","readFileSync","extname","join","resolve","sep","readFileSync","CSS","renderEntry","renderHeader","DEFAULT_PATH","readFileSync","resolve","mkdirSync","readFileSync","renameSync","rmSync","writeFileSync","basename","dirname","join","resolve","isNotFound","contentType","readFileSync","rmSync","NotFoundError","isNotFound","readFileSync","NotFoundError","rmSync","DEFAULT_PATH","DEFAULT_PORT","USAGE","extname","isNotFound","resolve","sep","readFileSync","join","contentType","parseArgs","isHttpUrl","randomBytes","mkdirSync","readFileSync","dirname","join","applyPublicPrivacyFilter","normalize","renderActivitySvg","validate","DEFAULT_PATH","USAGE","parseArgs","isHttpUrl","readFileSync","validate","applyPublicPrivacyFilter","normalize","join","mkdirSync","dirname","renderActivitySvg","readFileSync","resolve","validate","DEFAULT_PATH","USAGE","parseArgs","resolve","readFileSync","validate","readFileSync","DEFAULT_PATH","USAGE","parseArgs","readFileSync","lines","isNotFound","readFileSync","validate","DEFAULT_PATH","USAGE","parseArgs","resolve","pkg","readFileSync","resolve","MigrationError","SCHEMA_VERSION","migrateTakuhon","validate","DEFAULT_PATH","USAGE","SCHEMA_VERSION","parseArgs","readFileSync","resolve","migrateTakuhon","MigrationError","validate","lines","readFileSync","join","resolve","USAGE","resolve","join","pkg","readFileSync","readFileSync","validate","DEFAULT_PATH","USAGE","parseArgs","readFileSync","validate","lines","isNotFound","readFileSync","validate","DEFAULT_PATH","USAGE","parseArgs","readVersion","detail","readFileSync","validate","DEFAULT_PATH","USAGE","readFileSync","VERSION"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/activity-command.ts","../src/file-activity-storage.ts","../src/backup.ts","../src/admin-command.ts","../src/dev-command.ts","../src/file-asset-storage.ts","../src/file-storage.ts","../src/build-command.ts","../src/export-command.ts","../src/import-command.ts","../src/mcp-command.ts","../src/migrate-command.ts","../src/refresh-admin-command.ts","../src/restore-command.ts","../src/sync-command.ts","../src/validate-command.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * `@takuhon/cli` entry point — the `takuhon` command.\n *\n * Exposes `--version` / `--help`, the local profile commands (`validate`,\n * `migrate`, `restore`, `export`, `import`, `build`, `dev`, `admin`), `sync`\n * (push a local profile to a deployment), `activity` (sync / show the\n * developer-activity snapshot), and a pointer to `create-takuhon` for\n * scaffolding.\n *\n * `main` is pure (returns an exit code, never calls `process.exit`); the only\n * place that exits the process is {@link run}, invoked either when this module\n * is the entry script or by the bare-name `takuhon` package's `bin.mjs`, which\n * imports and calls `run()`. Keeping `process.exit` at that single boundary\n * lets tests import this module without terminating the test runner.\n */\n\nimport { readFileSync, realpathSync } from 'node:fs';\nimport { stdin, stdout } from 'node:process';\nimport { createInterface } from 'node:readline/promises';\nimport { fileURLToPath } from 'node:url';\n\nimport { runActivityShow, runActivitySync } from './activity-command.js';\nimport { runAdmin } from './admin-command.js';\nimport { runBuild } from './build-command.js';\nimport { runDev } from './dev-command.js';\nimport { runExport } from './export-command.js';\nimport { runImport } from './import-command.js';\nimport { runMcp } from './mcp-command.js';\nimport { runMigrate } from './migrate-command.js';\nimport { runRefreshAdmin } from './refresh-admin-command.js';\nimport { runRestore } from './restore-command.js';\nimport { runSync } from './sync-command.js';\nimport { runValidate } from './validate-command.js';\n\n// Source the reported version from package.json (read at runtime relative to\n// this module) so `takuhon --version` can never drift from the published\n// release — there is no hand-maintained version literal to fall out of sync.\nconst pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n};\nconst VERSION = pkg.version;\n\nconst HELP = `takuhon ${VERSION}\n\nTakuhon — open-source portable profile API server.\n\nUsage:\n takuhon --version Show the installed CLI version\n takuhon --help Show this help\n\nCommands:\n takuhon validate [path] Validate a takuhon.json (default: ./takuhon.json)\n takuhon migrate [path] [--to <v>] Forward-migrate a takuhon.json to a newer schema\n version (default target: latest). Backs up first.\n Add --out <file> to write elsewhere, --dry-run to preview.\n takuhon restore --from <backup> Restore a profile from a backup (prompts before\n overwriting; pass --yes to skip).\n takuhon export [path] [--output <f>] Serialise a takuhon.json to stdout (or --output file).\n takuhon import <file> [path] Import an exported profile into a takuhon.json,\n migrating to the current schema version. Backs up first.\n takuhon build [path] [--output <d>] Render a takuhon.json into a static site (HTML +\n JSON-LD, one page per locale). --base-url <url> adds\n absolute canonical/hreflang links.\n takuhon dev [path] [--port <n>] Serve a takuhon.json as a local static preview,\n re-rendered on each request (default port: 4321).\n --base-url <url> adds canonical/hreflang links.\n takuhon admin [path] [--port <n>] Run a local admin server: edit the profile through\n the form UI at /admin (writes takuhon.json, backs up\n first) with a preview at / (default port: 4322). Binds\n 127.0.0.1; prints a per-run token to paste into the form.\n takuhon admin update [path] Refresh a project's admin-dist/ with the admin form UI\n bundled in this @takuhon/cli (use after upgrading the\n CLI). Updates an existing admin-dist/ only.\n takuhon mcp [path] Serve a takuhon.json over the Model Context Protocol on\n stdio (read-only), so an MCP client such as Claude Desktop\n can read the profile. Re-reads the file per request.\n takuhon sync [path] --url <url> Push a takuhon.json to a deployment's admin API\n (PUT <url>/api/admin/profile). Reads the admin token\n from TAKUHON_ADMIN_TOKEN. --if-match <etag> opts into\n optimistic locking; --dry-run previews without sending.\n takuhon activity sync [path] Fetch the GitHub / WakaTime activity configured in\n settings.activity and store it as activity.json beside\n the profile. Secrets come from the environment only:\n TAKUHON_GITHUB_TOKEN (optional) / TAKUHON_WAKATIME_KEY.\n takuhon activity show [path] Print the stored activity snapshot (activity.json).\n\nScaffolding a new profile project:\n npx create-takuhon my-profile\n npx create-takuhon my-profile --license CC-BY-4.0\n`;\n\nasync function main(argv: readonly string[]): Promise<number> {\n const first = argv[0];\n\n if (first === '--version' || first === '-v') {\n process.stdout.write(`${VERSION}\\n`);\n return 0;\n }\n\n if (first === undefined || first === '--help' || first === '-h') {\n process.stdout.write(HELP);\n return 0;\n }\n\n if (first === 'validate') {\n return emit(runValidate(argv.slice(1)));\n }\n\n if (first === 'migrate') {\n return emit(runMigrate(argv.slice(1)));\n }\n\n if (first === 'export') {\n return emit(runExport(argv.slice(1)));\n }\n\n if (first === 'build') {\n return emit(runBuild(argv.slice(1)));\n }\n\n if (first === 'dev') {\n // `dev` runs a long-lived server and streams its own output, so it does not\n // go through `emit` (a one-shot result writer); it returns the exit code\n // directly and resolves only on graceful shutdown.\n return runDev(argv.slice(1));\n }\n\n if (first === 'admin') {\n if (argv[1] === 'update') {\n // One-shot bundle refresh, not the long-lived server: goes through `emit`.\n return emit(await runRefreshAdmin(argv.slice(2)));\n }\n // Long-lived server, like `dev`: returns the exit code directly.\n return runAdmin(argv.slice(1));\n }\n\n if (first === 'mcp') {\n // Long-lived stdio server, like `dev`: returns the exit code directly and\n // resolves on client disconnect / Ctrl-C. stdout is the MCP channel, so it\n // does not go through `emit`.\n return runMcp(argv.slice(1));\n }\n\n if (first === 'import') {\n return emit(runImport(argv.slice(1)));\n }\n\n if (first === 'restore') {\n // Only offer an interactive prompt on a real TTY; otherwise `runRestore`\n // refuses to overwrite without `--yes`, which is the safe default for\n // pipelines.\n const confirm = stdin.isTTY ? promptConfirm : undefined;\n return emit(await runRestore(argv.slice(1), { confirm }));\n }\n\n if (first === 'sync') {\n return emit(await runSync(argv.slice(1)));\n }\n\n if (first === 'activity') {\n const sub = argv[1];\n if (sub === 'sync') {\n return emit(await runActivitySync(argv.slice(2)));\n }\n if (sub === 'show') {\n return emit(await runActivityShow(argv.slice(2)));\n }\n const usage =\n 'Subcommands:\\n' +\n ' takuhon activity sync [path] Fetch and store the activity snapshot\\n' +\n ' takuhon activity show [path] Print the stored activity snapshot\\n' +\n 'Run `takuhon activity sync --help` for details.\\n';\n if (sub === '--help' || sub === '-h') {\n process.stdout.write(usage);\n return 0;\n }\n process.stderr.write(\n sub === undefined\n ? `takuhon: \\`activity\\` requires a subcommand.\\n${usage}`\n : `takuhon: unknown subcommand \\`${sub}\\` for \\`activity\\`.\\n${usage}`,\n );\n return 2;\n }\n\n process.stderr.write(\n `takuhon: unknown command '${first}'\\n` +\n `Run \\`takuhon --help\\` for usage. For scaffolding a new project, use \\`create-takuhon\\`.\\n`,\n );\n return 2;\n}\n\n/** Write a command outcome's streams and return its exit code. */\nfunction emit(outcome: { code: number; stdout: string; stderr: string }): number {\n if (outcome.stdout) process.stdout.write(outcome.stdout);\n if (outcome.stderr) process.stderr.write(outcome.stderr);\n return outcome.code;\n}\n\n/** Interactive [y/N] confirmation used by `restore` on a TTY. */\nasync function promptConfirm(message: string): Promise<boolean> {\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n const answer = await rl.question(`${message} `);\n return /^y(es)?$/i.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n\n/**\n * Process entry point: run {@link main} and exit with its code. This is the\n * only function that calls `process.exit`. Exported so the bare-name\n * `takuhon` package can invoke it after importing this module.\n */\nexport async function run(argv: readonly string[] = process.argv.slice(2)): Promise<void> {\n try {\n process.exit(await main(argv));\n } catch (error) {\n process.stderr.write(`takuhon: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n}\n\n/** True when this module was started directly (`node …/index.js`). */\nfunction isEntrypoint(): boolean {\n const entry = process.argv[1];\n if (entry === undefined) return false;\n try {\n return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));\n } catch {\n return false;\n }\n}\n\nif (isEntrypoint()) {\n void run();\n}\n","/**\n * `takuhon activity sync [path]` — fetch the GitHub / WakaTime activity\n * configured under `settings.activity` and store the snapshot as an\n * `activity.json` beside the profile, plus `takuhon activity show [path]` to\n * print what is stored.\n *\n * `activity` is a subcommand namespace (not a flag on `takuhon sync`): `sync`\n * already means \"push the profile to a deployment\", and the activity sync is a\n * different operation against different storage. Secrets are read from the\n * environment only (`TAKUHON_GITHUB_TOKEN` optional, `TAKUHON_WAKATIME_KEY`\n * required for WakaTime), never from flags or the profile, so they cannot leak\n * into shell history or any stored document.\n *\n * A sync that gathers no data never overwrites a good snapshot: the last-known\n * `activity.json` is kept and the command exits non-zero so a scheduled run\n * surfaces the failure.\n *\n * Exit codes:\n * 0 — snapshot synced and written (individual sources may still have been\n * skipped or failed; those are reported on stderr) / snapshot shown\n * 1 — the sync gathered no data (the last-known snapshot, if any, is kept and\n * nothing is written), or `show` found no stored snapshot\n * 2 — the command could not run: bad arguments, a missing/unreadable/non-JSON\n * profile, or activity is not configured / not enabled in\n * `settings.activity`\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport { fetchActivitySnapshot, isEmptySnapshot, type ActivitySecrets } from '@takuhon/activity';\nimport type { ActivitySettings } from '@takuhon/core';\n\nimport { ACTIVITY_FILENAME, FileActivityStorage } from './file-activity-storage.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\n/** Environment variable carrying the optional GitHub token. */\nconst GITHUB_TOKEN_ENV = 'TAKUHON_GITHUB_TOKEN';\n\n/** Environment variable carrying the WakaTime API key. */\nconst WAKATIME_KEY_ENV = 'TAKUHON_WAKATIME_KEY';\n\nconst SYNC_USAGE = `Usage: takuhon activity sync [path]\n\nFetch the GitHub / WakaTime activity configured under settings.activity in a\ntakuhon.json and store the result as activity.json beside it — the sibling\nsnapshot the renderer reads, kept outside the canonical profile. With no path,\nsyncs ./takuhon.json in the current working directory.\n\nSecrets are read from the environment, never from flags or the profile:\n ${GITHUB_TOKEN_ENV} Optional. Raises the GitHub rate limit and unlocks the\n contribution calendar (which is GraphQL/token-only).\n Languages work without it.\n ${WAKATIME_KEY_ENV} Required to read WakaTime coding time.\n\nA sync that gathers no data keeps the last-known activity.json — a good\nsnapshot is never overwritten with an empty one.\n\nExit codes: 0 = synced, 1 = no data gathered (last-known snapshot kept),\n2 = bad arguments / profile missing / unreadable / not JSON / activity not\nconfigured or not enabled in settings.activity.\n`;\n\nconst SHOW_USAGE = `Usage: takuhon activity show [path]\n\nPrint the stored activity snapshot (the activity.json beside the profile) as\nJSON. With no path, reads beside ./takuhon.json in the current working\ndirectory.\n\nExit codes: 0 = printed, 1 = no (or invalid) snapshot stored, 2 = bad\narguments.\n`;\n\nexport interface ActivityOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies, so tests can stub the network, secrets, and clock. */\nexport interface ActivitySyncDeps {\n /** HTTP client. Defaults to the global `fetch` (Node 22+). */\n fetch?: typeof fetch;\n /** Secret source. Defaults to reading the two environment variables. */\n getSecrets?: () => ActivitySecrets;\n /** Clock for `lastSyncedAt`; injectable for deterministic tests. */\n now?: () => Date;\n}\n\nfunction defaultSecrets(): ActivitySecrets {\n return {\n githubToken: process.env[GITHUB_TOKEN_ENV],\n wakatimeKey: process.env[WAKATIME_KEY_ENV],\n };\n}\n\n/** Parse the single optional positional path; `activity` takes no other options. */\nfunction parsePathArg(\n args: readonly string[],\n sub: 'sync' | 'show',\n): { path: string } | { error: string } {\n let path: string | undefined;\n for (const arg of args) {\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`activity ${sub}\\`.` };\n }\n if (path !== undefined) {\n return { error: `takuhon: \\`activity ${sub}\\` takes at most one path argument.` };\n }\n path = arg;\n }\n return { path: path ?? DEFAULT_PATH };\n}\n\n/** The `activity.json` path as shown to the user, relative to the path they gave. */\nfunction activityDisplayPath(profilePath: string): string {\n return join(dirname(profilePath), ACTIVITY_FILENAME);\n}\n\n/**\n * Read `settings.activity` out of the profile at `path`. The profile is not\n * schema-validated here — `activity sync` only needs the activity config, and\n * an unrelated validation error should not block a metrics refresh — but it\n * must be readable JSON and the config must be present, enabled, and name at\n * least one account to sync.\n */\nfunction readActivityConfig(\n path: string,\n): { config: ActivitySettings } | { outcome: ActivityOutcome } {\n const fail = (code: number, stderr: string): { outcome: ActivityOutcome } => ({\n outcome: { code, stdout: '', stderr },\n });\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return fail(\n 2,\n `takuhon: cannot read '${path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n );\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return fail(2, `takuhon: '${path}' is not valid JSON: ${detail}\\n`);\n }\n\n const settings =\n typeof data === 'object' && data !== null\n ? (data as { settings?: { activity?: ActivitySettings } }).settings\n : undefined;\n const config = settings?.activity;\n\n if (config === undefined) {\n return fail(\n 2,\n `takuhon: '${path}' has no settings.activity; nothing to sync.\\n` +\n `Opt in by adding settings.activity with \"enabled\": true and a github/wakatime username.\\n`,\n );\n }\n if (config.enabled !== true) {\n return fail(\n 2,\n `takuhon: activity is not enabled in '${path}'; nothing to sync.\\n` +\n `Set settings.activity.enabled to true to opt in.\\n`,\n );\n }\n const hasGithub = config.github?.username !== undefined && config.github.username !== '';\n const hasWakatime = config.wakatime?.username !== undefined && config.wakatime.username !== '';\n if (!hasGithub && !hasWakatime) {\n return fail(\n 2,\n `takuhon: settings.activity in '${path}' names no github or wakatime username; nothing to sync.\\n`,\n );\n }\n\n return { config };\n}\n\n/** Remove any literal occurrence of the secrets from a string before it is shown. */\nfunction redactSecrets(text: string, secrets: ActivitySecrets): string {\n let out = text;\n for (const secret of [secrets.githubToken, secrets.wakatimeKey]) {\n if (secret !== undefined && secret !== '') out = out.split(secret).join('***');\n }\n return out;\n}\n\n/**\n * Run `takuhon activity sync` against the arguments that follow the\n * subcommand (i.e. `process.argv.slice(2)` minus the leading\n * `\"activity\", \"sync\"`).\n */\nexport async function runActivitySync(\n args: readonly string[] = [],\n deps: ActivitySyncDeps = {},\n): Promise<ActivityOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: SYNC_USAGE, stderr: '' };\n }\n\n const parsed = parsePathArg(args, 'sync');\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon activity sync --help\\` for usage.\\n`,\n };\n }\n\n const read = readActivityConfig(parsed.path);\n if ('outcome' in read) return read.outcome;\n const { config } = read;\n\n const getSecrets = deps.getSecrets ?? defaultSecrets;\n const secrets = getSecrets();\n\n // A configured source whose secret is absent is skipped silently by the\n // fetcher; surface that here so a scheduled run's log explains the gap.\n const notes: string[] = [];\n const githubUser = config.github?.username;\n if (\n githubUser !== undefined &&\n githubUser !== '' &&\n config.github?.showContributions !== false &&\n !secrets.githubToken\n ) {\n notes.push(\n `takuhon: ${GITHUB_TOKEN_ENV} is not set; skipping GitHub contributions (languages need no token).\\n`,\n );\n }\n const wakatimeUser = config.wakatime?.username;\n if (\n wakatimeUser !== undefined &&\n wakatimeUser !== '' &&\n config.wakatime?.showCodingTime !== false &&\n !secrets.wakatimeKey\n ) {\n notes.push(`takuhon: ${WAKATIME_KEY_ENV} is not set; skipping WakaTime coding time.\\n`);\n }\n\n const snapshot = await fetchActivitySnapshot(config, secrets, {\n fetch: deps.fetch,\n now: deps.now,\n onError: (source, err) => {\n const detail = err instanceof Error ? err.message : String(err);\n notes.push(`takuhon: ${source} failed: ${redactSecrets(detail, secrets)}\\n`);\n },\n });\n\n const storage = new FileActivityStorage(parsed.path);\n const displayPath = activityDisplayPath(parsed.path);\n const stderr = notes.join('');\n\n if (isEmptySnapshot(snapshot)) {\n // Never replace a good snapshot with an empty one: keep the last-known\n // file (if any) and exit non-zero so cron-style callers notice.\n const existing = await storage.getActivitySnapshot();\n const kept =\n existing !== null ? `keeping the last-known snapshot at '${displayPath}'` : 'nothing written';\n return {\n code: 1,\n stdout: '',\n stderr: `${stderr}takuhon: the sync gathered no activity data; ${kept}.\\n`,\n };\n }\n\n await storage.saveActivitySnapshot(snapshot);\n const gathered = (['languages', 'contributions', 'codingTime', 'rank'] as const)\n .filter((field) => snapshot[field] !== undefined)\n .join(', ');\n return { code: 0, stdout: `synced activity -> ${displayPath} (${gathered})\\n`, stderr };\n}\n\n/**\n * Run `takuhon activity show` against the arguments that follow the\n * subcommand.\n */\nexport async function runActivityShow(args: readonly string[] = []): Promise<ActivityOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: SHOW_USAGE, stderr: '' };\n }\n\n const parsed = parsePathArg(args, 'show');\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon activity show --help\\` for usage.\\n`,\n };\n }\n\n const snapshot = await new FileActivityStorage(parsed.path).getActivitySnapshot();\n if (snapshot === null) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: no activity snapshot at '${activityDisplayPath(parsed.path)}'. Run \\`takuhon activity sync\\` first.\\n`,\n };\n }\n return { code: 0, stdout: `${JSON.stringify(snapshot, null, 2)}\\n`, stderr: '' };\n}\n","/**\n * Filesystem-backed {@link ActivityStorage} for the CLI. Persists the synced\n * developer-activity snapshot as an `activity.json` beside the project's\n * `takuhon.json` — the sibling-document layout the design mandates: the\n * snapshot is machine-written, externally sourced, and therefore lives outside\n * the canonical, owner-curated profile.\n *\n * Reads are deliberately forgiving: an absent, unparseable, or malformed file\n * resolves to `null` (never throws), because a missing snapshot is the normal\n * pre-sync / opt-out state and the renderer simply omits the activity section.\n * Writes are last-writer-wins through {@link writeFileAtomic}, so a reader\n * never observes a half-written snapshot.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\n\nimport { isActivitySnapshot, type ActivitySnapshot, type ActivityStorage } from '@takuhon/core';\n\nimport { writeFileAtomic } from './backup.js';\n\n/** Sibling-document filename holding the synced activity snapshot. */\nexport const ACTIVITY_FILENAME = 'activity.json';\n\nfunction isNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'ENOENT';\n}\n\n/** Absolute path of the `activity.json` beside the given profile path. */\nexport function activityPathFor(profilePath: string): string {\n return join(dirname(resolve(profilePath)), ACTIVITY_FILENAME);\n}\n\nfunction readSnapshotFile(path: string): ActivitySnapshot | null {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch (err) {\n if (isNotFound(err)) return null;\n throw err;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // A truncated or corrupt snapshot is treated as absent, not fatal: the\n // next sync rewrites it whole, and the renderer omits the section.\n return null;\n }\n return isActivitySnapshot(parsed) ? parsed : null;\n}\n\n/**\n * Synchronous read of the snapshot beside `profilePath` (or `null`), for the\n * synchronous render pipelines (`takuhon build` / `dev`) that cannot await the\n * {@link ActivityStorage} contract.\n */\nexport function readActivitySnapshotSync(profilePath: string): ActivitySnapshot | null {\n return readSnapshotFile(activityPathFor(profilePath));\n}\n\nexport class FileActivityStorage implements ActivityStorage {\n /** Absolute path of the `activity.json` this storage reads and writes. */\n readonly path: string;\n\n constructor(profilePath: string) {\n this.path = activityPathFor(profilePath);\n }\n\n // The filesystem work is synchronous, but the ActivityStorage contract is\n // async. Each method runs its body inside `Promise.resolve().then(...)` so a\n // synchronous throw becomes a rejected promise rather than an exception the\n // caller must catch outside `await` (same pattern as FileStorage).\n\n getActivitySnapshot(): Promise<ActivitySnapshot | null> {\n return Promise.resolve().then(() => readSnapshotFile(this.path));\n }\n\n saveActivitySnapshot(snapshot: ActivitySnapshot): Promise<void> {\n return Promise.resolve().then(() => {\n writeFileAtomic(this.path, `${JSON.stringify(snapshot, null, 2)}\\n`);\n });\n }\n}\n","/**\n * Local backup helpers shared by `takuhon migrate` and `takuhon restore`.\n *\n * Backups are written to a `.takuhon-backups/` directory co-located with the\n * profile file being mutated, rather than a `~/.takuhon/backups/{slug}/`\n * tree: a local `takuhon.json` operated on by path has no guaranteed slug,\n * and the restore flow documented in operational-lifecycle §4.1 / §4.3 reads\n * and writes `.takuhon-backups/...` relative to the working file. Keeping the\n * backups beside the source also mirrors the Vercel / Static adapter layout\n * (§3.1), which is the closest analogue for a local file workflow.\n *\n * Naming follows §3.1 (`takuhon-backup-v{version}-{timestamp}.json`) and the\n * pre-restore convention of §4.1 (`pre-restore-{timestamp}.json`). Timestamps\n * are ISO 8601 basic format in UTC at second precision; {@link createBackup}\n * implements the §3.3 overwrite guard by falling back to millisecond\n * precision when a same-second name already exists.\n */\n\nimport { mkdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { basename, dirname, join } from 'node:path';\n\n/** Directory (relative to the target file) that holds local backups. */\nexport const BACKUP_DIR_NAME = '.takuhon-backups';\n\n/**\n * Thrown by {@link createBackup} when a backup cannot be written without\n * overwriting an existing file even after disambiguating with milliseconds.\n */\nexport class BackupError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'BackupError';\n }\n}\n\n/**\n * Format `date` as a compact ISO 8601 basic-format UTC timestamp.\n *\n * @example\n * compactTimestamp(new Date('2026-05-11T12:00:00.123Z')) // \"20260511T120000Z\"\n * compactTimestamp(new Date('2026-05-11T12:00:00.123Z'), true) // \"20260511T120000.123Z\"\n */\nexport function compactTimestamp(date: Date, withMillis = false): string {\n const iso = date.toISOString(); // e.g. \"2026-05-11T12:00:00.123Z\"\n const trimmed = withMillis ? iso : iso.replace(/\\.\\d{3}Z$/, 'Z');\n // Drop the extended-format separators; the millisecond `.` (if kept) stays.\n return trimmed.replace(/[-:]/g, '');\n}\n\n/** Build the migration pre-backup filename for a given source `version`. */\nexport function migrateBackupName(version: string, date: Date, withMillis = false): string {\n return `takuhon-backup-v${version}-${compactTimestamp(date, withMillis)}.json`;\n}\n\n/** Build the pre-restore backup filename (operational-lifecycle §4.1). */\nexport function preRestoreName(date: Date, withMillis = false): string {\n return `pre-restore-${compactTimestamp(date, withMillis)}.json`;\n}\n\n/** Build the pre-import backup filename (operational-lifecycle §5.3 step 3). */\nexport function preImportName(date: Date, withMillis = false): string {\n return `pre-import-${compactTimestamp(date, withMillis)}.json`;\n}\n\n/** Build the pre-save backup filename for a `takuhon admin` write. */\nexport function preAdminSaveName(date: Date, withMillis = false): string {\n return `pre-admin-${compactTimestamp(date, withMillis)}.json`;\n}\n\n/** Resolve the `.takuhon-backups/` directory beside `targetPath`. */\nexport function backupDirFor(targetPath: string): string {\n return join(dirname(targetPath), BACKUP_DIR_NAME);\n}\n\n/**\n * Write `content` into `.takuhon-backups/` beside `targetPath`, returning the\n * path actually written.\n *\n * `name(withMillis)` yields the second-precision filename when `false` and a\n * millisecond-precision variant when `true`. The directory is created if\n * absent and writes use the `wx` flag so an existing file is never\n * overwritten (operational-lifecycle §3.3): a same-second collision retries\n * once at millisecond precision, and a further collision throws\n * {@link BackupError}.\n */\nexport function createBackup(params: {\n targetPath: string;\n content: string;\n name: (withMillis: boolean) => string;\n}): string {\n const dir = backupDirFor(params.targetPath);\n mkdirSync(dir, { recursive: true });\n\n const primary = join(dir, params.name(false));\n try {\n writeFileSync(primary, params.content, { flag: 'wx' });\n return primary;\n } catch (error) {\n if (!isAlreadyExists(error)) throw error;\n }\n\n const fallback = join(dir, params.name(true));\n try {\n writeFileSync(fallback, params.content, { flag: 'wx' });\n return fallback;\n } catch (error) {\n if (isAlreadyExists(error)) {\n throw new BackupError(\n `backup target already exists and could not be disambiguated: ${fallback}`,\n { cause: error },\n );\n }\n throw error;\n }\n}\n\nfunction isAlreadyExists(error: unknown): boolean {\n return (\n typeof error === 'object' && error !== null && (error as { code?: unknown }).code === 'EEXIST'\n );\n}\n\n/**\n * Write `content` to `target` atomically: stage it in a sibling temp file and\n * `rename` it into place. A `rename` within a directory is atomic, so a reader\n * never observes a half-written profile and an interrupted write (crash,\n * ENOSPC) cannot truncate the existing file — it is replaced whole or not at\n * all. The temp file is removed on failure. Pairs with the backup-before-write\n * ordering in the migrate / restore commands.\n */\nexport function writeFileAtomic(target: string, content: string): void {\n const tmp = join(dirname(target), `.${basename(target)}.${process.pid}.tmp`);\n try {\n writeFileSync(tmp, content, 'utf8');\n renameSync(tmp, target);\n } catch (error) {\n rmSync(tmp, { force: true });\n throw error;\n }\n}\n","/**\n * `takuhon admin [path] [--port <n>] [--base-url <url>]` — run a local admin\n * server (Spec §14.1 Phase 5 \"local Admin\"): the React admin form SPA at\n * `/admin`, the canonical admin API at `/api/admin/*` backed by the project's\n * `takuhon.json`, and the static HTML preview at `/`, all on a loopback port.\n *\n * It reuses `@takuhon/api`'s {@link createAdminApiApp} (validation, `If-Match`\n * optimistic locking, RFC 7807 errors) over a filesystem {@link FileStorage},\n * and serves the same admin bundle shipped in this package (the one\n * `create-takuhon` copies into scaffolded projects). Editing in the form writes\n * `takuhon.json`; reloading `/` re-renders it, closing the edit → preview loop.\n *\n * Image uploads work locally too: `POST /api/admin/assets` stores into an\n * `assets/` directory beside `takuhon.json` via {@link FileTakuhonAssetStorage},\n * and `GET /assets/*` serves them back (with `nosniff`), so the local pipeline\n * matches the Cloudflare adapter's R2 + delivery proxy.\n *\n * Authentication reuses the Bearer-token path: a fresh random token is minted\n * per run, the server binds `127.0.0.1`, and the token is printed for the\n * operator to paste into the SPA's sign-in form. Nothing is persisted.\n *\n * Like `takuhon dev`, the long-running server splits testable wiring\n * ({@link createAdminApp}, served via `app.fetch`) from the I/O lifecycle\n * ({@link runAdmin}).\n *\n * Exit codes:\n * 0 — served, then stopped cleanly (Ctrl-C), or `--help`\n * 2 — bad arguments, or the port was already in use\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { extname, join, resolve, sep } from 'node:path';\n\nimport { serve } from '@hono/node-server';\nimport {\n adminAssetSecurityHeaders,\n createAdminApiApp,\n noopAuditLogger,\n noopCachePurger,\n} from '@takuhon/api';\nimport { Hono } from 'hono';\n\nimport { handleRequest, loadSiteState } from './dev-command.js';\nimport { FileTakuhonAssetStorage } from './file-asset-storage.js';\nimport { FileStorage } from './file-storage.js';\nimport { resolveAdminBundleDir } from './scaffold/index.js';\n\nconst DEFAULT_PATH = 'takuhon.json';\nconst DEFAULT_PORT = 4322;\n\nconst USAGE = `Usage: takuhon admin [path] [--port <n>] [--base-url <url>]\n\nRun a local admin server: the form editor at /admin, the admin API at\n/api/admin/*, and a preview at /. With no path, edits ./takuhon.json. A fresh\nadmin token is printed each run; paste it into the sign-in form. The server\nbinds 127.0.0.1 only. Stop with Ctrl-C.\n\nOptions:\n --port <n> Port to listen on (default: ${DEFAULT_PORT}).\n --base-url <url> Site origin for the preview's canonical / hreflang links.\n\nExit codes: 0 = served then stopped, 2 = bad arguments / port in use.\n`;\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n '.woff2': 'font/woff2',\n '.map': 'application/json; charset=utf-8',\n};\n\nfunction contentTypeFor(file: string): string {\n return CONTENT_TYPES[extname(file).toLowerCase()] ?? 'application/octet-stream';\n}\n\nfunction isNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'ENOENT';\n}\n\n/**\n * Headers for the admin bundle over loopback http. Mirrors the production\n * `adminAssetSecurityHeaders()` (strict `script-src 'self'` CSP, nosniff, frame\n * DENY, no-store) but drops `strict-transport-security` (meaningless over http)\n * and `upgrade-insecure-requests` (would try to upgrade localhost subresources\n * to https and fail).\n */\nfunction localAdminHeaders(): Record<string, string> {\n const headers: Record<string, string> = { ...adminAssetSecurityHeaders() };\n delete headers['strict-transport-security'];\n const csp = headers['content-security-policy'];\n if (csp !== undefined) {\n headers['content-security-policy'] = csp\n .split(';')\n .map((directive) => directive.trim())\n .filter((directive) => directive !== '' && directive !== 'upgrade-insecure-requests')\n .join('; ');\n }\n return headers;\n}\n\nconst plain = (status: number, body: string): Response =>\n new Response(body, { status, headers: { 'content-type': 'text/plain; charset=utf-8' } });\n\n/** Escape a string for safe inclusion in a double-quoted HTML attribute. */\nfunction escapeHtmlAttr(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\n/**\n * Inject the per-run token into the served `index.html` as a `<meta>` tag so\n * the loopback SPA can auto-authenticate instead of asking the operator to\n * paste the token. This is done at response time on the local server only — the\n * shipped bundle files (and therefore the public Cloudflare deployment, which\n * serves them verbatim) never carry the tag, so the public sign-in gate is\n * unaffected. A `<meta>` is CSP-safe under the strict `script-src 'self'`\n * policy (it is not script), unlike an inline bootstrap `<script>`.\n */\nfunction injectLocalToken(html: string, token: string): string {\n const meta = `<meta name=\"takuhon-local-token\" content=\"${escapeHtmlAttr(token)}\" />`;\n if (html.includes('</head>')) return html.replace('</head>', `${meta}</head>`);\n // No <head> (e.g. a minimal test fixture): the browser still hoists a leading\n // <meta> into the document head during parsing, so querySelector finds it.\n return meta + html;\n}\n\n/**\n * Serve a file from the admin bundle for an `/admin*` request. Mirrors the\n * Cloudflare adapter's `serveAdminSpa`: strip the `/admin` prefix, map `/admin`\n * and `/admin/` to `index.html`, fall back to `index.html` for extension-less\n * paths (SPA routes), and 404 for missing assets. Guards against path traversal.\n * The `index.html` response gets the per-run token injected (see\n * {@link injectLocalToken}); other assets are served byte-for-byte.\n */\nfunction serveAdminBundle(\n bundleDir: string,\n method: string,\n pathname: string,\n token: string,\n): Response {\n if (method !== 'GET' && method !== 'HEAD') return plain(405, 'Method Not Allowed\\n');\n\n const rest = pathname.slice('/admin'.length);\n let rel: string;\n try {\n rel = decodeURIComponent(rest).replace(/^\\/+/, '');\n } catch {\n rel = '';\n }\n if (rel === '') rel = 'index.html';\n\n const root = resolve(bundleDir);\n let full = resolve(root, rel);\n if (full !== root && !full.startsWith(root + sep)) return plain(403, 'Forbidden\\n');\n\n let body: Buffer;\n try {\n body = readFileSync(full);\n } catch (err) {\n if (!isNotFound(err)) throw err;\n // SPA semantics: extension-less paths fall back to index.html; a missing\n // asset (e.g. /admin/assets/x.js) is a genuine 404.\n if (extname(rel) !== '') return plain(404, 'Not Found\\n');\n full = join(root, 'index.html');\n try {\n body = readFileSync(full);\n } catch {\n return plain(404, 'Not Found\\n');\n }\n }\n\n const contentType = contentTypeFor(full);\n const headers = new Headers(localAdminHeaders());\n headers.set('content-type', contentType);\n\n // The SPA entry document carries the per-run token so the loopback UI can\n // skip the sign-in form; every other asset is served unchanged.\n if (contentType.startsWith('text/html')) {\n const injected = injectLocalToken(body.toString('utf8'), token);\n return new Response(method === 'HEAD' ? null : injected, { status: 200, headers });\n }\n\n return new Response(method === 'HEAD' ? null : body, { status: 200, headers });\n}\n\n/**\n * Serve an uploaded asset from the project's `assets/` directory for a\n * `GET`/`HEAD /assets/*` request — the local counterpart of the Cloudflare\n * adapter's R2 delivery proxy. Forces `X-Content-Type-Options: nosniff`\n * (`security.md` §4.7) and a long-lived immutable cache (keys are unique). The\n * traversal guard lives in {@link FileTakuhonAssetStorage.readForServing}.\n */\nfunction serveLocalAsset(\n assetStorage: FileTakuhonAssetStorage,\n method: string,\n pathname: string,\n): Response {\n if (method !== 'GET' && method !== 'HEAD') return plain(405, 'Method Not Allowed\\n');\n let key: string;\n try {\n key = decodeURIComponent(pathname.slice(1)); // drop the leading '/', → 'assets/...'\n } catch {\n return plain(400, 'Bad Request\\n');\n }\n const asset = assetStorage.readForServing(key);\n if (asset === null) return plain(404, 'Not Found\\n');\n const headers = new Headers();\n headers.set('content-type', asset.contentType);\n headers.set('x-content-type-options', 'nosniff');\n headers.set('cache-control', 'public, max-age=31536000, immutable');\n return new Response(method === 'HEAD' ? null : asset.bytes, { status: 200, headers });\n}\n\nexport interface CreateAdminAppOptions {\n /** Path to the project's takuhon.json. */\n readonly path: string;\n /** Bearer token the SPA must present (minted per run by {@link runAdmin}). */\n readonly token: string;\n /** Optional preview origin for canonical / hreflang links. */\n readonly baseUrl?: string;\n /** Admin bundle directory; defaults to the bundle shipped in this package. */\n readonly bundleDir?: string;\n /**\n * Origin used to build absolute asset `publicUrl`s (the loopback origin,\n * passed by {@link runAdmin}). When omitted, `publicUrl` is the relative\n * `/assets/...` path, which the same-origin SPA / preview can still fetch.\n */\n readonly assetBaseUrl?: string;\n}\n\n/**\n * Build the local admin Hono app. Pure (no listening socket) so tests can\n * exercise it through `app.fetch(new Request(...))`.\n */\nexport function createAdminApp(opts: CreateAdminAppOptions): Hono {\n const bundleDir = opts.bundleDir ?? resolveAdminBundleDir();\n const storage = new FileStorage(opts.path);\n // Uploads land in `assets/` beside takuhon.json and are served below at\n // /assets/*, mirroring the Cloudflare adapter's R2 + GET /assets/* pairing.\n const assetStorage = new FileTakuhonAssetStorage(opts.path, { publicBaseUrl: opts.assetBaseUrl });\n const app = new Hono();\n\n app.route(\n '/api/admin',\n createAdminApiApp({\n storage,\n assetStorage,\n getAdminToken: () => opts.token,\n // Loopback, same-origin SPA: no Origin allowlist needed (empty = skip).\n getAdminOrigins: () => [],\n cachePurger: noopCachePurger,\n auditLogger: noopAuditLogger,\n }),\n );\n\n // Unmatched `/api/admin/*` paths stay inside the mounted admin app (its\n // bearer middleware still gates them — an unknown path is a 401, never a\n // fall-through to the public preview), so only non-admin paths reach here.\n app.all('*', (c) => {\n const { method, path } = c.req;\n if (path === '/admin' || path.startsWith('/admin/')) {\n return serveAdminBundle(bundleDir, method, path, opts.token);\n }\n if (path.startsWith('/assets/')) {\n return serveLocalAsset(assetStorage, method, path);\n }\n const state = loadSiteState(opts.path, opts.baseUrl);\n const res = handleRequest(method, path, state);\n return new Response(method === 'HEAD' ? null : res.body, {\n status: res.status,\n headers: { 'content-type': res.contentType },\n });\n });\n\n return app;\n}\n\n/** Injectable sinks + token so tests run without a TTY or randomness. */\nexport interface AdminDeps {\n stdout?: (text: string) => void;\n stderr?: (text: string) => void;\n /** Override the per-run token (tests); defaults to 32 random bytes. */\n token?: string;\n}\n\ninterface ParsedArgs {\n path: string;\n port: number;\n baseUrl?: string;\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let portRaw: string | undefined;\n let baseUrl: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n if (arg === '--port' || arg === '--base-url') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--port') portRaw = value;\n else baseUrl = value;\n i++;\n continue;\n }\n if (arg.startsWith('--port=')) {\n portRaw = arg.slice('--port='.length);\n if (portRaw === '') return { error: 'takuhon: `--port` requires a value.' };\n continue;\n }\n if (arg.startsWith('--base-url=')) {\n baseUrl = arg.slice('--base-url='.length);\n if (baseUrl === '') return { error: 'takuhon: `--base-url` requires a value.' };\n continue;\n }\n if (arg.startsWith('-')) return { error: `takuhon: unknown option \\`${arg}\\` for \\`admin\\`.` };\n if (path !== undefined) return { error: 'takuhon: `admin` takes at most one path argument.' };\n path = arg;\n }\n\n let port = DEFAULT_PORT;\n if (portRaw !== undefined) {\n if (!/^\\d+$/.test(portRaw)) {\n return {\n error: `takuhon: \\`--port\\` must be an integer between 1 and 65535 (got \\`${portRaw}\\`).`,\n };\n }\n const n = Number(portRaw);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n return {\n error: `takuhon: \\`--port\\` must be an integer between 1 and 65535 (got \\`${portRaw}\\`).`,\n };\n }\n port = n;\n }\n\n if (baseUrl !== undefined && !isHttpUrl(baseUrl)) {\n return { error: 'takuhon: `--base-url` must be an absolute http(s) URL.' };\n }\n\n return { path: path ?? DEFAULT_PATH, port, baseUrl: baseUrl?.replace(/\\/+$/, '') };\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\n/**\n * Run `takuhon admin`. Resolves with the process exit code: it stays pending\n * while the server runs and resolves on graceful shutdown (Ctrl-C). The source\n * file may be absent — the form's empty-state import flow handles that — so\n * there is no missing-file fast-fail.\n */\nexport async function runAdmin(\n args: readonly string[] = [],\n deps: AdminDeps = {},\n): Promise<number> {\n const out = deps.stdout ?? ((text: string) => void process.stdout.write(text));\n const err = deps.stderr ?? ((text: string) => void process.stderr.write(text));\n\n if (args[0] === '--help' || args[0] === '-h') {\n out(USAGE);\n return 0;\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n err(`${parsed.error}\\nRun \\`takuhon admin --help\\` for usage.\\n`);\n return 2;\n }\n\n const token = deps.token ?? randomBytes(32).toString('base64url');\n const app = createAdminApp({\n path: parsed.path,\n token,\n baseUrl: parsed.baseUrl,\n assetBaseUrl: `http://127.0.0.1:${String(parsed.port)}`,\n });\n\n return await new Promise<number>((resolvePromise) => {\n let closing = false;\n\n // Signal handlers are registered only once listening succeeds (below), so a\n // bind failure (EADDRINUSE) leaves none attached — mirroring `runDev`.\n function shutdown(): void {\n if (closing) return;\n closing = true;\n process.removeListener('SIGINT', shutdown);\n process.removeListener('SIGTERM', shutdown);\n server.close(() => resolvePromise(0));\n }\n\n const server = serve({ fetch: app.fetch, hostname: '127.0.0.1', port: parsed.port }, () => {\n const origin = `http://127.0.0.1:${parsed.port}`;\n out(`takuhon admin: editing ${parsed.path} at ${origin}/ (Ctrl-C to stop)\\n`);\n out(` Admin UI: ${origin}/admin\\n`);\n out(` Admin token: ${token}\\n`);\n out(` Paste the token into the sign-in form; it is valid for this run only.\\n`);\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n });\n\n server.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EADDRINUSE') {\n err(`takuhon: port ${parsed.port} is already in use; pass --port <n> to choose another.\\n`);\n } else {\n err(`takuhon: ${error.message}\\n`);\n }\n resolvePromise(2);\n });\n });\n}\n","/**\n * `takuhon dev [path] [--port <n>] [--base-url <url>]` — serve a `takuhon.json`\n * as a local static preview (Spec §14.1 Phase 1 \"edit → preview\").\n *\n * This is the in-memory companion of `takuhon build`: it renders the same\n * per-locale surface (via the shared {@link generateSite}) but serves it over\n * `node:http` instead of writing to disk — no bundler, no Hono/Vite/React, no\n * remote/secret coupling. The source is re-read and re-rendered on every\n * request, so editing `takuhon.json` and reloading the browser shows changes\n * with no watch machinery; an invalid file is served as a live error page so it\n * can be fixed in place.\n *\n * A long-running server does not fit the pure `runXxx → {code,stdout,stderr}`\n * shape of the other commands, so the testable logic is split from the I/O:\n * {@link loadSiteState} (source → route map), {@link resolveRoute},\n * {@link contentType}, and {@link handleRequest} are pure; {@link createDevServer}\n * and {@link runDev} are the thin `node:http` wrapper around them.\n *\n * Exit codes:\n * 0 — served, then stopped cleanly (Ctrl-C), or `--help`\n * 2 — the command could not run: bad arguments, a missing/unreadable file,\n * or the port was already in use\n */\n\nimport { readFileSync } from 'node:fs';\nimport { createServer, type Server } from 'node:http';\n\nimport { escapeHtml, generateSite } from '@takuhon/api';\nimport { applyPublicPrivacyFilter, normalize, validate } from '@takuhon/core';\n\nimport { readActivitySnapshotSync } from './file-activity-storage.js';\n\nconst DEFAULT_PATH = 'takuhon.json';\nconst DEFAULT_PORT = 4321;\n\nconst USAGE = `Usage: takuhon dev [path] [--port <n>] [--base-url <url>]\n\nServe a takuhon.json as a local static preview (one page per locale, plus a\nprint-ready CV page at /cv) — the same surface \\`takuhon build\\` produces. With\nno path, serves ./takuhon.json. The file is re-read and re-rendered on every\nrequest, so edit it and reload the browser to see changes. Stop with Ctrl-C.\n\nOptions:\n --port <n> Port to listen on (default: ${DEFAULT_PORT}).\n --base-url <url> Site origin (e.g. https://me.example). Enables absolute\n canonical and hreflang links; without it those are omitted.\n\nThe public privacy filter is applied (meta.privacy is honoured). An invalid\ntakuhon.json is served as an error page so you can fix it and reload.\n\nExit codes: 0 = served then stopped, 2 = bad arguments / file missing /\nunreadable / port in use.\n`;\n\n/** Injectable output sinks so tests can capture streams without a real TTY. */\nexport interface DevDeps {\n stdout?: (text: string) => void;\n stderr?: (text: string) => void;\n}\n\ninterface ParsedArgs {\n path: string;\n port: number;\n baseUrl?: string;\n}\n\n/**\n * The current render state of the source file. Recomputed per request so edits\n * are reflected on reload; a read/parse/validate failure becomes a served error\n * page rather than crashing the server.\n */\nexport type SiteState =\n | { readonly ok: true; readonly pages: ReadonlyMap<string, string> }\n | { readonly ok: false; readonly status: number; readonly message: string };\n\n/** A fully-resolved HTTP response, independent of the `node:http` socket. */\nexport interface DevResponse {\n readonly status: number;\n readonly contentType: string;\n readonly body: string;\n}\n\n/**\n * Read, validate, and render `path` into a route→HTML map. Any failure (missing\n * file, bad JSON, invalid profile) yields a 500 state carrying a human-readable\n * message — validation errors are formatted with their JSON Pointers, mirroring\n * `takuhon build`. The public privacy filter is applied, exactly as `build` does.\n */\nexport function loadSiteState(path: string, baseUrl?: string): SiteState {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return { ok: false, status: 500, message: `cannot read '${path}'.` };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { ok: false, status: 500, message: `'${path}' is not valid JSON: ${detail}` };\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n ok: false,\n status: 500,\n message: `'${path}' is not a valid takuhon profile:\\n${lines.join('\\n')}`,\n };\n }\n\n const filtered = applyPublicPrivacyFilter(normalize(result.data));\n // Re-read the synced activity snapshot alongside the profile on every load,\n // so a fresh `takuhon activity sync` shows up on the next request. Only read\n // when the owner opted in; generateSite re-checks the gate.\n const activitySnapshot =\n filtered.settings.activity?.enabled === true ? readActivitySnapshotSync(path) : null;\n // `dev` always renders the CV page too (at /cv), so it is previewable without\n // a flag; `takuhon build` gates the same output behind `--cv`.\n const pages = new Map(\n generateSite(filtered, { baseUrl, activitySnapshot, cv: true }).map((p) => [p.route, p.html]),\n );\n return { ok: true, pages };\n}\n\n/**\n * Normalize a request path to a site route key. A trailing `index.html` is\n * stripped and a trailing slash is added, so `/ja`, `/ja/`, and\n * `/ja/index.html` all map to `/ja/` (and `/`, ``, `/index.html` to `/`).\n */\nexport function resolveRoute(urlPath: string): string {\n let p = urlPath;\n try {\n p = decodeURIComponent(urlPath);\n } catch {\n // Keep the raw path on malformed percent-encoding; it will simply 404.\n }\n p = p.replace(/\\/index\\.html$/, '/');\n if (p === '' || p === '/') return '/';\n if (!p.startsWith('/')) p = `/${p}`;\n if (!p.endsWith('/')) p = `${p}/`;\n return p;\n}\n\n/**\n * Content-type for a served route. The static surface is HTML-only (the build\n * emits one HTML page per locale and no assets/JSON), so this is constant; it is\n * a named function to keep the response shape ready should the surface grow.\n */\nexport function contentType(_route: string): string {\n return 'text/html; charset=utf-8';\n}\n\n/** Resolve a request to a response against the current {@link SiteState}. Pure. */\nexport function handleRequest(method: string, urlPath: string, state: SiteState): DevResponse {\n if (method !== 'GET' && method !== 'HEAD') {\n return { status: 405, contentType: 'text/plain; charset=utf-8', body: 'Method Not Allowed\\n' };\n }\n if (!state.ok) {\n return {\n status: state.status,\n contentType: contentType('/'),\n body: renderErrorPage(state.message),\n };\n }\n const route = resolveRoute(urlPath);\n const html = state.pages.get(route);\n if (html === undefined) {\n return {\n status: 404,\n contentType: contentType(route),\n body: renderNotFoundPage(route, [...state.pages.keys()]),\n };\n }\n return { status: 200, contentType: contentType(route), body: html };\n}\n\n/**\n * Create the preview server. Each request recomputes the site state (so edits\n * are live on reload) and delegates to the pure {@link handleRequest}. Not\n * started here — the caller binds a port. Importing this module has no side\n * effects.\n */\nexport function createDevServer(opts: { path: string; baseUrl?: string }): Server {\n return createServer((req, res) => {\n const method = req.method ?? 'GET';\n const state = loadSiteState(opts.path, opts.baseUrl);\n const response = handleRequest(method, pathnameOf(req.url ?? '/'), state);\n res.writeHead(response.status, { 'Content-Type': response.contentType });\n if (method === 'HEAD') res.end();\n else res.end(response.body);\n });\n}\n\n/**\n * Run `takuhon dev`. Resolves with the process exit code: it stays pending while\n * the server runs and resolves on graceful shutdown (Ctrl-C). Argument and\n * missing-file errors return before any port is bound.\n */\nexport async function runDev(args: readonly string[] = [], deps: DevDeps = {}): Promise<number> {\n const out = deps.stdout ?? ((text: string) => void process.stdout.write(text));\n const err = deps.stderr ?? ((text: string) => void process.stderr.write(text));\n\n if (args[0] === '--help' || args[0] === '-h') {\n out(USAGE);\n return 0;\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n err(`${parsed.error}\\nRun \\`takuhon dev --help\\` for usage.\\n`);\n return 2;\n }\n\n // Fast-fail only when the source is missing/unreadable — there is nothing to\n // preview. A present-but-invalid file is served as a live error page instead,\n // so it can be fixed without restarting the server.\n try {\n readFileSync(parsed.path, 'utf8');\n } catch {\n err(\n `takuhon: cannot read '${parsed.path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n );\n return 2;\n }\n\n const server = createDevServer({ path: parsed.path, baseUrl: parsed.baseUrl });\n\n return await new Promise<number>((resolve) => {\n let closing = false;\n const shutdown = (): void => {\n if (closing) return;\n closing = true;\n process.removeListener('SIGINT', shutdown);\n process.removeListener('SIGTERM', shutdown);\n server.close(() => resolve(0));\n // Drop keep-alive sockets so close() does not hang (Node >= 18.2).\n server.closeAllConnections();\n };\n\n server.once('error', (error: NodeJS.ErrnoException) => {\n // The signal handlers are only registered once listening starts, so a\n // bind failure (e.g. EADDRINUSE) has none to detach.\n if (error.code === 'EADDRINUSE') {\n err(`takuhon: port ${parsed.port} is already in use; pass --port <n> to choose another.\\n`);\n } else {\n err(`takuhon: ${error.message}\\n`);\n }\n resolve(2);\n });\n\n // Bind to loopback only: a local preview must not expose draft profile\n // content (or the dev error pages) to the rest of the network.\n server.listen(parsed.port, '127.0.0.1', () => {\n out(\n `takuhon dev: serving ${parsed.path} at http://localhost:${parsed.port}/ (Ctrl-C to stop)\\n`,\n );\n // Surface a current validation problem up front; the browser shows it too.\n const state = loadSiteState(parsed.path, parsed.baseUrl);\n if (!state.ok) {\n err(\n `takuhon dev: ${parsed.path} is not a valid profile yet; the preview will show the error until it is fixed.\\n`,\n );\n }\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n });\n });\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let portRaw: string | undefined;\n let baseUrl: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--port' || arg === '--base-url') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--port') portRaw = value;\n else baseUrl = value;\n i++;\n continue;\n }\n if (arg.startsWith('--port=')) {\n const value = arg.slice('--port='.length);\n if (value === '') return { error: 'takuhon: `--port` requires a value.' };\n portRaw = value;\n continue;\n }\n if (arg.startsWith('--base-url=')) {\n const value = arg.slice('--base-url='.length);\n if (value === '') return { error: 'takuhon: `--base-url` requires a value.' };\n baseUrl = value;\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`dev\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `dev` takes at most one path argument.' };\n }\n path = arg;\n }\n\n let port = DEFAULT_PORT;\n if (portRaw !== undefined) {\n const parsedPort = parsePort(portRaw);\n if (parsedPort === undefined) {\n return {\n error: `takuhon: \\`--port\\` must be an integer between 1 and 65535 (got \\`${portRaw}\\`).`,\n };\n }\n port = parsedPort;\n }\n\n if (baseUrl !== undefined && !isHttpUrl(baseUrl)) {\n return { error: 'takuhon: `--base-url` must be an absolute http(s) URL.' };\n }\n\n return {\n path: path ?? DEFAULT_PATH,\n port,\n // Drop any trailing slash so URL joins are predictable.\n baseUrl: baseUrl?.replace(/\\/+$/, ''),\n };\n}\n\nfunction parsePort(value: string): number | undefined {\n if (!/^\\d+$/.test(value)) return undefined;\n const n = Number(value);\n return Number.isInteger(n) && n >= 1 && n <= 65535 ? n : undefined;\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\n/** Extract the path portion of a request URL, dropping any query/fragment. */\nfunction pathnameOf(url: string): string {\n try {\n return new URL(url, 'http://localhost').pathname;\n } catch {\n return url;\n }\n}\n\n/** Minimal HTML document used by the dev-only error and 404 pages. */\nfunction devPage(title: string, body: string): string {\n return (\n `<!DOCTYPE html>\\n<html lang=\"en\">\\n<head>\\n` +\n `<meta charset=\"utf-8\">\\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\\n` +\n `<title>${escapeHtml(title)}</title>\\n` +\n `<style>body{margin:0;font:16px/1.6 system-ui,-apple-system,sans-serif;color:#1a1a1a}` +\n `main{max-width:42rem;margin:2rem auto;padding:0 1.25rem}` +\n `pre{background:#f6f6f6;padding:1rem;border-radius:.4rem;overflow:auto;white-space:pre-wrap}` +\n `code{background:#f2f2f2;padding:.1rem .3rem;border-radius:.2rem}</style>\\n` +\n `</head>\\n<body>\\n<main>\\n${body}\\n</main>\\n</body>\\n</html>\\n`\n );\n}\n\nfunction renderErrorPage(message: string): string {\n return devPage(\n 'takuhon dev — error',\n `<h1>takuhon dev</h1>\\n<p>The profile could not be rendered:</p>\\n` +\n `<pre>${escapeHtml(message)}</pre>\\n<p>Fix the file and reload.</p>`,\n );\n}\n\nfunction renderNotFoundPage(route: string, routes: readonly string[]): string {\n const links = routes\n .map((r) => `<li><a href=\"${escapeHtml(r)}\">${escapeHtml(r)}</a></li>`)\n .join('');\n return devPage(\n 'takuhon dev — 404',\n `<h1>404</h1>\\n<p>No page for <code>${escapeHtml(route)}</code>.</p>\\n` +\n `<p>Available pages:</p>\\n<ul>${links}</ul>`,\n );\n}\n","/**\n * Filesystem-backed {@link TakuhonAssetStorage} for the local admin server\n * (`takuhon admin`). Stores uploaded images in an `assets/` directory beside\n * the project's `takuhon.json`, so a locally-edited profile gets the same asset\n * pipeline the Cloudflare adapter provides over R2.\n *\n * Like the R2 adapter, the bytes arrive already validated and metadata-stripped\n * by `@takuhon/api`'s admin app, so this class only persists them and mints the\n * key / URL. Object keys follow `assets/{timestamp}-{shortHash}.{ext}`\n * (`security.md` §4.6) — identical to {@link R2TakuhonAssetStorage} so a profile\n * edited locally and later deployed keeps working relative `/assets/...` URLs.\n * Dimensions are read back header-only via {@link readImageInfo} (no decode).\n *\n * Delivery is the local server's job (`GET /assets/*`), so {@link readForServing}\n * exposes the bytes + content-type for that route while keeping all path\n * resolution (and the traversal guard) in one place.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport { basename, dirname, extname, join, resolve, sep } from 'node:path';\n\nimport {\n ACCEPTED_IMAGE_MIME_TYPES,\n detectImageMime,\n IMAGE_EXTENSIONS,\n NotFoundError,\n readImageInfo,\n type AcceptedImageMime,\n type AssetOptions,\n type AssetRecord,\n type TakuhonAssetStorage,\n} from '@takuhon/core';\n\n/** Directory (beside `takuhon.json`) holding uploaded assets; the key prefix. */\nconst ASSET_DIRNAME = 'assets';\nconst ASSET_KEY_PREFIX = `${ASSET_DIRNAME}/`;\n\n/** Extension used when the bytes are not one of the accepted image types. */\nconst DEFAULT_EXTENSION = 'bin';\n\n/** Reverse of {@link IMAGE_EXTENSIONS}: file extension → MIME, for delivery. */\nconst MIME_BY_EXTENSION = new Map<string, string>(\n Object.entries(IMAGE_EXTENSIONS).map(([mime, ext]) => [ext, mime]),\n);\n\n/** Narrow an arbitrary content-type string to an accepted image MIME, or null. */\nfunction asAcceptedMime(value: string | undefined): AcceptedImageMime | null {\n if (value === undefined) return null;\n return (ACCEPTED_IMAGE_MIME_TYPES as readonly string[]).includes(value)\n ? (value as AcceptedImageMime)\n : null;\n}\n\n/** Content-type for a stored asset filename, by extension. */\nfunction contentTypeForFile(name: string): string {\n const ext = extname(name).slice(1).toLowerCase();\n return MIME_BY_EXTENSION.get(ext) ?? 'application/octet-stream';\n}\n\n/** Seconds since the Unix epoch, the leading object-key component. */\nfunction timestampSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/** Four hex characters of randomness, defeating key enumeration (§4.7). */\nfunction shortHash(): string {\n return randomBytes(2).toString('hex');\n}\n\nfunction isNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'ENOENT';\n}\n\n/**\n * Write `bytes` to `target` atomically: stage in a sibling temp file and\n * `rename` into place, so a reader (e.g. the SPA fetching the just-returned\n * `publicUrl`) never observes a half-written asset. Binary counterpart of\n * `writeFileAtomic` in `backup.ts`.\n */\nfunction writeFileAtomicBinary(target: string, bytes: Uint8Array): void {\n const tmp = join(dirname(target), `.${basename(target)}.${String(process.pid)}.tmp`);\n try {\n writeFileSync(tmp, bytes);\n renameSync(tmp, target);\n } catch (error) {\n rmSync(tmp, { force: true });\n throw error;\n }\n}\n\nexport class FileTakuhonAssetStorage implements TakuhonAssetStorage {\n private readonly profilePath: string;\n private readonly publicBaseUrl: string;\n\n constructor(profilePath: string, options: { publicBaseUrl?: string } = {}) {\n this.profilePath = profilePath;\n this.publicBaseUrl = options.publicBaseUrl ?? '';\n }\n\n // Genuinely async (awaits the Blob), so no `Promise.resolve().then()` wrap is\n // needed here — unlike the synchronous methods below.\n async putAsset(file: File | Blob, options?: AssetOptions): Promise<AssetRecord> {\n const bytes = new Uint8Array(await file.arrayBuffer());\n // Trust the API-validated content-type when present; otherwise authenticate\n // from the bytes so the adapter is self-sufficient for direct callers.\n const mime = asAcceptedMime(options?.contentType) ?? detectImageMime(bytes);\n const ext = mime !== null ? IMAGE_EXTENSIONS[mime] : DEFAULT_EXTENSION;\n const contentType = mime ?? options?.contentType ?? file.type ?? 'application/octet-stream';\n\n const key = `${ASSET_KEY_PREFIX}${String(timestampSeconds())}-${shortHash()}.${ext}`;\n const full = join(this.projectDir, key);\n mkdirSync(dirname(full), { recursive: true });\n writeFileAtomicBinary(full, bytes);\n\n const info = mime !== null ? readImageInfo(bytes, mime) : null;\n const url = `/${key}`;\n return {\n id: key,\n url,\n publicUrl: this.absoluteUrl(url),\n mimeType: contentType,\n size: bytes.length,\n width: info?.width,\n height: info?.height,\n createdAt: new Date().toISOString(),\n };\n }\n\n getPublicUrl(assetId: string): Promise<string> {\n return Promise.resolve().then(() => {\n const full = this.resolveKey(assetId);\n if (full === null || !existsSync(full)) {\n throw new NotFoundError(`No asset is stored for \"${assetId}\".`);\n }\n return this.absoluteUrl(`/${assetId}`);\n });\n }\n\n deleteAsset(assetId: string): Promise<void> {\n return Promise.resolve().then(() => {\n const full = this.resolveKey(assetId);\n // Idempotent: a missing file (or an out-of-bounds key) is a no-op.\n if (full !== null) rmSync(full, { force: true });\n });\n }\n\n listAssets(): Promise<AssetRecord[]> {\n return Promise.resolve().then(() => {\n const root = join(this.projectDir, ASSET_DIRNAME);\n let names: string[];\n try {\n names = readdirSync(root);\n } catch (err) {\n if (isNotFound(err)) return [];\n throw err;\n }\n return names\n .filter((name) => statSync(join(root, name)).isFile())\n .sort()\n .map((name) => {\n const key = `${ASSET_KEY_PREFIX}${name}`;\n const url = `/${key}`;\n return {\n id: key,\n url,\n publicUrl: this.absoluteUrl(url),\n mimeType: contentTypeForFile(name),\n size: statSync(join(root, name)).size,\n createdAt: statSync(join(root, name)).mtime.toISOString(),\n };\n });\n });\n }\n\n /**\n * Local-server helper (not part of {@link TakuhonAssetStorage}): read an asset\n * for static delivery, or `null` when it is absent or the key escapes the\n * `assets/` directory. The traversal guard lives here so the delivery route\n * and the persistence methods share one notion of \"where assets live\".\n */\n readForServing(key: string): { bytes: Buffer; contentType: string } | null {\n const full = this.resolveKey(key);\n if (full === null) return null;\n let bytes: Buffer;\n try {\n bytes = readFileSync(full);\n } catch (err) {\n if (isNotFound(err)) return null;\n throw err;\n }\n return { bytes, contentType: contentTypeForFile(full) };\n }\n\n private get projectDir(): string {\n return dirname(resolve(this.profilePath));\n }\n\n /** Resolve an `assets/...` key to an absolute path, or null if out of bounds. */\n private resolveKey(key: string): string | null {\n const root = resolve(this.projectDir, ASSET_DIRNAME);\n const full = resolve(this.projectDir, key);\n if (full !== root && !full.startsWith(root + sep)) return null;\n return full;\n }\n\n private absoluteUrl(path: string): string {\n return this.publicBaseUrl !== '' ? `${this.publicBaseUrl}${path}` : path;\n }\n}\n","/**\n * Filesystem-backed {@link TakuhonStorage} for the local admin server\n * (`takuhon admin`). Persists the single profile document as the project's\n * `takuhon.json` so the form editor writes the same file the rest of the CLI\n * (build / dev / export / sync) reads.\n *\n * The opaque `version` is the SHA-256 of the file's bytes. This is stateless\n * (no sidecar metadata) and doubles as external-edit detection: if the file is\n * changed on disk between the editor's load and save, the hashes differ and the\n * `If-Match` precondition fails with a {@link ConflictError} — surfaced to the\n * SPA as a 409 conflict. Writes go through {@link writeFileAtomic} after a\n * backup, mirroring `import` / `migrate` / `restore`.\n */\n\nimport { createHash } from 'node:crypto';\nimport { readFileSync, rmSync } from 'node:fs';\n\nimport { ConflictError, NotFoundError, type Takuhon, type TakuhonStorage } from '@takuhon/core';\n\nimport { createBackup, preAdminSaveName, writeFileAtomic } from './backup.js';\n\nfunction sha256Hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\nfunction isNotFound(err: unknown): boolean {\n return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'ENOENT';\n}\n\n/** Read the file's bytes, or `undefined` when it does not exist. */\nfunction readMaybe(path: string): string | undefined {\n try {\n return readFileSync(path, 'utf8');\n } catch (err) {\n if (isNotFound(err)) return undefined;\n throw err;\n }\n}\n\nexport interface FileStorageDeps {\n /** Clock for backup timestamps; injectable for deterministic tests. */\n now?: () => Date;\n}\n\nexport class FileStorage implements TakuhonStorage {\n private readonly now: () => Date;\n\n constructor(\n private readonly path: string,\n deps: FileStorageDeps = {},\n ) {\n this.now = deps.now ?? (() => new Date());\n }\n\n // The filesystem work is synchronous, but the TakuhonStorage contract is\n // async. Each method runs its body inside `Promise.resolve().then(...)` so a\n // synchronous throw becomes a rejected promise (e.g. NotFoundError →\n // ConflictError mapping in the admin API) rather than an exception the caller\n // must catch outside `await`.\n\n getProfile(): Promise<{ data: Takuhon; version: string }> {\n return Promise.resolve().then(() => {\n const raw = readMaybe(this.path);\n if (raw === undefined) {\n // No path in the message: it is serialized into the admin API response,\n // and the SPA maps 404 to its empty-state import flow regardless.\n throw new NotFoundError('No profile is stored yet.');\n }\n let data: Takuhon;\n try {\n data = JSON.parse(raw) as Takuhon;\n } catch (err) {\n // Surfaced by the admin API as a 500; the form's job is to fix invalid\n // *schema*, but unparseable JSON cannot be loaded into the editor. Keep\n // the host path out of the message (it goes in `cause` for local logs).\n throw new Error('The profile file is not valid JSON.', { cause: err });\n }\n return { data, version: sha256Hex(raw) };\n });\n }\n\n saveProfile(data: Takuhon, ifMatch?: string): Promise<{ version: string }> {\n return Promise.resolve().then(() => {\n const current = readMaybe(this.path);\n\n if (ifMatch !== undefined) {\n const currentVersion = current === undefined ? undefined : sha256Hex(current);\n if (currentVersion !== ifMatch) {\n throw new ConflictError(\n `If-Match preconditioned on version \"${ifMatch}\" but current is \"${currentVersion ?? 'absent'}\".`,\n { currentVersion },\n );\n }\n }\n\n // Back up the file we are about to overwrite (none on first write).\n if (current !== undefined) {\n const stamp = this.now();\n createBackup({\n targetPath: this.path,\n content: current,\n name: (withMillis) => preAdminSaveName(stamp, withMillis),\n });\n }\n\n const content = `${JSON.stringify(data, null, 2)}\\n`;\n writeFileAtomic(this.path, content);\n return { version: sha256Hex(content) };\n });\n }\n\n deleteProfile(): Promise<void> {\n return Promise.resolve().then(() => {\n const current = readMaybe(this.path);\n if (current === undefined) return;\n const stamp = this.now();\n createBackup({\n targetPath: this.path,\n content: current,\n name: (withMillis) => preAdminSaveName(stamp, withMillis),\n });\n rmSync(this.path, { force: true });\n });\n }\n}\n","/**\n * `takuhon build [path] [--output <dir>] [--base-url <url>]` — render a\n * `takuhon.json` into a deployable static site (Spec §13 Static Edition).\n *\n * Mirrors the other command runners: {@link runBuild} is a pure function that\n * reads the source itself and returns its output as strings plus an exit code.\n * The render pipeline reuses `@takuhon/core` only: validate → normalize →\n * `applyPublicPrivacyFilter` (so the static site honours `meta.privacy` exactly\n * like the live API) → for each available locale `resolveLocale` →\n * {@link renderProfileHtml}. The default locale is written to `<dir>/index.html`\n * and every other locale to `<dir>/<locale>/index.html`.\n *\n * Canonical / hreflang links require absolute URLs, so they are emitted only\n * when `--base-url` is supplied; the human locale switcher always uses\n * depth-correct relative links. Asset files are referenced by URL as-is and\n * never copied (out of scope; assets are remote per the schema). Writes are\n * atomic via {@link writeFileAtomic}.\n *\n * Exit codes:\n * 0 — site generated (or `--help`)\n * 1 — the source was read but is not a valid takuhon profile\n * 2 — the command could not run: bad arguments, a missing/unreadable file,\n * a non-JSON file, or a failed write\n */\n\nimport { mkdirSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport { generateSite } from '@takuhon/api';\nimport {\n DARK_PALETTE,\n applyPublicPrivacyFilter,\n normalize,\n renderActivitySvg,\n validate,\n} from '@takuhon/core';\n\nimport { writeFileAtomic } from './backup.js';\nimport { readActivitySnapshotSync } from './file-activity-storage.js';\n\nconst DEFAULT_PATH = 'takuhon.json';\nconst DEFAULT_OUTPUT = 'dist';\n\nconst USAGE = `Usage: takuhon build [path] [--output <dir>] [--base-url <url>] [--cv]\n\nRender a takuhon.json into a static site (one HTML page per locale, with\nbuild-time Schema.org JSON-LD). With no path, builds ./takuhon.json.\n\nOptions:\n --output <dir> Output directory (default: ${DEFAULT_OUTPUT}). The default\n locale is written to <dir>/index.html and each other locale\n to <dir>/<locale>/index.html.\n --base-url <url> Site origin (e.g. https://me.example). Enables absolute\n canonical and hreflang links; without it those are omitted.\n --cv Also emit a print-ready CV/résumé page per locale\n (<dir>/cv.html and <dir>/<locale>/cv.html). Open it and use\n the browser's \"Save as PDF\" to produce a résumé PDF.\n\nThe public privacy filter is applied (meta.privacy is honoured). Asset URLs are\nreferenced as-is and are not copied. The output directory is written into, not\ncleaned — use a dedicated/empty directory so stale pages do not linger.\n\nWhen settings.activity.enabled is true and an activity.json sits beside the\nprofile, the activity card is also written as activity.svg and\nactivity-dark.svg for embedding as a GitHub README badge.\n\nExit codes: 0 = built, 1 = source is not a valid profile,\n2 = bad arguments / file missing / unreadable / not JSON / write failed.\n`;\n\nexport interface BuildOutcome {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\ninterface ParsedArgs {\n path: string;\n output: string;\n baseUrl?: string;\n cv: boolean;\n}\n\n/**\n * Run `takuhon build` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"build\"`).\n */\nexport function runBuild(args: readonly string[] = []): BuildOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon build --help\\` for usage.\\n`,\n };\n }\n\n return buildSite(parsed);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let output: string | undefined;\n let baseUrl: string | undefined;\n let cv = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--cv') {\n cv = true;\n continue;\n }\n if (arg === '--output' || arg === '--base-url') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--output') output = value;\n else baseUrl = value;\n i++;\n continue;\n }\n if (arg.startsWith('--output=')) {\n const value = arg.slice('--output='.length);\n if (value === '') return { error: 'takuhon: `--output` requires a value.' };\n output = value;\n continue;\n }\n if (arg.startsWith('--base-url=')) {\n const value = arg.slice('--base-url='.length);\n if (value === '') return { error: 'takuhon: `--base-url` requires a value.' };\n baseUrl = value;\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`build\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `build` takes at most one path argument.' };\n }\n path = arg;\n }\n\n if (baseUrl !== undefined && !isHttpUrl(baseUrl)) {\n return { error: 'takuhon: `--base-url` must be an absolute http(s) URL.' };\n }\n\n return {\n path: path ?? DEFAULT_PATH,\n output: output ?? DEFAULT_OUTPUT,\n // Drop any trailing slash so URL joins are predictable.\n baseUrl: baseUrl?.replace(/\\/+$/, ''),\n cv,\n };\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\nfunction buildSite(parsed: ParsedArgs): BuildOutcome {\n const { path, output, baseUrl, cv } = parsed;\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: cannot read '${path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${path}' is not valid JSON: ${detail}\\n` };\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${path}' is not a valid takuhon profile; refusing to build:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n const filtered = applyPublicPrivacyFilter(normalize(result.data));\n // The synced developer-activity snapshot (activity.json beside the profile)\n // is read only when the owner opted in; generateSite re-checks the gate.\n const activitySnapshot =\n filtered.settings.activity?.enabled === true ? readActivitySnapshotSync(path) : null;\n\n const written: string[] = [];\n const assets: string[] = [];\n try {\n for (const page of generateSite(filtered, { baseUrl, activitySnapshot, cv })) {\n const outFile = join(output, page.file);\n mkdirSync(dirname(outFile), { recursive: true });\n writeFileAtomic(outFile, page.html);\n written.push(outFile);\n }\n // Activity badges (hosting mode B): when the owner opted in and the synced\n // snapshot carries metrics, emit standalone light + dark SVG cards beside\n // the pages so they can be embedded as a README badge (the dynamic\n // counterpart is the Cloudflare adapter's GET /activity.svg). A metric-less\n // snapshot renders to '' and writes nothing.\n if (activitySnapshot) {\n const variants: readonly (readonly [string, string])[] = [\n ['activity.svg', renderActivitySvg(activitySnapshot)],\n ['activity-dark.svg', renderActivitySvg(activitySnapshot, { palette: DARK_PALETTE })],\n ];\n for (const [file, svg] of variants) {\n if (svg === '') continue;\n const outFile = join(output, file);\n mkdirSync(dirname(outFile), { recursive: true });\n writeFileAtomic(outFile, svg);\n assets.push(outFile);\n }\n }\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: failed to write the site: ${detail}\\n` };\n }\n\n const summary = [...written, ...assets].map((w) => ` ${w}`).join('\\n');\n const assetNote =\n assets.length > 0 ? ` and ${assets.length} asset${assets.length === 1 ? '' : 's'}` : '';\n return {\n code: 0,\n stdout: `built ${written.length} page${written.length === 1 ? '' : 's'}${assetNote} from ${path}:\\n${summary}\\n`,\n stderr: '',\n };\n}\n","/**\n * `takuhon export [path] [--output <file>]` — serialise a `takuhon.json` into\n * its transport form for transfer or archival.\n *\n * Mirrors the other command runners: {@link runExport} is a pure function that\n * reads the source itself but returns its output as strings plus an exit code.\n * It validates the source first (an export should be a valid transport\n * artifact), then delegates to core's `exportTakuhon`, which deep-clones and\n * refreshes `meta.updatedAt` (the round-trip exception in\n * operational-lifecycle §5.1). The source file is only read, never modified.\n *\n * Output defaults to stdout so the command composes in a pipeline; `--output`\n * writes a file atomically instead. Asset embedding (`--embed-assets`) is a\n * remote-storage concern and is deferred — it is rejected with a clear message\n * rather than silently ignored.\n *\n * Exit codes:\n * 0 — exported (to stdout or `--output`)\n * 1 — the source was read but is not a valid takuhon profile\n * 2 — the command could not run: bad arguments, a missing/unreadable file,\n * a file that is not valid JSON, or a failed `--output` write\n */\n\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { exportTakuhon, validate } from '@takuhon/core';\nimport type { Takuhon } from '@takuhon/core';\n\nimport { writeFileAtomic } from './backup.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon export [path] [--output <file>]\n\nSerialise a takuhon.json into its transport form and print it to stdout, or\nwrite it to a file with --output. With no path, exports ./takuhon.json in the\ncurrent working directory.\n\nOptions:\n --output <file> Write the export to <file> instead of stdout (atomic).\n\nExit codes: 0 = exported, 1 = source is not a valid profile,\n2 = bad arguments / file missing / unreadable / not JSON / write failed.\n`;\n\nexport interface ExportOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\ninterface ParsedArgs {\n path: string;\n output?: string;\n}\n\n/**\n * Run `takuhon export` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"export\"`).\n */\nexport function runExport(args: readonly string[] = []): ExportOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon export --help\\` for usage.\\n`,\n };\n }\n\n return exportFile(parsed);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let output: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--embed-assets') {\n return {\n error:\n 'takuhon: --embed-assets is not supported yet; assets are remote and asset embedding is deferred.',\n };\n }\n if (arg === '--output') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: 'takuhon: `--output` requires a value.' };\n }\n output = value;\n i++;\n continue;\n }\n if (arg.startsWith('--output=')) {\n const value = arg.slice('--output='.length);\n if (value === '') {\n return { error: 'takuhon: `--output` requires a value.' };\n }\n output = value;\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`export\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `export` takes at most one path argument.' };\n }\n path = arg;\n }\n\n return { path: path ?? DEFAULT_PATH, output };\n}\n\nfunction exportFile(parsed: ParsedArgs): ExportOutcome {\n const { path, output } = parsed;\n\n // Export produces a separate transport artifact; refuse to write it over the\n // source (which would mutate the source via the refreshed meta.updatedAt).\n if (output !== undefined && resolve(output) === resolve(path)) {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: --output '${output}' is the source file; export writes a separate artifact.\\n` +\n 'Omit --output to print to stdout, or choose a different file.\\n',\n };\n }\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: cannot read '${path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${path}' is not valid JSON: ${detail}\\n` };\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${path}' is not a valid takuhon profile; refusing to export:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n // Export the validated source faithfully — only meta.updatedAt is refreshed\n // (operational-lifecycle §5.1). We pass the parsed document rather than\n // `result.data` so `validate`'s coercion of absent optional arrays into `[]`\n // does not silently rewrite the export.\n const content = `${JSON.stringify(exportTakuhon(data as Takuhon), null, 2)}\\n`;\n\n if (output === undefined) {\n // No --output: the serialised profile is the command's stdout.\n return { code: 0, stdout: content, stderr: '' };\n }\n\n try {\n writeFileAtomic(output, content);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: failed to write '${output}': ${detail}\\n` };\n }\n\n return { code: 0, stdout: `exported ${path} -> ${output}\\n`, stderr: '' };\n}\n","/**\n * `takuhon import <file> [path]` — load a previously exported profile into a\n * local `takuhon.json` (operational-lifecycle §5.3 / §17.2).\n *\n * Like the other command runners this returns its output as strings plus an\n * exit code. The flow follows §5.3: read the input, migrate it forward to the\n * current schema version if it is older (core's `importTakuhon` deliberately\n * does *not* auto-migrate, so the CLI composes `migrateTakuhon` +\n * `importTakuhon`), validate the result, back up the current profile, then\n * write the imported document. Unlike `restore` (a verbatim reproduction),\n * `import` writes the migrated-and-validated document re-serialised in\n * canonical form.\n *\n * Import overwrites the target, but a backup is taken first, so — matching the\n * spec, which mandates a confirmation prompt only for `restore` — there is no\n * interactive prompt. Asset expansion and edge-cache purge (§5.3 steps 5-6)\n * are remote concerns and do not apply to this local-file command.\n *\n * Exit codes:\n * 0 — imported\n * 1 — the input was read but cannot be imported (missing/unusable\n * schemaVersion, no migration path to the current version, or the\n * result failed validation)\n * 2 — the command could not run: bad arguments, a missing/unreadable input\n * or target, a non-JSON input, or a failed write\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport {\n ImportError,\n MigrationError,\n SCHEMA_VERSION,\n importTakuhon,\n migrateTakuhon,\n} from '@takuhon/core';\nimport type { Takuhon } from '@takuhon/core';\n\nimport { BackupError, createBackup, preImportName, writeFileAtomic } from './backup.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon import <file> [path]\n\nLoad a previously exported profile from <file> into a local takuhon.json,\nmigrating it to the current schema version if needed. With no path, writes\n./takuhon.json in the current working directory.\n\nThe current profile (if any) is backed up to\n.takuhon-backups/pre-import-<timestamp>.json before being overwritten.\n\nExit codes: 0 = imported, 1 = input cannot be imported (invalid / unsupported\nversion), 2 = bad arguments / file missing / unreadable / not JSON / write failed.\n`;\n\nexport interface ImportOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies, so tests can pin the backup timestamp. */\nexport interface ImportDeps {\n /** Clock used for the pre-import backup filename. Defaults to `() => new Date()`. */\n now?: () => Date;\n}\n\ninterface ParsedArgs {\n file: string;\n path: string;\n}\n\n/**\n * Run `takuhon import` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"import\"`).\n */\nexport function runImport(args: readonly string[] = [], deps: ImportDeps = {}): ImportOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon import --help\\` for usage.\\n`,\n };\n }\n\n const now = deps.now ?? (() => new Date());\n return importFile(parsed, now);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n const positionals: string[] = [];\n\n for (const arg of args) {\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`import\\`.` };\n }\n positionals.push(arg);\n }\n\n if (positionals.length === 0) {\n return { error: 'takuhon: `import` requires an input <file>.' };\n }\n if (positionals.length > 2) {\n return { error: 'takuhon: `import` takes at most an input <file> and a target path.' };\n }\n\n return { file: positionals[0]!, path: positionals[1] ?? DEFAULT_PATH };\n}\n\nfunction importFile(parsed: ParsedArgs, now: () => Date): ImportOutcome {\n const { file, path } = parsed;\n\n let raw: string;\n try {\n raw = readFileSync(file, 'utf8');\n } catch {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read '${file}'.\\n` };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${file}' is not valid JSON: ${detail}\\n` };\n }\n\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${file}' is not a takuhon profile (expected a JSON object).\\n`,\n };\n }\n\n const source = (data as { schemaVersion?: unknown }).schemaVersion;\n if (typeof source !== 'string' || source.length === 0) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${file}' has no usable schemaVersion; cannot import.\\n`,\n };\n }\n\n // Bring the input up to the current schema version (§5.3 step 2). core's\n // importTakuhon does not auto-migrate, so we migrate first when needed.\n let candidate: unknown = data;\n if (source !== SCHEMA_VERSION) {\n try {\n candidate = migrateTakuhon(data as Takuhon, SCHEMA_VERSION);\n } catch (error) {\n if (error instanceof MigrationError) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: cannot import '${file}': ${error.message}.\\n`,\n };\n }\n throw error;\n }\n }\n\n let imported: Takuhon;\n try {\n imported = importTakuhon(candidate as Takuhon);\n } catch (error) {\n if (error instanceof ImportError) {\n const lines = (error.errors ?? []).map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n const detail = lines.length > 0 ? `:\\n${lines.join('\\n')}` : '.';\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${file}' is not a valid takuhon profile; refusing to import${detail}\\n`,\n };\n }\n throw error;\n }\n\n // Back up the current profile before overwriting it (§5.3 step 3). A missing\n // target is fine — there is nothing to preserve on a fresh import.\n let currentRaw: string | undefined;\n try {\n currentRaw = readFileSync(path, 'utf8');\n } catch (error) {\n if (!isNotFound(error)) {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read current profile '${path}'.\\n` };\n }\n }\n\n let savedPath: string | undefined;\n if (currentRaw !== undefined) {\n // One timestamp for the backup name (createBackup may call the builder\n // twice on a same-second collision).\n const stamp = now();\n try {\n savedPath = createBackup({\n targetPath: path,\n content: currentRaw,\n name: (withMillis) => preImportName(stamp, withMillis),\n });\n } catch (error) {\n const detail = error instanceof BackupError ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: refusing to import into '${path}' — pre-import backup failed: ${detail}\\n`,\n };\n }\n }\n\n try {\n writeFileAtomic(path, `${JSON.stringify(imported, null, 2)}\\n`);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: failed to write '${path}': ${detail}\\n` };\n }\n\n const lines = [`imported ${file} -> ${path} (schemaVersion ${imported.schemaVersion})`];\n if (savedPath !== undefined) {\n lines.push(` previous profile saved to ${savedPath}`);\n }\n return { code: 0, stdout: `${lines.join('\\n')}\\n`, stderr: '' };\n}\n\nfunction isNotFound(error: unknown): boolean {\n return (\n typeof error === 'object' && error !== null && (error as { code?: unknown }).code === 'ENOENT'\n );\n}\n","/**\n * `takuhon mcp [path]` — serve a local takuhon.json over the Model Context\n * Protocol on stdio, so an MCP client (Claude Desktop, …) can read the profile.\n *\n * It connects the transport-agnostic server from `@takuhon/mcp` to a\n * `StdioServerTransport`. The profile is exposed read-only — the same surface as\n * the public HTTP API (`get_profile` / `get_section` / `get_jsonld` /\n * `list_locales` tools; `takuhon://profile` / `takuhon://schema` resources),\n * with the public privacy filter applied. The file is re-read and re-validated\n * on every request, so edits are reflected without restarting.\n *\n * stdio note: stdin/stdout carry the MCP JSON-RPC stream, so this command writes\n * NOTHING to stdout — all diagnostics go to stderr.\n *\n * Like `dev` / `admin`, this is a long-lived process: {@link runMcp} resolves\n * with the exit code only when the client disconnects (stdin EOF) or on a\n * signal.\n *\n * Exit codes:\n * 0 — served, then the client disconnected / Ctrl-C; or `--help`\n * 2 — could not start: bad arguments, or the file is missing / unreadable /\n * not a valid takuhon profile\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { validate, type Takuhon } from '@takuhon/core';\nimport { createTakuhonMcpServer } from '@takuhon/mcp';\n\nconst DEFAULT_PATH = 'takuhon.json';\nconst SERVER_NAME = 'takuhon';\n\nconst USAGE = `Usage: takuhon mcp [path]\n\nServe a takuhon.json over the Model Context Protocol on stdio, so an MCP client\n(e.g. Claude Desktop) can read the profile read-only. With no path, serves\n./takuhon.json. The file is re-read on every request, so edits are reflected\nwithout restarting.\n\nExposes (read-only, the same surface as the public API, privacy filter applied):\n tools: get_profile, get_section, get_jsonld, list_locales\n resources: takuhon://profile, takuhon://schema\n\nstdin/stdout carry the protocol stream; diagnostics go to stderr. The server\nruns until the client disconnects or you press Ctrl-C.\n\nExample Claude Desktop config (claude_desktop_config.json):\n {\n \"mcpServers\": {\n \"my-profile\": { \"command\": \"takuhon\", \"args\": [\"mcp\", \"/path/to/takuhon.json\"] }\n }\n }\n\nExit codes: 0 = served then stopped, 2 = bad arguments / file missing /\nunreadable / invalid.\n`;\n\n/** Injectable seams so tests can capture output and supply a transport. */\nexport interface McpDeps {\n /**\n * Sink for `--help` only. The MCP session never writes to stdout (that is the\n * protocol channel); this exists solely so the help path is testable.\n */\n stdout?: (text: string) => void;\n stderr?: (text: string) => void;\n /** Transport factory; defaults to stdio. Tests inject an in-memory transport. */\n createTransport?: () => Transport;\n}\n\n/** The CLI version, advertised to MCP clients as the server version. */\nconst VERSION = readCliVersion();\n\ninterface ParsedArgs {\n path: string;\n}\n\n/**\n * Read, parse, and validate a takuhon.json. Throws an `Error` with a\n * human-readable message (validation errors carry their JSON Pointers) on any\n * failure — the caller turns it into a startup error or a tool error result.\n */\nexport function loadMcpProfile(path: string): Takuhon {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n throw new Error(`cannot read '${path}'.`);\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n throw new Error(`'${path}' is not valid JSON: ${detail}`, { cause: error });\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n throw new Error(`'${path}' is not a valid takuhon profile:\\n${lines.join('\\n')}`);\n }\n return result.data;\n}\n\n/**\n * Run `takuhon mcp`. Resolves with the process exit code: it stays pending while\n * the server runs and resolves on client disconnect (stdin EOF) or a signal.\n * Argument and load errors return before any transport is connected.\n */\nexport async function runMcp(args: readonly string[] = [], deps: McpDeps = {}): Promise<number> {\n const err = deps.stderr ?? ((text: string) => void process.stderr.write(text));\n\n if (args[0] === '--help' || args[0] === '-h') {\n // --help is the one thing that goes to stdout: it is an ordinary CLI help\n // request, not an active MCP session, so stdout is safe here.\n const out = deps.stdout ?? ((text: string) => void process.stdout.write(text));\n out(USAGE);\n return 0;\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n err(`${parsed.error}\\nRun \\`takuhon mcp --help\\` for usage.\\n`);\n return 2;\n }\n\n // Fast-fail with a clear message if the profile cannot be loaded now: an MCP\n // client launching a broken server would otherwise see only opaque tool\n // errors. A file that later becomes invalid is handled per-request below.\n try {\n loadMcpProfile(parsed.path);\n } catch (error) {\n err(`takuhon: ${error instanceof Error ? error.message : String(error)}\\n`);\n return 2;\n }\n\n const server = createTakuhonMcpServer({\n // Re-read per request so edits are reflected; if the file later becomes\n // invalid, the load failure surfaces to the client as a tool error result.\n loadProfile: () => loadMcpProfile(parsed.path),\n name: SERVER_NAME,\n version: VERSION,\n });\n\n const transport = (deps.createTransport ?? (() => new StdioServerTransport()))();\n await server.connect(transport);\n err(`takuhon mcp: serving ${parsed.path} (read-only) on stdio\\n`);\n\n return await new Promise<number>((resolve) => {\n let done = false;\n function finish(): void {\n if (done) return;\n done = true;\n process.removeListener('SIGINT', shutdown);\n process.removeListener('SIGTERM', shutdown);\n resolve(0);\n }\n function shutdown(): void {\n void server.close();\n finish();\n }\n // The client disconnecting closes the transport (stdin EOF), which the SDK\n // surfaces as the server's `onclose`; that ends the run.\n server.server.onclose = finish;\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n });\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n for (const arg of args) {\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`mcp\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `mcp` takes at most one path argument.' };\n }\n path = arg;\n }\n return { path: path ?? DEFAULT_PATH };\n}\n\n/** Read this CLI's version from its package.json (same source as `--version`). */\nfunction readCliVersion(): string {\n try {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n };\n return pkg.version;\n } catch {\n return '0.0.0';\n }\n}\n","/**\n * `takuhon migrate [path] [--to <version>] [--out <file>] [--dry-run]` —\n * forward-migrate a `takuhon.json` to a newer schema version.\n *\n * Mirrors `validate-command.ts`: {@link runMigrate} is a pure,\n * side-effect-light function that reads/writes files itself but returns its\n * output as strings plus an exit code, leaving stdout/stderr writes and\n * `process.exit` to the entry layer. The source version is read from the\n * file's own `schemaVersion` (the file is the source of truth), so the\n * command takes only `--to`; the default target is the latest schema this\n * build of `@takuhon/core` ships ({@link SCHEMA_VERSION}).\n *\n * The transform is intentionally minimal — `migrateTakuhon` then a re-`validate`\n * gate, with no `meta.updatedAt` touch and no `normalize` pass — so a\n * migration stays lossless (operational-lifecycle §2.6) and its diff is\n * limited to the schema-shape change. Before an in-place write the original\n * bytes are backed up beside the file (§3.1 / §3.3). Migrations are\n * forward-only (§2.4); downgrades surface as a clear error pointing at\n * `takuhon restore`.\n *\n * Exit codes:\n * 0 — migrated, already at the target (no-op), `--dry-run`, or `--help`\n * 1 — the document was read but cannot be migrated to the target (no\n * forward path / downgrade / unusable schemaVersion) or the migrated\n * result unexpectedly failed re-validation\n * 2 — the command could not run: bad arguments, an out-of-window `--to`, a\n * missing/unreadable file, or a file that is not valid JSON\n */\n\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport {\n MigrationError,\n SCHEMA_VERSION,\n SUPPORTED_SCHEMA_VERSIONS,\n migrateTakuhon,\n validate,\n} from '@takuhon/core';\nimport type { Takuhon } from '@takuhon/core';\n\nimport {\n BackupError,\n backupDirFor,\n createBackup,\n migrateBackupName,\n writeFileAtomic,\n} from './backup.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon migrate [path] [--to <version>] [--out <file>] [--dry-run]\n\nForward-migrate a takuhon.json to a newer schema version. The source version\nis read from the file's own schemaVersion. With no path, migrates\n./takuhon.json in the current working directory.\n\nOptions:\n --to <version> Target schema version (default: ${SCHEMA_VERSION}).\n One of: ${SUPPORTED_SCHEMA_VERSIONS.join(', ')}.\n --out <file> Write the result to <file> instead of in place. The source\n file is left unchanged and no backup is created.\n --dry-run Report the planned migration and backup path; write nothing.\n\nBefore an in-place write the current file is backed up to .takuhon-backups/\nbeside it. Migrations are forward-only; to move to an older schema, restore\nfrom a backup with \\`takuhon restore\\`.\n\nExit codes: 0 = migrated / already current / dry-run, 1 = cannot migrate,\n2 = bad arguments / file missing / unreadable / not JSON.\n`;\n\nexport interface MigrateOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies, so tests can pin the backup timestamp. */\nexport interface MigrateDeps {\n /** Clock used for backup filenames. Defaults to `() => new Date()`. */\n now?: () => Date;\n}\n\ninterface ParsedArgs {\n path: string;\n to: string;\n out?: string;\n dryRun: boolean;\n}\n\n/**\n * Run `takuhon migrate` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"migrate\"`). Never throws\n * and never writes to the process streams.\n */\nexport function runMigrate(args: readonly string[] = [], deps: MigrateDeps = {}): MigrateOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon migrate --help\\` for usage.\\n`,\n };\n }\n\n const now = deps.now ?? (() => new Date());\n return migrateFile(parsed, now);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let to: string | undefined;\n let out: string | undefined;\n let dryRun = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--dry-run') {\n dryRun = true;\n continue;\n }\n if (arg === '--to' || arg === '--out') {\n const value = args[i + 1];\n if (value === undefined || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--to') to = value;\n else out = value;\n i++;\n continue;\n }\n if (arg.startsWith('--to=')) {\n to = arg.slice('--to='.length);\n continue;\n }\n if (arg.startsWith('--out=')) {\n out = arg.slice('--out='.length);\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`migrate\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `migrate` takes at most one path argument.' };\n }\n path = arg;\n }\n\n const target = to ?? SCHEMA_VERSION;\n if (!(SUPPORTED_SCHEMA_VERSIONS as readonly string[]).includes(target)) {\n return {\n error: `takuhon: unsupported --to version \"${target}\". Supported: ${SUPPORTED_SCHEMA_VERSIONS.join(', ')}.`,\n };\n }\n\n return { path: path ?? DEFAULT_PATH, to: target, out, dryRun };\n}\n\nfunction migrateFile(parsed: ParsedArgs, now: () => Date): MigrateOutcome {\n const { path, to: target, out, dryRun } = parsed;\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: cannot read '${path}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${path}' is not valid JSON: ${detail}\\n` };\n }\n\n const source = (data as { schemaVersion?: unknown }).schemaVersion;\n if (typeof source !== 'string' || source.length === 0) {\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${path}' has no usable schemaVersion; cannot determine what to migrate from.\\n`,\n };\n }\n\n if (source === target) {\n return {\n code: 0,\n stdout: `${path}: already at schemaVersion ${target}; nothing to do.\\n`,\n stderr: '',\n };\n }\n\n // Back up the original bytes only for a genuine in-place write. `--out` to a\n // different name leaves the source untouched, so a backup would be redundant.\n // The write is atomic (write-temp-then-rename), which replaces the target\n // *name* rather than following it, so even an `--out` symlink that points at\n // the source cannot clobber the source unbacked — it just replaces the link.\n const writeTarget = out ?? path;\n const inPlace = resolve(writeTarget) === resolve(path);\n\n let migrated: Takuhon;\n try {\n migrated = migrateTakuhon(data as Takuhon, target);\n } catch (error) {\n if (error instanceof MigrationError) {\n return {\n code: 1,\n stdout: '',\n stderr:\n `takuhon: cannot migrate '${path}': ${error.message}.\\n` +\n 'Migrations are forward-only; to move to an older schema, restore from a backup with `takuhon restore`.\\n',\n };\n }\n throw error;\n }\n\n // Re-validate as a structural safety gate. The bundled validator accepts the\n // entire supported window, so a non-latest `--to` still passes here;\n // `migrateTakuhon` is what guarantees the result carries the requested target\n // shape (it stops the chain at `target`). This catches only a migration that\n // produced something the schema rejects outright.\n const revalidated = validate(migrated);\n if (!revalidated.ok) {\n const lines = revalidated.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: migrated '${path}' to ${target} but the result failed validation:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n // One timestamp per run, shared by the dry-run preview and the real backup so\n // the reported and actual backup paths cannot drift.\n const stamp = now();\n\n // `--dry-run` previews only after the migration is proven feasible above, so\n // an impossible migration (e.g. a downgrade) still reports exit 1 rather than\n // a misleading \"would migrate\".\n if (dryRun) {\n const lines = [`${path}: would migrate ${source} -> ${target}`, ` write: ${writeTarget}`];\n if (inPlace) {\n lines.push(` backup: ${backupDirFor(path)}/${migrateBackupName(source, stamp)}`);\n } else {\n lines.push(' (source left unchanged; no backup created)');\n }\n return { code: 0, stdout: `${lines.join('\\n')}\\n`, stderr: '' };\n }\n\n let backupPath: string | undefined;\n if (inPlace) {\n try {\n backupPath = createBackup({\n targetPath: path,\n content: raw,\n name: (withMillis) => migrateBackupName(source, stamp, withMillis),\n });\n } catch (error) {\n const detail = error instanceof BackupError ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: refusing to migrate '${path}' — backup failed: ${detail}\\n`,\n };\n }\n }\n\n try {\n writeFileAtomic(writeTarget, `${JSON.stringify(migrated, null, 2)}\\n`);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: failed to write '${writeTarget}': ${detail}\\n`,\n };\n }\n\n const lines = [`migrated ${path}: ${source} -> ${target}`];\n if (inPlace) {\n lines.push(` backup: ${backupPath}`);\n } else {\n lines.push(` wrote: ${writeTarget} (source left unchanged; no backup created)`);\n }\n return { code: 0, stdout: `${lines.join('\\n')}\\n`, stderr: '' };\n}\n","/**\n * `takuhon admin update [path]` — refresh a scaffolded project's `admin-dist/`\n * with the admin form-UI bundle shipped in this `@takuhon/cli`.\n *\n * `create-takuhon` copies the admin SPA into a new project's `admin-dist/` at\n * scaffold time, where it is then frozen (see `scaffold/index.ts`). When the\n * CLI is upgraded to a release that carries a newer admin UI, this command\n * replaces the project's `admin-dist/` with that newer bundle, so the form UI\n * can be updated without re-scaffolding (which would clobber the rest of the\n * project). It only refreshes an existing `admin-dist/`; creating one is\n * `create-takuhon`'s job.\n *\n * Argument handling is a pure function returning streams plus an exit code,\n * like the other one-shot commands; `index.ts` writes the streams and exits.\n *\n * Exit codes:\n * 0 — refreshed (or `--help`)\n * 2 — bad arguments, the path is not a takuhon project, the project has no\n * `admin-dist/` to refresh, or the bundle copy failed (operational\n * errors)\n */\n\nimport { readFileSync } from 'node:fs';\nimport { cp, mkdtemp, readdir, rename, rm, stat } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nimport { resolveAdminBundleDir } from './scaffold/index.js';\nimport { ADMIN_DIST_DIRNAME } from './scaffold/wrangler-toml.js';\n\n/** Marker file that identifies a directory as a takuhon project. */\nconst PROFILE_FILENAME = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon admin update [path]\n\nRefresh a project's ${ADMIN_DIST_DIRNAME}/ with the admin form-UI bundle shipped in\nthis @takuhon/cli. Use it after upgrading @takuhon/cli to pick up a newer admin\nUI without re-scaffolding. With no path, refreshes the project in the current\ndirectory. The project must already have an ${ADMIN_DIST_DIRNAME}/ (created by\ncreate-takuhon); this command updates it, it does not create one.\n\nExit codes: 0 = refreshed, 2 = bad arguments / not a takuhon project /\nno ${ADMIN_DIST_DIRNAME}/ to refresh / copy failed.\n`;\n\nexport interface RefreshAdminOptions {\n /**\n * Source bundle directory. Defaults to the bundle shipped in this package\n * ({@link resolveAdminBundleDir}); overridable for tests.\n */\n readonly bundleDir?: string;\n}\n\nexport interface RefreshAdminOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/**\n * Run `takuhon admin update` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"admin\" \"update\"`).\n *\n * Handles `--help` / `-h`, rejects unknown flags and extra positionals, then\n * replaces the project's `admin-dist/` with the shipped bundle. Never throws\n * and never writes to the process streams — the caller renders the returned\n * `stdout` / `stderr` and exits with `code`.\n */\nexport async function runRefreshAdmin(\n args: readonly string[] = [],\n opts: RefreshAdminOptions = {},\n): Promise<RefreshAdminOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const unknownFlag = args.find((arg) => arg.startsWith('-'));\n if (unknownFlag !== undefined) {\n return usageError(`unknown option '${unknownFlag}'.`);\n }\n\n if (args.length > 1) {\n return usageError('`admin update` takes at most one path argument.');\n }\n\n const displayDir = args[0] ?? '.';\n const projectDir = resolve(args[0] ?? '.');\n\n // Guard 1: a takuhon.json marks this as a takuhon project, so we never\n // clobber an unrelated admin-dist/ in some other kind of directory.\n try {\n await stat(join(projectDir, PROFILE_FILENAME));\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: '${displayDir}' is not a takuhon project (no ${PROFILE_FILENAME} found).\\n` +\n `Run \\`takuhon admin update\\` from a project created by create-takuhon.\\n`,\n };\n }\n\n // Guard 2: refresh updates an existing bundle; it does not install one.\n const adminDist = join(projectDir, ADMIN_DIST_DIRNAME);\n const adminDistStat = await stat(adminDist).catch(() => undefined);\n if (adminDistStat === undefined) {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: '${displayDir}' has no ${ADMIN_DIST_DIRNAME}/ directory to refresh.\\n` +\n `This project was not scaffolded with the admin form UI; create a new project ` +\n `with create-takuhon to add it.\\n`,\n };\n }\n if (!adminDistStat.isDirectory()) {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: '${join(displayDir, ADMIN_DIST_DIRNAME)}' exists but is not a directory.\\n`,\n };\n }\n\n const bundleDir = opts.bundleDir ?? resolveAdminBundleDir();\n\n let fileCount: number;\n let staged: string | undefined;\n try {\n fileCount = (await readdir(bundleDir, { recursive: true, withFileTypes: true })).filter(\n (entry) => entry.isFile(),\n ).length;\n // Stage the new bundle in a temp directory alongside admin-dist/, then swap\n // it in with a rename. A mid-copy failure leaves the staging dir (cleaned up\n // below), never a half-written admin-dist/, and the rename also drops any\n // stale files the previous bundle had that the new one does not.\n staged = await mkdtemp(join(projectDir, `.${ADMIN_DIST_DIRNAME}-`));\n await cp(bundleDir, staged, { recursive: true });\n await rm(adminDist, { recursive: true, force: true });\n await rename(staged, adminDist);\n staged = undefined;\n } catch (err) {\n if (staged !== undefined) {\n await rm(staged, { recursive: true, force: true }).catch(() => undefined);\n }\n // The bundle ships inside @takuhon/cli, so a copy failure usually means a\n // broken or incomplete install. Surface a sanitized message — the raw error\n // would embed the absolute node_modules path of this package.\n const code = isNodeErrnoException(err) ? ` (${err.code})` : '';\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: failed to refresh ${ADMIN_DIST_DIRNAME}/ from @takuhon/cli${code}. ` +\n `Reinstall or upgrade @takuhon/cli and try again.\\n`,\n };\n }\n\n const plural = fileCount === 1 ? '' : 's';\n return {\n code: 0,\n stdout: `Refreshed ${ADMIN_DIST_DIRNAME}/ from @takuhon/cli@${readVersion()} (${fileCount} file${plural}).\\n`,\n stderr: '',\n };\n}\n\nfunction usageError(message: string): RefreshAdminOutcome {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: ${message}\\nRun \\`takuhon admin update --help\\` for usage.\\n`,\n };\n}\n\n/**\n * The reported version is sourced from package.json at runtime (relative to\n * this module) so it can never drift from the bundle actually shipped in this\n * `@takuhon/cli`.\n */\nfunction readVersion(): string {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n };\n return pkg.version;\n}\n\nfunction isNodeErrnoException(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && typeof (err as NodeJS.ErrnoException).code === 'string';\n}\n","/**\n * `takuhon restore --from <backup> [path] [--yes]` — overwrite a profile with\n * a previously saved backup (operational-lifecycle §4.1 / §4.3).\n *\n * Like the other command runners this returns its output as strings plus an\n * exit code rather than touching the process streams. It is `async` because\n * the confirmation prompt is interactive: the entry layer injects a `confirm`\n * callback that reads from the terminal, while tests inject a deterministic\n * predicate (or pass `--yes` to skip the prompt entirely).\n *\n * Restore is destructive, so the flow is deliberately careful:\n * 1. read and schema-`validate` the backup — never restore an invalid file;\n * 2. confirm (unless `--yes`); a non-interactive run with neither `--yes`\n * nor an injected `confirm` refuses rather than overwriting silently;\n * 3. back up the current profile to `.takuhon-backups/pre-restore-…json`;\n * 4. write the backup's bytes to the target verbatim (a faithful\n * reproduction; `validate` is only the gate, not a rewrite).\n *\n * Edge-cache purge (§4.1 step 4) is a remote-adapter concern and does not\n * apply to this local-file command.\n *\n * Exit codes:\n * 0 — restored, aborted at the prompt, or `--help`\n * 1 — the backup was read but failed schema validation\n * 2 — the command could not run: bad arguments, a missing `--from`, an\n * unreadable backup or current profile, a non-JSON backup, or a refusal\n * to overwrite without confirmation\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { validate } from '@takuhon/core';\nimport type { Takuhon } from '@takuhon/core';\n\nimport {\n BackupError,\n backupDirFor,\n createBackup,\n preRestoreName,\n writeFileAtomic,\n} from './backup.js';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon restore --from <backup> [path] [--yes]\n\nOverwrite a profile with a previously saved backup. With no path, restores\n./takuhon.json in the current working directory.\n\nOptions:\n --from <backup> Backup file to restore from (required).\n --yes, -y Skip the confirmation prompt.\n\nThe backup is schema-validated first, and the current profile is saved to\n.takuhon-backups/pre-restore-<timestamp>.json before being overwritten.\n\nExit codes: 0 = restored / aborted, 1 = backup failed validation,\n2 = bad arguments / file missing / unreadable / not JSON / unconfirmed.\n`;\n\nexport interface RestoreOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies for deterministic tests and interactive prompting. */\nexport interface RestoreDeps {\n /** Clock used for the pre-restore backup filename. Defaults to `() => new Date()`. */\n now?: () => Date;\n /**\n * Confirmation prompt. Receives the rendered message and resolves to the\n * user's decision. Omitted in non-interactive contexts, where restore then\n * refuses unless `--yes` was passed.\n */\n confirm?: (message: string) => boolean | Promise<boolean>;\n}\n\ninterface ParsedArgs {\n from: string;\n path: string;\n yes: boolean;\n}\n\n/**\n * Run `takuhon restore` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"restore\"`).\n */\nexport async function runRestore(\n args: readonly string[] = [],\n deps: RestoreDeps = {},\n): Promise<RestoreOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon restore --help\\` for usage.\\n`,\n };\n }\n\n const now = deps.now ?? (() => new Date());\n return restoreFile(parsed, now, deps.confirm);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let from: string | undefined;\n let path: string | undefined;\n let yes = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--yes' || arg === '-y') {\n yes = true;\n continue;\n }\n if (arg === '--from') {\n const value = args[i + 1];\n if (value === undefined || value.startsWith('-')) {\n return { error: 'takuhon: `--from` requires a value.' };\n }\n from = value;\n i++;\n continue;\n }\n if (arg.startsWith('--from=')) {\n from = arg.slice('--from='.length);\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`restore\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `restore` takes at most one path argument.' };\n }\n path = arg;\n }\n\n if (from === undefined || from.length === 0) {\n return { error: 'takuhon: `restore` requires `--from <backup>`.' };\n }\n\n return { from, path: path ?? DEFAULT_PATH, yes };\n}\n\nasync function restoreFile(\n parsed: ParsedArgs,\n now: () => Date,\n confirm: RestoreDeps['confirm'],\n): Promise<RestoreOutcome> {\n const { from, path, yes } = parsed;\n\n let backupRaw: string;\n try {\n backupRaw = readFileSync(from, 'utf8');\n } catch {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read backup '${from}'.\\n` };\n }\n\n let backupData: unknown;\n try {\n backupData = JSON.parse(backupRaw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: backup '${from}' is not valid JSON: ${detail}\\n`,\n };\n }\n\n const result = validate(backupData);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: backup '${from}' is not a valid takuhon profile; refusing to restore:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n // Read the current profile up front: its existence shapes the confirmation\n // message and decides whether a pre-restore backup is needed.\n let currentRaw: string | undefined;\n try {\n currentRaw = readFileSync(path, 'utf8');\n } catch (error) {\n if (!isNotFound(error)) {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read current profile '${path}'.\\n` };\n }\n }\n\n // One timestamp per run, shared by the prompt preview and the actual\n // pre-restore backup so the reported and written paths cannot drift.\n const stamp = now();\n const preRestorePath = `${backupDirFor(path)}/${preRestoreName(stamp)}`;\n\n if (!yes) {\n if (!confirm) {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: refusing to overwrite '${path}' without confirmation.\\n` +\n 'Re-run interactively, or pass `--yes` to skip the prompt.\\n',\n };\n }\n const decided = await confirm(\n confirmationMessage(path, from, result.data, currentRaw, preRestorePath),\n );\n if (!decided) {\n return { code: 0, stdout: 'Aborted; no changes made.\\n', stderr: '' };\n }\n }\n\n let savedPath: string | undefined;\n if (currentRaw !== undefined) {\n try {\n savedPath = createBackup({\n targetPath: path,\n content: currentRaw,\n name: (withMillis) => preRestoreName(stamp, withMillis),\n });\n } catch (error) {\n const detail = error instanceof BackupError ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: refusing to restore '${path}' — pre-restore backup failed: ${detail}\\n`,\n };\n }\n }\n\n // Write the backup's bytes verbatim: restore is a faithful reproduction of\n // the saved state, and `validate` above was only the gate.\n try {\n writeFileAtomic(path, backupRaw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: failed to write '${path}': ${detail}\\n` };\n }\n\n const lines = [`restored ${path} from ${from}`];\n if (savedPath !== undefined) {\n lines.push(` previous profile saved to ${savedPath}`);\n }\n return { code: 0, stdout: `${lines.join('\\n')}\\n`, stderr: '' };\n}\n\nfunction confirmationMessage(\n path: string,\n from: string,\n data: Takuhon,\n currentRaw: string | undefined,\n preRestorePath: string,\n): string {\n const when = typeof data.meta.updatedAt === 'string' ? ` (from ${data.meta.updatedAt})` : '';\n const preNote =\n currentRaw !== undefined\n ? `Your current profile will be saved as ${preRestorePath}.`\n : `(no existing profile at ${path} to preserve)`;\n return (\n `This will overwrite the profile at ${path} with the backup ${from}${when}.\\n` +\n `${preNote}\\n` +\n 'Continue? [y/N]'\n );\n}\n\nfunction isNotFound(error: unknown): boolean {\n return (\n typeof error === 'object' && error !== null && (error as { code?: unknown }).code === 'ENOENT'\n );\n}\n","/**\n * `takuhon sync [path] --url <base-url> [--if-match <etag>] [--dry-run]` —\n * push a local `takuhon.json` to a deployed takuhon instance.\n *\n * `sync` is the secondary update path in Spec §9.6: it mirrors the local\n * profile into the deployment's store by calling the same admin write\n * endpoint the Admin UI uses — `PUT <base-url>/api/admin/profile` — so it\n * reuses the server's schema validation, optimistic locking, audit logging,\n * and edge-cache purge. The local file is the source of truth: by default the\n * push is unconditional (a mirror). Pass `--if-match <etag>` to opt into\n * optimistic locking (the server rejects the write with 409 if the stored\n * version no longer matches).\n *\n * The admin bearer token is read from the `TAKUHON_ADMIN_TOKEN` environment\n * variable, never a flag, so it cannot leak into shell history; the name\n * matches the Worker secret. The document is sent as-is after a local schema\n * check — `sync` does not migrate (run `takuhon migrate` first if the file is\n * an older schema version).\n *\n * Exit codes:\n * 0 — synced (or, with --dry-run, the payload is valid and ready to send)\n * 1 — the local file failed validation, or the remote refused the write for\n * content reasons (422 validation, 409 If-Match conflict)\n * 2 — the command could not run: bad arguments, a missing/unreadable/non-JSON\n * file, an unset token, an auth failure (401/403), a network error, or\n * any other non-success response\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { validate } from '@takuhon/core';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\n/** Admin write endpoint, appended to the supplied `--url` origin. */\nconst ADMIN_PROFILE_PATH = '/api/admin/profile';\n\n/** Environment variable carrying the deployment's admin bearer token. */\nconst TOKEN_ENV = 'TAKUHON_ADMIN_TOKEN';\n\nconst USAGE = `Usage: takuhon sync [path] --url <base-url> [--if-match <etag>] [--dry-run]\n\nPush a local takuhon.json to a deployed takuhon instance by calling its admin\nwrite endpoint (PUT <base-url>/api/admin/profile). With no path, syncs\n./takuhon.json in the current working directory.\n\nThe local file is the source of truth: by default the push is unconditional\n(a mirror). Pass --if-match <etag> to opt into optimistic locking (the server\nreturns 409 if the stored version no longer matches). The document is sent\nas-is after a local schema check; run \\`takuhon migrate\\` first if it is an\nolder schema version.\n\nThe admin bearer token is read from the ${TOKEN_ENV} environment variable, e.g.:\n ${TOKEN_ENV}=... takuhon sync --url https://me.example\n\nOptions:\n --url <base-url> Required. Absolute http(s) origin of the deployment.\n --if-match <etag> Send If-Match for optimistic locking (opt-in).\n --dry-run Validate locally and report what would be sent; no request.\n\nExit codes: 0 = synced (or dry-run ok), 1 = local invalid / remote refused\n(422 / 409), 2 = bad arguments / file missing / unreadable / not JSON / token\nunset / auth failure / network error / other non-success response.\n`;\n\nexport interface SyncOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/** Injectable dependencies, so tests can stub the network and the token source. */\nexport interface SyncDeps {\n /** HTTP client. Defaults to the global `fetch` (Node 22+). */\n fetch?: typeof fetch;\n /** Admin token source. Defaults to reading `TAKUHON_ADMIN_TOKEN`. */\n getToken?: () => string | undefined;\n}\n\ninterface ParsedArgs {\n path: string;\n url: string;\n ifMatch?: string;\n dryRun: boolean;\n}\n\n/**\n * Run `takuhon sync` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"sync\"`).\n */\nexport async function runSync(\n args: readonly string[] = [],\n deps: SyncDeps = {},\n): Promise<SyncOutcome> {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n const parsed = parseArgs(args);\n if ('error' in parsed) {\n return {\n code: 2,\n stdout: '',\n stderr: `${parsed.error}\\nRun \\`takuhon sync --help\\` for usage.\\n`,\n };\n }\n\n return syncProfile(parsed, deps);\n}\n\nfunction parseArgs(args: readonly string[]): ParsedArgs | { error: string } {\n let path: string | undefined;\n let url: string | undefined;\n let ifMatch: string | undefined;\n let dryRun = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n\n if (arg === '--dry-run') {\n dryRun = true;\n continue;\n }\n if (arg === '--url' || arg === '--if-match') {\n const value = args[i + 1];\n if (value === undefined || value === '' || value.startsWith('-')) {\n return { error: `takuhon: \\`${arg}\\` requires a value.` };\n }\n if (arg === '--url') url = value;\n else ifMatch = value;\n i++;\n continue;\n }\n if (arg.startsWith('--url=')) {\n const value = arg.slice('--url='.length);\n if (value === '') return { error: 'takuhon: `--url` requires a value.' };\n url = value;\n continue;\n }\n if (arg.startsWith('--if-match=')) {\n const value = arg.slice('--if-match='.length);\n if (value === '') return { error: 'takuhon: `--if-match` requires a value.' };\n ifMatch = value;\n continue;\n }\n if (arg.startsWith('-')) {\n return { error: `takuhon: unknown option \\`${arg}\\` for \\`sync\\`.` };\n }\n if (path !== undefined) {\n return { error: 'takuhon: `sync` takes at most one path argument.' };\n }\n path = arg;\n }\n\n if (url === undefined) {\n return { error: 'takuhon: `sync` requires `--url <base-url>`.' };\n }\n const base = parseOrigin(url);\n if ('error' in base) return base;\n\n return { path: path ?? DEFAULT_PATH, url: base.origin, ifMatch, dryRun };\n}\n\n/**\n * Validate `--url` and reduce it to a bare `http(s)` origin. We reject any URL\n * carrying a path, query, fragment, or credentials: the admin endpoint is\n * appended to the origin, and a non-origin URL would both misplace that suffix\n * and risk sending the bearer token to an unintended target.\n */\nfunction parseOrigin(value: string): { origin: string } | { error: string } {\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n return { error: 'takuhon: `--url` must be an absolute http(s) URL.' };\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return { error: 'takuhon: `--url` must be an absolute http(s) URL.' };\n }\n if (\n parsed.username !== '' ||\n parsed.password !== '' ||\n parsed.search !== '' ||\n parsed.hash !== '' ||\n (parsed.pathname !== '/' && parsed.pathname !== '')\n ) {\n return {\n error:\n \"takuhon: `--url` must be the deployment's origin (e.g. https://me.example), \" +\n 'with no path, query, or credentials.',\n };\n }\n return { origin: parsed.origin };\n}\n\n/** Remove any literal occurrence of a secret from a string before it is shown. */\nfunction redact(text: string, secret: string): string {\n return secret === '' ? text : text.split(secret).join('***');\n}\n\n/** Strip RFC 7232 double-quote delimiters so the value can be re-wrapped cleanly. */\nfunction unquoteETag(raw: string): string {\n const trimmed = raw.trim();\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"') && trimmed.length >= 2) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\nasync function syncProfile(parsed: ParsedArgs, deps: SyncDeps): Promise<SyncOutcome> {\n const { path, url, ifMatch, dryRun } = parsed;\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return { code: 2, stdout: '', stderr: `takuhon: cannot read '${path}'.\\n` };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return { code: 2, stdout: '', stderr: `takuhon: '${path}' is not valid JSON: ${detail}\\n` };\n }\n\n const result = validate(data);\n if (!result.ok) {\n const lines = result.errors.map((e) => ` ${e.pointer || '/'}: ${e.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: '${path}' is not a valid takuhon profile; refusing to sync:\\n${lines.join('\\n')}\\n`,\n };\n }\n\n const endpoint = `${url}${ADMIN_PROFILE_PATH}`;\n const body = `${JSON.stringify(result.data)}\\n`;\n const bytes = Buffer.byteLength(body, 'utf8');\n\n if (dryRun) {\n const lock = ifMatch !== undefined ? `, If-Match \"${unquoteETag(ifMatch)}\"` : '';\n return {\n code: 0,\n stdout: `would sync ${path} -> ${endpoint} (${String(bytes)} bytes${lock})\\n--dry-run: nothing sent.\\n`,\n stderr: '',\n };\n }\n\n const getToken = deps.getToken ?? (() => process.env[TOKEN_ENV]);\n const token = getToken();\n if (token === undefined || token === '') {\n return {\n code: 2,\n stdout: '',\n stderr:\n `takuhon: ${TOKEN_ENV} is not set; sync needs the deployment's admin token.\\n` +\n `Set it for this command only, e.g.:\\n` +\n ` ${TOKEN_ENV}=... takuhon sync --url ${url}\\n`,\n };\n }\n\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n authorization: `Bearer ${token}`,\n };\n if (ifMatch !== undefined) {\n headers['if-match'] = `\"${unquoteETag(ifMatch)}\"`;\n }\n\n const fetchImpl = deps.fetch ?? fetch;\n let res: Response;\n try {\n res = await fetchImpl(endpoint, { method: 'PUT', headers, body });\n } catch (error) {\n const detail = redact(error instanceof Error ? error.message : String(error), token);\n return { code: 2, stdout: '', stderr: `takuhon: could not reach ${endpoint}: ${detail}\\n` };\n }\n\n return interpretResponse(res, { path, url, endpoint });\n}\n\ninterface Target {\n path: string;\n url: string;\n endpoint: string;\n}\n\nasync function interpretResponse(res: Response, target: Target): Promise<SyncOutcome> {\n const { path, url, endpoint } = target;\n\n if (res.ok) {\n const version = await readVersion(res);\n if (version === undefined) {\n // A 2xx without the admin API's `{ meta: { version } }` envelope means we\n // did not actually reach a takuhon admin endpoint (a proxy login page, a\n // static host, a wrong path). Do not claim success.\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: unexpected response from ${endpoint} (HTTP ${String(res.status)} but no meta.version); is --url a takuhon deployment?\\n`,\n };\n }\n return { code: 0, stdout: `synced ${path} -> ${url} (version ${version})\\n`, stderr: '' };\n }\n\n const problem = await readProblem(res);\n const status = res.status;\n\n if (status === 409) {\n const current = problem?.currentVersion;\n const hint =\n current !== undefined\n ? `remote is at version ${current}. Re-sync against it, or drop --if-match to overwrite.`\n : 'the stored version no longer matches --if-match. Re-sync, or drop --if-match to overwrite.';\n return { code: 1, stdout: '', stderr: `takuhon: sync conflict (409): ${hint}\\n` };\n }\n\n if (status === 422) {\n const errors = Array.isArray(problem?.errors) ? problem.errors : [];\n const lines = errors.map((e) => ` ${e.path}: ${e.message}`);\n const detail = lines.length > 0 ? `:\\n${lines.join('\\n')}` : '.';\n return {\n code: 1,\n stdout: '',\n stderr: `takuhon: the deployment rejected the profile (422)${detail}\\n`,\n };\n }\n\n if (status === 401 || status === 403) {\n const reason =\n status === 401\n ? `unauthorized (401); check ${TOKEN_ENV}`\n : 'forbidden (403); the request origin may not be allowed';\n return { code: 2, stdout: '', stderr: `takuhon: ${reason}.\\n` };\n }\n\n const detail = problem?.detail ?? res.statusText ?? '';\n const tail = detail ? `: ${detail}` : '.';\n return { code: 2, stdout: '', stderr: `takuhon: sync failed (${String(status)})${tail}\\n` };\n}\n\ninterface ProblemBody {\n detail?: string;\n currentVersion?: string;\n errors?: { path: string; message: string }[];\n}\n\n/** Best-effort parse of the success body's `meta.version`. */\nasync function readVersion(res: Response): Promise<string | undefined> {\n try {\n const parsed = (await res.json()) as { meta?: { version?: unknown } };\n const version = parsed.meta?.version;\n return typeof version === 'string' ? version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort parse of an RFC 7807 problem body; undefined if it is not JSON. */\nasync function readProblem(res: Response): Promise<ProblemBody | undefined> {\n let text: string;\n try {\n text = await res.text();\n } catch {\n return undefined;\n }\n try {\n return JSON.parse(text) as ProblemBody;\n } catch {\n return undefined;\n }\n}\n","/**\n * `takuhon validate [path]` — validate a `takuhon.json` against `@takuhon/core`.\n *\n * All argument handling and the validation itself live here as pure,\n * side-effect-free functions so the whole command is unit-testable: `index.ts`\n * runs `process.exit(main(...))` at module top level and is therefore not\n * import-safe. `runValidate` reads the target file itself but returns its\n * output as strings plus an exit code, leaving the actual stdout/stderr writes\n * and `process.exit` to the caller.\n *\n * Exit codes:\n * 0 — the document is valid (or `--help` was requested)\n * 1 — the document was read and parsed but failed schema validation\n * 2 — the command could not run: bad arguments, a missing/unreadable file,\n * or a file that is not valid JSON (operational errors, distinct from an\n * invalid-but-readable document)\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { validate } from '@takuhon/core';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon validate [path]\n\nValidate a takuhon.json against the takuhon schema. With no path, validates\n./takuhon.json in the current working directory.\n\nExit codes: 0 = valid, 1 = invalid, 2 = file missing / unreadable / not JSON.\n`;\n\nexport interface ValidateOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/**\n * Run `takuhon validate` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"validate\"`).\n *\n * Handles `--help` / `-h` and rejects extra positionals, then validates the\n * `takuhon.json` at the single optional path argument (default\n * `./takuhon.json`). Never throws and never writes to the process streams —\n * the caller renders the returned `stdout` / `stderr` and exits with `code`.\n */\nexport function runValidate(args: readonly string[] = []): ValidateOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n if (args.length > 1) {\n return {\n code: 2,\n stdout: '',\n stderr:\n 'takuhon: `validate` takes at most one path argument.\\n' +\n 'Run `takuhon validate --help` for usage.\\n',\n };\n }\n\n return validateFile(args[0]);\n}\n\n/** Read, parse, and schema-validate the profile at `pathArg` (default `./takuhon.json`). */\nfunction validateFile(pathArg?: string): ValidateOutcome {\n const target = pathArg ?? DEFAULT_PATH;\n\n let raw: string;\n try {\n raw = readFileSync(target, 'utf8');\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: cannot read '${target}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: '${target}' is not valid JSON: ${detail}\\n`,\n };\n }\n\n const result = validate(data);\n if (result.ok) {\n return {\n code: 0,\n stdout: `${target}: valid (schemaVersion ${result.data.schemaVersion}).\\n`,\n stderr: '',\n };\n }\n\n const count = result.errors.length;\n const lines = result.errors.map((error) => ` ${error.pointer || '/'}: ${error.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `${target}: invalid (${count} error${count === 1 ? '' : 's'}):\\n${lines.join('\\n')}\\n`,\n };\n}\n"],"mappings":"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","names":["readFileSync","readFileSync","dirname","join","dirname","join","join","dirname","join","dirname","readFileSync","randomBytes","readFileSync","extname","join","resolve","sep","readFileSync","DEFAULT_PATH","readFileSync","resolve","mkdirSync","readFileSync","renameSync","rmSync","writeFileSync","basename","dirname","join","resolve","isNotFound","contentType","readFileSync","rmSync","NotFoundError","isNotFound","readFileSync","NotFoundError","rmSync","DEFAULT_PATH","DEFAULT_PORT","USAGE","extname","isNotFound","resolve","sep","readFileSync","join","contentType","parseArgs","isHttpUrl","randomBytes","mkdirSync","readFileSync","dirname","join","generateSite","applyPublicPrivacyFilter","normalize","validate","DEFAULT_PATH","USAGE","parseArgs","isHttpUrl","readFileSync","validate","applyPublicPrivacyFilter","normalize","generateSite","join","mkdirSync","dirname","readFileSync","resolve","validate","DEFAULT_PATH","USAGE","parseArgs","resolve","readFileSync","validate","readFileSync","DEFAULT_PATH","USAGE","parseArgs","readFileSync","lines","isNotFound","readFileSync","validate","DEFAULT_PATH","USAGE","parseArgs","resolve","pkg","readFileSync","resolve","MigrationError","SCHEMA_VERSION","migrateTakuhon","validate","DEFAULT_PATH","USAGE","SCHEMA_VERSION","parseArgs","readFileSync","resolve","migrateTakuhon","MigrationError","validate","lines","readFileSync","join","resolve","USAGE","resolve","join","pkg","readFileSync","readFileSync","validate","DEFAULT_PATH","USAGE","parseArgs","readFileSync","validate","lines","isNotFound","readFileSync","validate","DEFAULT_PATH","USAGE","parseArgs","readVersion","detail","readFileSync","validate","DEFAULT_PATH","USAGE","readFileSync","VERSION"]}
|