zidane 9.1.9 → 9.1.13
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/{acp-Bha_yCgU.js → acp-Cz5mFUjB.js} +3 -3
- package/dist/{acp-Bha_yCgU.js.map → acp-Cz5mFUjB.js.map} +1 -1
- package/dist/acp-cli.js +4 -4
- package/dist/acp.js +1 -1
- package/dist/{auth-store-port-BvxVXx9g.js → auth-store-port-CDJa8Q8U.js} +2 -2
- package/dist/{auth-store-port-BvxVXx9g.js.map → auth-store-port-CDJa8Q8U.js.map} +1 -1
- package/dist/{auto-update-restart-CF3uPnr4.js → auto-update-restart-B4pU7Qgf.js} +10 -10
- package/dist/{auto-update-restart-CF3uPnr4.js.map → auto-update-restart-B4pU7Qgf.js.map} +1 -1
- package/dist/chat.js +9 -9
- package/dist/{cli-shared-C17TUz6H.js → cli-shared-BVtiiICW.js} +3 -3
- package/dist/{cli-shared-C17TUz6H.js.map → cli-shared-BVtiiICW.js.map} +1 -1
- package/dist/{credentials-BZP-MgtZ.js → credentials-axv5VvMR.js} +2 -2
- package/dist/{credentials-BZP-MgtZ.js.map → credentials-axv5VvMR.js.map} +1 -1
- package/dist/daemon-cli.js +2 -2
- package/dist/{discovery-Czp7bZZP.js → discovery-D7qVZWzq.js} +4 -4
- package/dist/{discovery-Czp7bZZP.js.map → discovery-D7qVZWzq.js.map} +1 -1
- package/dist/{engine-CDuOhwbt.js → engine-DjM5NPIf.js} +3 -3
- package/dist/{engine-CDuOhwbt.js.map → engine-DjM5NPIf.js.map} +1 -1
- package/dist/{executor-CoHqHsCp.js → executor-DlW7qAeN.js} +2 -2
- package/dist/{executor-CoHqHsCp.js.map → executor-DlW7qAeN.js.map} +1 -1
- package/dist/extensions/testing.js +2 -2
- package/dist/{extensions-BEaWxX4e.js → extensions-D7DeZNpn.js} +6 -6
- package/dist/{extensions-BEaWxX4e.js.map → extensions-D7DeZNpn.js.map} +1 -1
- package/dist/extensions.js +5 -5
- package/dist/{google-Bzt7uvy9.js → google-D1zjAViO.js} +2 -2
- package/dist/{google-Bzt7uvy9.js.map → google-D1zjAViO.js.map} +1 -1
- package/dist/{host-DkqB2P7L.js → host-Ds3RDlG5.js} +9 -9
- package/dist/{host-DkqB2P7L.js.map → host-Ds3RDlG5.js.map} +1 -1
- package/dist/{host-signals-Ca_oUeYl.js → host-signals-C8x5Yso0.js} +5 -5
- package/dist/{host-signals-Ca_oUeYl.js.map → host-signals-C8x5Yso0.js.map} +1 -1
- package/dist/{host-version-BwON7XSr.js → host-version-BGZumnpZ.js} +2 -2
- package/dist/{host-version-BwON7XSr.js.map → host-version-BGZumnpZ.js.map} +1 -1
- package/dist/index.js +5 -5
- package/dist/ipc/remote.js +3 -3
- package/dist/{ipc-Dlqz_xIe.js → ipc-DGfuNEfR.js} +4 -4
- package/dist/{ipc-Dlqz_xIe.js.map → ipc-DGfuNEfR.js.map} +1 -1
- package/dist/ipc.js +4 -4
- package/dist/{links-B5Dz7jku.js → links-D_1RhPh5.js} +8 -8
- package/dist/{links-B5Dz7jku.js.map → links-D_1RhPh5.js.map} +1 -1
- package/dist/{loaded-entry-BGN69Fns.js → loaded-entry-CLva6GQT.js} +2 -2
- package/dist/{loaded-entry-BGN69Fns.js.map → loaded-entry-CLva6GQT.js.map} +1 -1
- package/dist/{memory-ZWFqa7di.js → memory-CaFXUvQs.js} +15 -15
- package/dist/{memory-ZWFqa7di.js.map → memory-CaFXUvQs.js.map} +1 -1
- package/dist/{path-display-B3Jeqx9E.js → path-display-ZIWdFaTI.js} +3 -3
- package/dist/{path-display-B3Jeqx9E.js.map → path-display-ZIWdFaTI.js.map} +1 -1
- package/dist/providers/google.js +1 -1
- package/dist/{providers-ChifjOYS.js → providers-CsYrxuX1.js} +2 -2
- package/dist/{providers-ChifjOYS.js.map → providers-CsYrxuX1.js.map} +1 -1
- package/dist/providers.js +1 -1
- package/dist/{registry-mmAX5qBS.js → registry-BY45AOA-.js} +2 -2
- package/dist/{registry-mmAX5qBS.js.map → registry-BY45AOA-.js.map} +1 -1
- package/dist/{registry-TW3C94iD.js → registry-DTotchq6.js} +2 -2
- package/dist/{registry-TW3C94iD.js.map → registry-DTotchq6.js.map} +1 -1
- package/dist/{registry-CYEN4C4M.js → registry-Dl6261Xh.js} +2 -2
- package/dist/{registry-CYEN4C4M.js.map → registry-Dl6261Xh.js.map} +1 -1
- package/dist/{remote-hosts-Ctg9fwQY.js → remote-hosts-usqMiMbs.js} +4 -4
- package/dist/{remote-hosts-Ctg9fwQY.js.map → remote-hosts-usqMiMbs.js.map} +1 -1
- package/dist/{setting-DPim5iCP.js → setting-DzTMJIV3.js} +2 -2
- package/dist/{setting-DPim5iCP.js.map → setting-DzTMJIV3.js.map} +1 -1
- package/dist/{settings-keys-BIWy4T_0.js → settings-keys-DDrfnBHi.js} +2 -2
- package/dist/{settings-keys-BIWy4T_0.js.map → settings-keys-DDrfnBHi.js.map} +1 -1
- package/dist/{system-freeze-CEuTQzNu.js → system-freeze-CY4Hc9WX.js} +2 -2
- package/dist/{system-freeze-CEuTQzNu.js.map → system-freeze-CY4Hc9WX.js.map} +1 -1
- package/dist/tui.js +15 -15
- package/dist/{version-CU8ppdCQ.js → version-Bu3Nc8-U.js} +2 -2
- package/dist/{version-CU8ppdCQ.js.map → version-Bu3Nc8-U.js.map} +1 -1
- package/dist/{wire-GwOPpxJg.js → wire-BKcREhuL.js} +2 -2
- package/dist/{wire-GwOPpxJg.js.map → wire-BKcREhuL.js.map} +1 -1
- package/dist/{ws-CqlZkB4w.js → ws-DriNRyP-.js} +4 -4
- package/dist/{ws-CqlZkB4w.js.map → ws-DriNRyP-.js.map} +1 -1
- package/dist/{ws-client-Bn_asPwE.js → ws-client-PZdbO0fj.js} +2 -2
- package/dist/{ws-client-Bn_asPwE.js.map → ws-client-PZdbO0fj.js.map} +1 -1
- package/docs/RELEASE.md +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Mt as formatBindingForDisplay } from "./registry-
|
|
1
|
+
import { Mt as formatBindingForDisplay } from "./registry-DTotchq6.js";
|
|
2
2
|
import { i as writeJsonFileAtomic } from "./atomic-write-BewQOXlD.js";
|
|
3
3
|
import { r as artifactKindLabel, u as renderArtifactRow } from "./builtin-artifact-kinds-DsqaAQjX.js";
|
|
4
|
-
import { C as parseSemver, E as reinstallHint, O as updateChecksOptedOut, T as performSelfUpdate, _ as checkForUpdate, f as isUpdateLockStale, h as findInstalledCompanion, o as watchInstalledVersion, p as readUpdateLock, s as writeUpdateAppliedMarker, u as acquireUpdateLock, v as checkTarballAvailable, x as detectPackageManager, y as compareSemver } from "./auto-update-restart-
|
|
4
|
+
import { C as parseSemver, E as reinstallHint, O as updateChecksOptedOut, T as performSelfUpdate, _ as checkForUpdate, f as isUpdateLockStale, h as findInstalledCompanion, o as watchInstalledVersion, p as readUpdateLock, s as writeUpdateAppliedMarker, u as acquireUpdateLock, v as checkTarballAvailable, x as detectPackageManager, y as compareSemver } from "./auto-update-restart-B4pU7Qgf.js";
|
|
5
5
|
import { Buffer } from "node:buffer";
|
|
6
6
|
import { isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
7
7
|
import { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, readSync, renameSync, statSync, unlinkSync } from "node:fs";
|
|
@@ -1357,4 +1357,4 @@ function formatPathForCwd(projectRelativePath, projectRoot, cwd) {
|
|
|
1357
1357
|
//#endregion
|
|
1358
1358
|
export { upgradeError as C, summarizeArtifactCounts as D, groupArtifacts as E, readFailedUpdate as S, upgradeInstalledPackages as T, UPDATE_FAILED_FILE as _, UPDATE_RESULT_FILE as a, isAutoInstallCandidate as b, readUpdateWorkerResult as c, buildRestartHint as d, buildUpdateHint as f, AUTO_INSTALL_RETRY_BACKOFF_MS as g, useUpdateCheck as h, UPDATE_LOG_FILE as i, runUpdateWorkerMain as l, useRestartRequired as m, buildHints as n, autoInstallViaWorker as o, useAutoInstallUpdate as p, shortChord as r, followRunningUpdate as s, formatPathForCwd as t, startUpdateWorker as u, autoInstallUpdate as v, writeFailedUpdate as w, isTransientFailure as x, clearFailedUpdate as y };
|
|
1359
1359
|
|
|
1360
|
-
//# sourceMappingURL=path-display-
|
|
1360
|
+
//# sourceMappingURL=path-display-ZIWdFaTI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-display-B3Jeqx9E.js","names":["nodeSpawn"],"sources":["../src/chat/artifacts-view.ts","../src/chat/auto-update-service.ts","../src/chat/auto-update-auto-install.ts","../src/chat/auto-update-hook.ts","../src/chat/auto-update-worker.ts","../src/chat/footer-hints.ts","../src/chat/path-display.ts"],"sourcesContent":["/**\n * Renderer-agnostic shaping of session artifacts for display — grouping\n * and row presentation. Shared by the TUI rail + modal and the GUI rail, so\n * every surface agrees on what an artifact looks like.\n */\n\nimport type { ExtensionArtifactRow, ExtensionRegistry } from '../extensions'\nimport type { SessionArtifact } from '../session'\nimport { artifactKindLabel, renderArtifactRow } from '../extensions/builtin-artifact-kinds'\n\n/** Whether artifacts are listed for the current session or the whole project. */\nexport type ArtifactScope = 'session' | 'project'\n\n/** One artifact paired with its rendered row. */\nexport interface ArtifactViewRow {\n /** The session artifact. */\n artifact: SessionArtifact\n /** Rendered row for the artifact. */\n row: ExtensionArtifactRow\n}\n\n/** Artifacts of one kind, ready to display. */\nexport interface ArtifactViewGroup {\n /** Artifact kind id. */\n kind: string\n /** Human kind label (`Pull request`), raw kind when unregistered. */\n label: string\n /** Rows of this kind, in the store's order. */\n rows: ArtifactViewRow[]\n}\n\ntype KindRegistry = Pick<ExtensionRegistry, 'artifactKinds'> | undefined\n\n/** Display order for the built-in kinds; anything else follows alphabetically. */\nconst KIND_ORDER = ['pull_request', 'branch', 'commit', 'file']\n\n/**\n * Group by kind (built-ins first in a fixed order, then extension kinds\n * alphabetically); rows inside a group keep the store's newest-first order.\n *\n * @param artifacts - Session artifacts to group.\n * @param registry - Extension registry that supplies kind labels and row renderers.\n */\nexport function groupArtifacts(artifacts: readonly SessionArtifact[], registry?: KindRegistry): ArtifactViewGroup[] {\n const byKind = new Map<string, ArtifactViewRow[]>()\n for (const artifact of artifacts) {\n const list = byKind.get(artifact.kind) ?? []\n list.push({ artifact, row: renderArtifactRow(artifact, registry) })\n byKind.set(artifact.kind, list)\n }\n const kinds = [...byKind.keys()].sort((a, b) => {\n const ai = KIND_ORDER.indexOf(a)\n const bi = KIND_ORDER.indexOf(b)\n if (ai !== -1 || bi !== -1)\n return (ai === -1 ? KIND_ORDER.length : ai) - (bi === -1 ? KIND_ORDER.length : bi)\n return a.localeCompare(b)\n })\n return kinds.map(kind => ({ kind, label: artifactKindLabel(kind, registry), rows: byKind.get(kind)! }))\n}\n\n/**\n * One count per kind, a badge each: `['3 pull requests', '2 files']`; empty\n * for none.\n *\n * @param artifacts - Session artifacts to count.\n * @param registry - Extension registry that supplies kind labels.\n */\nexport function summarizeArtifactCounts(artifacts: readonly SessionArtifact[], registry?: KindRegistry): string[] {\n return groupArtifacts(artifacts, registry)\n .map(g => `${g.rows.length} ${pluralizeKind(g.label, g.rows.length)}`)\n}\n\nfunction pluralizeKind(label: string, count: number): string {\n const lower = label.toLowerCase()\n if (count === 1)\n return lower\n return /(?:s|x|ch|sh)$/.test(lower) ? `${lower}es` : `${lower}s`\n}\n","// ---------------------------------------------------------------------------\n// Structured, host-agnostic upgrade of every INSTALLED zidane package.\n//\n// `runUpdateCommand` is the terminal-facing ledger for `zidane upgrade`; it\n// knows the TUI is the primary and detects that install's package manager\n// from `process.execPath`. A GUI cannot reuse it — its `execPath` is\n// Electron's — and wants results, not text. This module is the shared\n// engine both can build on: every target is located by its bin on PATH\n// (`findInstalledCompanion`), checked against the registry, installed through\n// the package manager that owns THAT bin, and verified on disk afterwards\n// (a PM exiting 0 is not proof; an optional platform binary that 404s is\n// silently skipped).\n//\n// On success the applied marker is written so running surfaces can offer a\n// restart — see `auto-update-restart.ts`.\n// ---------------------------------------------------------------------------\n\nimport type { PackageManagerCommand, UpdateStatus } from './auto-update'\nimport type { CompanionSpec, InstalledCompanion } from './auto-update-companion'\nimport { Writable } from 'node:stream'\nimport {\n checkForUpdate,\n checkTarballAvailable,\n compareSemver,\n detectPackageManager,\n performSelfUpdate,\n reinstallHint,\n SELF_UPDATE_TIMEOUT_MS,\n} from './auto-update'\nimport { findInstalledCompanion } from './auto-update-companion'\nimport { writeUpdateAppliedMarker } from './auto-update-restart'\n\n/** Outcome of upgrading one package. */\nexport interface UpgradePackageResult {\n /** npm package name. */\n packageName: string\n /** Version on disk before the run; `null` when not installed. */\n from: string | null\n /** Version the registry advertises; `null` when the check failed. */\n to: string | null\n /**\n * - `'updated'` — installed and verified on disk.\n * - `'up-to-date'` — nothing to do.\n * - `'not-installed'` — bin not on PATH; skipped silently.\n * - `'unavailable'` — published but the tarball is not downloadable yet.\n * - `'failed'` — registry error, PM failure, or the install did not land; see `error`.\n */\n status: 'updated' | 'up-to-date' | 'not-installed' | 'unavailable' | 'failed'\n /** The PM command that ran (or would run). */\n command?: string[]\n /** Why the upgrade did not complete. Set for `failed` and `unavailable`. */\n error?: string\n /**\n * What failed, for `failed`: the registry check, the package manager could\n * not start, ran past its timeout or exited non-zero, or the version on disk\n * is not the one asked for.\n */\n reason?: 'registry' | 'spawn' | 'timeout' | 'exit' | 'verify'\n}\n\n/** Outcome of {@link upgradeInstalledPackages}. */\nexport interface UpgradeReport {\n /** One result per target package, in order. */\n results: UpgradePackageResult[]\n /** No `failed` / `unavailable` entries. */\n ok: boolean\n /** At least one package was actually installed — a restart is now pending. */\n installedAny: boolean\n}\n\n/** Progress event emitted while packages are upgraded: a version check, an install start, a line of package manager output, or a finished package. */\nexport type UpgradeProgressEvent\n = | { kind: 'check', packageName: string }\n | { kind: 'install', packageName: string, from: string, to: string, command: string[] }\n | { kind: 'log', packageName: string, stream: 'stdout' | 'stderr', line: string }\n | { kind: 'done', result: UpgradePackageResult }\n\n/** Options for {@link upgradeInstalledPackages}. */\nexport interface UpgradeInstalledPackagesOptions {\n /** Packages to keep in step, e.g. `[{ packageName: 'zidane-gui' }, { packageName: 'zidane-tui', binName: 'zidane' }]`. */\n targets: readonly CompanionSpec[]\n /** dist-tag to install. Defaults to `'latest'`. */\n channel?: string\n /** Registry base URL. Defaults to `https://registry.npmjs.org`. */\n registry?: string\n /** Where the registry cache and the applied marker live. */\n cacheDir?: string\n /** Install even when the registry check failed or reports up-to-date. */\n force?: boolean\n /** Abort signal. Targets not yet started are skipped once it aborts. */\n signal?: AbortSignal\n /** Per package-manager run. Defaults to {@link SELF_UPDATE_TIMEOUT_MS}. */\n timeoutMs?: number\n /** Called with check, install, log and done events as the upgrade runs. */\n onProgress?: (event: UpgradeProgressEvent) => void\n /** Injected for tests. */\n findInstalled?: typeof findInstalledCompanion\n /** Injected for tests. Defaults to {@link performSelfUpdate}. */\n performSelfUpdate?: typeof performSelfUpdate\n /** Injected for tests. Defaults to {@link checkTarballAvailable}. */\n checkTarballAvailable?: typeof checkTarballAvailable\n /** Injected for tests. Defaults to {@link checkForUpdate}. */\n checkForUpdate?: typeof checkForUpdate\n /** Injected for tests. Defaults to {@link detectPackageManager}. */\n detectPackageManager?: typeof detectPackageManager\n /** Injected for tests. Defaults to {@link writeUpdateAppliedMarker}. */\n writeMarker?: typeof writeUpdateAppliedMarker\n /** Injected for tests. Defaults to `Date.now`. */\n now?: () => number\n}\n\n/**\n * Upgrades each installed target package through its own package manager.\n * Skips targets whose bin is not on PATH, checks the registry for each one, reports `unavailable` instead of installing when the tarball is not downloadable yet, runs the install, and verifies the new version on disk. Writes the applied marker when something was installed and `cacheDir` is set.\n *\n * @param options - Target packages, registry settings and progress callback.\n * @example\n * ```ts\n * import { upgradeInstalledPackages } from 'zidane/chat'\n *\n * const report = await upgradeInstalledPackages({\n * targets: [{ packageName: 'zidane-tui', binName: 'zidane' }],\n * onProgress: event => console.log(event.kind),\n * })\n * if (!report.ok)\n * console.error(report.results.filter(r => r.status === 'failed'))\n * ```\n */\nexport async function upgradeInstalledPackages(options: UpgradeInstalledPackagesOptions): Promise<UpgradeReport> {\n const channel = options.channel ?? 'latest'\n const find = options.findInstalled ?? findInstalledCompanion\n const check = options.checkForUpdate ?? checkForUpdate\n const available = options.checkTarballAvailable ?? checkTarballAvailable\n const install = options.performSelfUpdate ?? performSelfUpdate\n const detect = options.detectPackageManager ?? detectPackageManager\n const progress = options.onProgress ?? (() => {})\n\n const results: UpgradePackageResult[] = []\n for (const spec of options.targets) {\n if (options.signal?.aborted)\n break\n const installed = find(spec)\n if (!installed) {\n results.push({ packageName: spec.packageName, from: null, to: null, status: 'not-installed' })\n continue\n }\n progress({ kind: 'check', packageName: spec.packageName })\n const status = await check({\n packageName: installed.packageName,\n currentVersion: installed.version,\n channel,\n registry: options.registry,\n cacheDir: options.cacheDir,\n force: true,\n timeoutMs: 8000,\n signal: options.signal,\n })\n const result = await upgradeOne(installed, status)\n progress({ kind: 'done', result })\n results.push(result)\n }\n\n const installedAny = results.some(r => r.status === 'updated')\n if (installedAny && options.cacheDir) {\n const writeMarker = options.writeMarker ?? writeUpdateAppliedMarker\n writeMarker(options.cacheDir, {\n at: (options.now ?? Date.now)(),\n packages: results\n .filter(r => r.status === 'updated' && r.to)\n .map(r => ({ packageName: r.packageName, version: r.to! })),\n })\n }\n return {\n results,\n ok: results.every(r => r.status !== 'failed' && r.status !== 'unavailable'),\n installedAny,\n }\n\n async function upgradeOne(installed: InstalledCompanion, status: UpdateStatus): Promise<UpgradePackageResult> {\n const base = { packageName: installed.packageName, from: installed.version, to: status.latest }\n if (status.source === 'error' && !options.force)\n return { ...base, status: 'failed', reason: 'registry', error: `registry check failed: ${status.error ?? 'unknown error'}` }\n const outdated = status.latest !== null && compareSemver(status.latest, installed.version) > 0\n if (!outdated && !options.force)\n return { ...base, status: 'up-to-date' }\n if (status.latest && !options.force) {\n const availability = await available({ packageName: installed.packageName, version: status.latest, registry: options.registry })\n if (availability === 'missing')\n return { ...base, status: 'unavailable', error: `${installed.packageName} ${status.latest} is published but not downloadable yet. Try again in a few minutes.` }\n }\n // The PM that owns THIS bin — npm/pnpm/yarn/bun/Volta differ per install.\n const command: PackageManagerCommand = detect({ packageName: installed.packageName, channel, binaryPath: installed.binaryPath })\n progress({ kind: 'install', packageName: installed.packageName, from: installed.version, to: status.latest ?? channel, command: command.argv })\n\n const outcome = await install({\n packageName: installed.packageName,\n channel,\n packageManager: command.id,\n signal: options.signal,\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n stdout: lineSink(installed.packageName, 'stdout'),\n stderr: lineSink(installed.packageName, 'stderr'),\n // Quiet PM output: the UI shows one live line, not a progress bar dump.\n env: { npm_config_loglevel: 'error', npm_config_progress: 'false' },\n })\n if (outcome.spawnError)\n return { ...base, status: 'failed', reason: 'spawn', command: command.argv, error: `could not run ${command.argv[0]}: ${outcome.spawnError}` }\n if (outcome.timedOut) {\n const minutes = Math.round((options.timeoutMs ?? SELF_UPDATE_TIMEOUT_MS) / 60_000)\n return { ...base, status: 'failed', reason: 'timeout', command: command.argv, error: `${command.argv[0]} did not finish within ${minutes} min and was stopped` }\n }\n if (outcome.exitCode !== 0)\n return { ...base, status: 'failed', reason: 'exit', command: command.argv, error: `${command.argv[0]} exited with ${outcome.exitCode ?? 'unknown'}` }\n\n // Verify: what is on disk now vs. what we asked for.\n const after = find(installed)\n if (status.latest && after && compareSemver(after.version, status.latest) < 0) {\n return {\n ...base,\n status: 'failed',\n reason: 'verify',\n command: command.argv,\n error: `install finished but ${after.version} is on disk, expected ${status.latest}. Reinstall: ${reinstallHint(command, installed.packageName, channel)}`,\n }\n }\n return { ...base, to: after?.version ?? status.latest, status: 'updated', command: command.argv }\n }\n\n /** Split a PM's byte stream into trimmed lines for the progress sink. */\n function lineSink(packageName: string, stream: 'stdout' | 'stderr'): NodeJS.WritableStream {\n let buffer = ''\n const flush = (final: boolean) => {\n const parts = buffer.split(/\\r?\\n/)\n buffer = final ? '' : parts.pop() ?? ''\n for (const part of parts) {\n const line = part.trim()\n if (line)\n progress({ kind: 'log', packageName, stream, line })\n }\n }\n return new Writable({\n write(chunk: Uint8Array | string, _enc, cb) {\n buffer += chunk.toString()\n flush(false)\n cb()\n },\n final(cb) {\n flush(true)\n cb()\n },\n })\n }\n}\n","// ---------------------------------------------------------------------------\n// Automatic installs (`Settings.autoInstallUpdates`).\n//\n// When a background check finds a newer stable release, the host calls\n// `autoInstallUpdate`. It runs the same engine as the GUI's Install button\n// and `zidane upgrade` (`upgradeInstalledPackages`), under the shared install\n// lock (`auto-update-lock.ts`), and never restarts anything: the applied\n// marker it writes wakes the restart chip.\n//\n// A failed automatic install records its version in\n// `<cacheDir>/update-failed.json`. A failure that may pass on its own (the\n// registry or the tarball not reachable yet, a timeout) is retried after\n// {@link AUTO_INSTALL_RETRY_BACKOFF_MS}; any other failure is not retried\n// for that version. A newer release, or a manual install (which clears the\n// record), always runs. When the cache dir can't be written, the record is\n// kept in memory for the life of the process instead.\n// ---------------------------------------------------------------------------\n\nimport type { UpdateStatus } from './auto-update'\nimport type { CompanionSpec } from './auto-update-companion'\nimport type { UpdateLockHolder, UpdateLockOptions } from './auto-update-lock'\nimport type { UpgradeInstalledPackagesOptions, UpgradeReport } from './auto-update-service'\nimport { readFileSync, unlinkSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { writeJsonFileAtomic } from '../atomic-write'\nimport { parseSemver, updateChecksOptedOut } from './auto-update'\nimport { acquireUpdateLock } from './auto-update-lock'\nimport { upgradeInstalledPackages } from './auto-update-service'\n\n/** File name of the failed-install record inside the cache directory. */\nexport const UPDATE_FAILED_FILE = 'update-failed.json'\n\n/**\n * Waits before retrying a version whose install failed in a way that may pass\n * on its own: after the 1st, 2nd and 3rd such failure. A 4th is final.\n */\nexport const AUTO_INSTALL_RETRY_BACKOFF_MS: readonly number[] = [60 * 60_000, 6 * 60 * 60_000, 24 * 60 * 60_000]\n\n/** The automatic install that failed last. */\nexport interface FailedUpdateRecord {\n /** Release the install tried to reach. */\n version: string\n /** Why it failed, one package per line. */\n error: string\n /** ms since epoch. */\n at: number\n /**\n * The failure may pass on its own (registry or tarball not reachable yet, a\n * timeout): the version is retried after {@link AUTO_INSTALL_RETRY_BACKOFF_MS}.\n * Absent means final for this version.\n */\n transient?: boolean\n /** Failed attempts at this version. Absent counts as 1. */\n attempts?: number\n}\n\n/** Records the cache dir could not hold, per cache dir, for this process. */\nconst unwritable = new Map<string, FailedUpdateRecord>()\n\n/**\n * Reads the failed-install record. `null` when there is none or it does not\n * parse. Falls back to the record this process could not write to disk.\n *\n * @param cacheDir - Directory that holds the record.\n */\nexport function readFailedUpdate(cacheDir: string): FailedUpdateRecord | null {\n try {\n const raw = JSON.parse(readFileSync(join(cacheDir, UPDATE_FAILED_FILE), 'utf8')) as Partial<FailedUpdateRecord>\n if (typeof raw.version === 'string' && typeof raw.error === 'string' && typeof raw.at === 'number') {\n return {\n version: raw.version,\n error: raw.error,\n at: raw.at,\n ...(raw.transient === true ? { transient: true } : {}),\n ...(typeof raw.attempts === 'number' ? { attempts: raw.attempts } : {}),\n }\n }\n return null\n }\n catch {\n return unwritable.get(cacheDir) ?? null\n }\n}\n\n/**\n * Records a failed automatic install. When the cache dir can't be written,\n * the record is kept in memory for this process, so a broken cache never\n * turns into a package-manager run on every check.\n *\n * @param cacheDir - Directory that holds the record. Created when missing.\n * @param record - What failed.\n */\nexport function writeFailedUpdate(cacheDir: string, record: FailedUpdateRecord): void {\n try {\n writeJsonFileAtomic(join(cacheDir, UPDATE_FAILED_FILE), record, { ensureDir: true })\n unwritable.delete(cacheDir)\n }\n catch {\n unwritable.set(cacheDir, record)\n }\n}\n\n/**\n * The record for another failure of `version`: attempts count up when the\n * last record was for the same version.\n *\n * @param cacheDir - Directory that holds the record.\n * @param failure - The version, why it failed, whether it may pass on its own, and when.\n */\nexport function nextFailedUpdate(cacheDir: string, failure: { version: string, error: string, transient: boolean, at: number }): FailedUpdateRecord {\n const previous = readFailedUpdate(cacheDir)\n const attempts = previous?.version === failure.version ? (previous.attempts ?? 1) + 1 : 1\n return { version: failure.version, error: failure.error, at: failure.at, attempts, ...(failure.transient ? { transient: true } : {}) }\n}\n\n/**\n * Forgets the failed install, so the next check may try again. Called by every\n * manual install.\n *\n * @param cacheDir - Directory that holds the record.\n */\nexport function clearFailedUpdate(cacheDir: string): void {\n unwritable.delete(cacheDir)\n try {\n unlinkSync(join(cacheDir, UPDATE_FAILED_FILE))\n }\n catch {\n // Nothing to clear.\n }\n}\n\n/**\n * Whether `version` may be tried again: `true`, `'retry-later'` while a\n * passing failure is backing off, or `'failed-before'` for good.\n */\nfunction retryVerdict(record: FailedUpdateRecord | null, version: string, now: number): true | 'retry-later' | 'failed-before' {\n if (record?.version !== version)\n return true\n const attempts = record.attempts ?? 1\n if (!record.transient || attempts > AUTO_INSTALL_RETRY_BACKOFF_MS.length)\n return 'failed-before'\n return now - record.at >= AUTO_INSTALL_RETRY_BACKOFF_MS[attempts - 1]! ? true : 'retry-later'\n}\n\n/** Why {@link autoInstallUpdate} did nothing. */\nexport type AutoInstallSkipReason\n /** No newer stable release on `latest`, or the env opts out. */\n = | 'not-eligible'\n /** This version failed before; wait for a newer one or a manual install. */\n | 'failed-before'\n /** This version failed in a way that may pass; it is retried after a backoff. */\n | 'retry-later'\n /** Another install holds the lock. The installed-version watcher sees its result. */\n | 'locked'\n\n/** Outcome of {@link autoInstallUpdate}. */\nexport type AutoInstallOutcome\n = | { kind: 'installed', version: string, report: UpgradeReport }\n | { kind: 'skipped', reason: AutoInstallSkipReason, holder?: UpdateLockHolder }\n | { kind: 'failed', version: string, error: string }\n\n/** Options for {@link isAutoInstallCandidate}. */\nexport interface AutoInstallCandidateOptions {\n /** Directory that holds the failed-install record. */\n cacheDir: string\n /** Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv\n /** Defaults to `Date.now`. */\n now?: () => number\n}\n\n/**\n * Whether a check result is one an automatic install may act on: a newer\n * release on the `latest` dist-tag read from the registry just now (not a\n * stale cache), not a prerelease, not opted out by env, and not a version\n * that failed before (see {@link AUTO_INSTALL_RETRY_BACKOFF_MS}).\n *\n * @param status - Result of the background check.\n * @param options - Cache directory and environment.\n * @returns `true`, or why not.\n */\nexport function isAutoInstallCandidate(status: UpdateStatus | null, options: AutoInstallCandidateOptions): true | AutoInstallSkipReason {\n if (!status?.hasUpdate || !status.latest || status.channel !== 'latest')\n return 'not-eligible'\n if (status.source === 'skipped' || status.source === 'error' || status.stale || updateChecksOptedOut(options.env))\n return 'not-eligible'\n const semver = parseSemver(status.latest)\n if (!semver || semver.prerelease.length > 0)\n return 'not-eligible'\n return retryVerdict(readFailedUpdate(options.cacheDir), status.latest, (options.now ?? Date.now)())\n}\n\n/** Options for {@link autoInstallUpdate}. */\nexport interface AutoInstallUpdateOptions {\n /** Result of the background check that found the release. */\n status: UpdateStatus\n /** Where the lock, the failed record, the registry cache and the applied marker live. */\n cacheDir: string\n /** Packages to keep in step. */\n targets: readonly CompanionSpec[]\n /** Who runs the install, recorded in the lock: `'desktop app'`, `'terminal app'`. */\n by: string\n /** Passed through to {@link upgradeInstalledPackages} (progress, PATH-aware probes). */\n upgradeOptions?: Omit<UpgradeInstalledPackagesOptions, 'targets' | 'cacheDir' | 'channel' | 'force'>\n /** Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv\n /** Injected for tests. Defaults to {@link upgradeInstalledPackages}. */\n upgrade?: typeof upgradeInstalledPackages\n /** Injected for tests. */\n lock?: UpdateLockOptions\n /** Injected for tests. Defaults to `Date.now`. */\n now?: () => number\n}\n\n/**\n * Installs the release a background check found, unless something says not\n * to (see {@link isAutoInstallCandidate}) or another install holds the lock.\n * Never throws. Records a failure so the same version is not retried, and\n * clears the record on success.\n *\n * @param options - Check result, cache directory, targets and test seams.\n * @example\n * ```ts\n * const outcome = await autoInstallUpdate({ status, cacheDir, targets, by: 'terminal app' })\n * if (outcome.kind === 'failed')\n * log.warn('automatic update failed', { error: outcome.error })\n * ```\n */\nexport async function autoInstallUpdate(options: AutoInstallUpdateOptions): Promise<AutoInstallOutcome> {\n const candidate = isAutoInstallCandidate(options.status, { cacheDir: options.cacheDir, now: options.now ?? Date.now, ...(options.env ? { env: options.env } : {}) })\n if (candidate !== true)\n return { kind: 'skipped', reason: candidate }\n const lock = acquireUpdateLock(options.cacheDir, options.by, options.lock)\n if (!lock.acquired)\n return { kind: 'skipped', reason: 'locked', holder: lock.holder }\n try {\n return await runAutoInstallLocked(options)\n }\n finally {\n lock.release()\n }\n}\n\n/**\n * {@link autoInstallUpdate} for a caller that already holds the install lock\n * (the update worker, which must keep it until its result is written).\n *\n * @param options - Same as {@link autoInstallUpdate}; `lock` is ignored.\n */\nexport async function runAutoInstallLocked(options: AutoInstallUpdateOptions): Promise<AutoInstallOutcome> {\n const { status, cacheDir } = options\n const now = options.now ?? Date.now\n const candidate = isAutoInstallCandidate(status, { cacheDir, now, ...(options.env ? { env: options.env } : {}) })\n if (candidate !== true)\n return { kind: 'skipped', reason: candidate }\n const version = status.latest!\n try {\n const report = await (options.upgrade ?? upgradeInstalledPackages)({\n ...options.upgradeOptions,\n targets: options.targets,\n cacheDir,\n channel: 'latest',\n })\n const error = upgradeError(report)\n if (error) {\n writeFailedUpdate(cacheDir, nextFailedUpdate(cacheDir, { version, error, transient: isTransientFailure(report), at: now() }))\n return { kind: 'failed', version, error }\n }\n clearFailedUpdate(cacheDir)\n return { kind: 'installed', version, report }\n }\n catch (err) {\n const error = err instanceof Error ? err.message : String(err)\n writeFailedUpdate(cacheDir, nextFailedUpdate(cacheDir, { version, error, transient: false, at: now() }))\n return { kind: 'failed', version, error }\n }\n}\n\n/**\n * Whether every failure in the run may pass on its own: the registry could not\n * be read, the release is not downloadable yet, or the package manager timed\n * out. A package-manager error or a wrong version on disk is not.\n *\n * @param report - Result of {@link upgradeInstalledPackages}.\n */\nexport function isTransientFailure(report: UpgradeReport): boolean {\n const failures = report.results.filter(r => r.status === 'failed' || r.status === 'unavailable')\n return failures.length > 0 && failures.every(r => r.status === 'unavailable' || r.reason === 'registry' || r.reason === 'timeout')\n}\n\n/**\n * Why an upgrade run did not land, one package per line; `null` when it did.\n * A run that found no package on PATH failed too: nothing was installed.\n *\n * @param report - Result of {@link upgradeInstalledPackages}.\n */\nexport function upgradeError(report: UpgradeReport): string | null {\n const failures = report.results.filter(r => r.status === 'failed' || r.status === 'unavailable')\n if (failures.length > 0)\n return failures.map(f => `${f.packageName}: ${f.error ?? f.status}`).join('\\n')\n if (report.results.every(r => r.status === 'not-installed'))\n return 'no zidane packages found on PATH'\n return null\n}\n","// ---------------------------------------------------------------------------\n// `useUpdateCheck` — renderer-agnostic React hook driving the auto-update\n// footer chip + any banner a host wants to wire.\n//\n// Lives in `zidane/chat` (not `zidane/tui`) so a GUI shell can consume the\n// same status. Pure React + the renderer-agnostic core in\n// `./auto-update`; no OpenTUI imports.\n// ---------------------------------------------------------------------------\n\nimport type { UpdateStatus } from './auto-update'\nimport type { AutoInstallOutcome } from './auto-update-auto-install'\nimport type { CompanionSpec } from './auto-update-companion'\nimport type { InstalledVersionState } from './auto-update-restart'\nimport type { Hint } from './hints'\nimport { useEffect, useRef, useState } from 'react'\nimport { checkForUpdate } from './auto-update'\nimport { autoInstallUpdate } from './auto-update-auto-install'\nimport { watchInstalledVersion } from './auto-update-restart'\n\n/** Options for {@link useUpdateCheck}. */\nexport interface UseUpdateCheckOptions {\n /** npm package name. e.g. `'zidane-tui'`. */\n packageName: string\n /** Inlined at build time. Skips the check if empty/falsy. */\n currentVersion: string\n /**\n * Master enable flag — usually `Settings.checkForUpdates`. When `false`\n * the hook short-circuits without scheduling any work AND clears the\n * last-known status so a stale chip doesn't linger after the user\n * disabled the feature.\n */\n enabled: boolean\n /** Directory under which the on-disk cache lives. Usually `paths.userDir`. */\n cacheDir?: string\n /** Registry base URL. Defaults to `https://registry.npmjs.org`. */\n registry?: string\n /** dist-tag to read. Defaults to `'latest'`. */\n channel?: string\n /**\n * Cache lifetime for the read-through. **Defaults to `0`** (always\n * fetch fresh) — the TUI chip is the user's source of truth for \"what\n * version of zidane is on npm right now\"; we'd rather pay one HTTPS\n * call per session than tell them yesterday's news for up to 24 hours.\n *\n * The on-disk cache is still WRITTEN with each fresh result (so\n * `zidane upgrade` and other tooling reads benefit) and still READ to\n * feed the registry's ETag (so the cold path can short-circuit on 304\n * — meaningful bandwidth win when nothing changed). Pass a positive\n * value (e.g. `3_600_000` for 1 h) to enable read-through caching for\n * a GUI shell that mounts the hook in many places.\n */\n cacheTtlMs?: number\n /**\n * Wall-clock delay before the first check fires. Defaults to 1 500 ms so\n * we never compete with the boot path's first paint. The check itself\n * is async, so this only affects WHEN the chip first appears — never\n * blocks anything else.\n */\n delayMs?: number\n /**\n * Check again every this many ms while mounted, so a long session still\n * learns about a release. Defaults to `0`: once per mount.\n */\n recheckMs?: number\n /** Injected for tests. */\n now?: () => number\n /** Injected for tests. */\n fetcher?: typeof fetch\n}\n\n/**\n * Returns the current update status. The check fires:\n * - Once per mount (every TUI launch).\n * - Every `recheckMs` after that, when set.\n * - Every time `enabled` flips from `false` → `true` (user re-enabling\n * the setting expects an immediate refresh).\n *\n * The check is async and deferred by `delayMs` so it never competes with\n * first paint. Result is `null` until the first check resolves; flipping\n * `enabled` to `false` resets to `null` so any chip rendered against\n * `hasUpdate` disappears immediately.\n *\n * The hook owns its own AbortController so an unmount cancels any in-flight\n * registry request cleanly.\n *\n * @param options - Package, current version, enable flag and cache settings for the check.\n */\nexport function useUpdateCheck(options: UseUpdateCheckOptions): UpdateStatus | null {\n const [status, setStatus] = useState<UpdateStatus | null>(null)\n // Ref-mirror so the effect doesn't re-run on every keystroke when the\n // host wraps the result into derived hint state.\n const optsRef = useRef(options)\n optsRef.current = options\n\n useEffect(() => {\n if (!options.enabled || !options.currentVersion) {\n // Disabling the setting must hide a chip that was already painted\n // — otherwise the user sees a stale `↑ vX.Y.Z` linger until the\n // next launch. React bails on identical-state updates so the\n // initial-mount case (status already null) costs nothing.\n setStatus(null)\n return\n }\n const controller = new AbortController()\n const check = () => {\n void checkForUpdate({\n packageName: optsRef.current.packageName,\n currentVersion: optsRef.current.currentVersion,\n cacheDir: optsRef.current.cacheDir,\n registry: optsRef.current.registry,\n channel: optsRef.current.channel,\n // Default to TTL=0 (always fetch fresh). See `cacheTtlMs` docs.\n cacheTtlMs: optsRef.current.cacheTtlMs ?? 0,\n signal: controller.signal,\n now: optsRef.current.now,\n fetcher: optsRef.current.fetcher,\n }).then((s) => {\n if (!controller.signal.aborted)\n setStatus(s)\n })\n }\n const timer = setTimeout(check, options.delayMs ?? 1500)\n const recheckMs = optsRef.current.recheckMs ?? 0\n const recheck = recheckMs > 0 ? setInterval(check, recheckMs) : undefined\n return () => {\n clearTimeout(timer)\n clearInterval(recheck)\n controller.abort()\n }\n // Re-run when the master enable / package / version flip. Other\n // option changes stay inside `optsRef.current`; re-running on every\n // option mutation would cause render storms when the host wraps\n // the result into derived hint state.\n }, [options.enabled, options.packageName, options.currentVersion])\n\n return status\n}\n\n/** Options for {@link useAutoInstallUpdate}. */\nexport interface UseAutoInstallUpdateOptions {\n /** Result of {@link useUpdateCheck}. */\n status: UpdateStatus | null\n /** `Settings.checkForUpdates && Settings.autoInstallUpdates`, and the host allows background installs. */\n enabled: boolean\n /** Where the lock, the failed record and the applied marker live. Nothing runs without it. */\n cacheDir?: string\n /** Packages to keep in step. */\n targets: readonly CompanionSpec[]\n /** Who runs the install, recorded in the lock. */\n by: string\n /** Called once per attempt with what happened. */\n onOutcome?: (outcome: AutoInstallOutcome) => void\n /** Injected for tests. Defaults to {@link autoInstallUpdate}. */\n install?: typeof autoInstallUpdate\n}\n\n/**\n * Installs the release {@link useUpdateCheck} found, in the background, when\n * the user turned `autoInstallUpdates` on. One attempt per check result; the\n * failure record decides whether a version that failed is tried again. The\n * check itself runs after first paint, so this never competes with it.\n * Never restarts anything: the applied marker wakes `useRestartRequired`.\n * Returns the last outcome, `null` before the first attempt.\n *\n * @param options - Check result, switches, cache directory and targets.\n */\nexport function useAutoInstallUpdate(options: UseAutoInstallUpdateOptions): AutoInstallOutcome | null {\n const [outcome, setOutcome] = useState<AutoInstallOutcome | null>(null)\n const optsRef = useRef(options)\n optsRef.current = options\n const tried = useRef(new Set<string>())\n const latest = options.status?.hasUpdate ? options.status.latest : null\n const checkedAt = options.status?.checkedAt ?? null\n\n useEffect(() => {\n const { status, cacheDir } = optsRef.current\n const attempt = `${latest}@${checkedAt}`\n if (!options.enabled || !cacheDir || !status || !latest || tried.current.has(attempt))\n return\n tried.current.add(attempt)\n let live = true\n // Not aborted on unmount: killing a package manager mid-install can\n // leave a broken global install behind.\n void (optsRef.current.install ?? autoInstallUpdate)({\n status,\n cacheDir,\n targets: optsRef.current.targets,\n by: optsRef.current.by,\n }).then((next) => {\n optsRef.current.onOutcome?.(next)\n if (live)\n setOutcome(next)\n })\n return () => {\n live = false\n }\n }, [options.enabled, options.cacheDir, latest, checkedAt])\n\n return outcome\n}\n\n// ---------------------------------------------------------------------------\n// Footer chip helper — pure function so any renderer (TUI Footer, future\n// GUI status bar) can compose against the same `Hint`. The chip is\n// suppressed when there's no update; callers spread the result into their\n// hint array unconditionally.\n//\n// Color contract: the chip rides whatever accent the host passes (typically\n// `theme.colors.accent`). Renderers that want a \"fresh check\" badge can\n// inspect `status.source === 'fresh'` themselves — this helper sticks to\n// the one-line discoverability surface.\n// ---------------------------------------------------------------------------\n\n/** Display options for the update and restart hints. */\nexport interface BuildUpdateHintOptions {\n /** Foreground color for the `↑` glyph + version label. */\n color: string\n /**\n * When true (default), the chip renders the literal version (`↑ v5.6.0`).\n * When false, just the glyph + `update` label — useful when the row is\n * tight and the version would push higher-priority chips off the right.\n */\n showVersion?: boolean\n /** Override the leading glyph. Defaults to `'↑'`. */\n glyph?: string\n}\n\n/**\n * Builds the `↑ vX.Y.Z` hint for an available update.\n * Returns `null` when there is no newer version.\n *\n * @param status - Result of an update check, or `null` before the first check.\n * @param options - Color and label settings for the hint.\n */\nexport function buildUpdateHint(status: UpdateStatus | null, options: BuildUpdateHintOptions): Hint | null {\n if (!status?.hasUpdate || !status.latest)\n return null\n const label = options.showVersion === false\n ? 'update'\n : `v${stripVPrefix(status.latest)}`\n return {\n key: options.glyph ?? '↑',\n keyColor: options.color,\n label,\n labelColor: options.color,\n }\n}\n\n/**\n * `↻ v7.0.31` — a newer build is on disk than the one running (this\n * process is stale; see `auto-update-restart.ts`). Takes precedence over\n * the `↑` chip: once the release is installed, \"available\" is old news and\n * \"restart\" is the one action left. Same color contract as `buildUpdateHint`.\n *\n * @param state - Installed-version state, or `null` when unknown.\n * @param options - Color and label settings for the hint.\n */\nexport function buildRestartHint(\n state: { restartRequired: boolean, installed: string | null } | null,\n options: BuildUpdateHintOptions,\n): Hint | null {\n if (!state?.restartRequired || !state.installed)\n return null\n return {\n key: options.glyph ?? '↻',\n keyColor: options.color,\n label: options.showVersion === false ? 'restart' : `v${stripVPrefix(state.installed)}`,\n labelColor: options.color,\n }\n}\n\nfunction stripVPrefix(v: string): string {\n return v.startsWith('v') ? v.slice(1) : v\n}\n\n// ---------------------------------------------------------------------------\n// `useRestartRequired` — the TUI's consumer of the installed-version watcher\n// (`./auto-update-restart`). The GUI runs the watcher in its main process and\n// forwards the state over IPC instead.\n// ---------------------------------------------------------------------------\n\n/** Options for {@link useRestartRequired}. */\nexport interface UseRestartRequiredOptions {\n /** The running package. `null` disables the hook. */\n spec: CompanionSpec | null\n /** Version the process was built with. */\n currentVersion: string\n /** Directory that holds the applied marker. Omit to rely on polling alone. */\n cacheDir?: string\n /** Master switch — mirrors `Settings.checkForUpdates`. */\n enabled: boolean\n /** Poll interval in milliseconds. Defaults to 5 minutes. `0` disables polling. */\n pollMs?: number\n}\n\n/**\n * `null` until the first probe (or when disabled / no spec), then the live\n * state. Flipping `enabled` off tears the watcher down and clears the state\n * so no stale \"restart\" chip lingers.\n *\n * @param options - Package spec, running version, cache directory, poll interval and the master `enabled` switch.\n */\nexport function useRestartRequired(options: UseRestartRequiredOptions): InstalledVersionState | null {\n const [state, setState] = useState<InstalledVersionState | null>(null)\n const { spec, currentVersion, cacheDir, enabled, pollMs } = options\n const packageName = spec?.packageName\n const binName = spec?.binName\n\n useEffect(() => {\n if (!enabled || !packageName || !currentVersion) {\n setState(null)\n return\n }\n const watcher = watchInstalledVersion({\n spec: binName ? { packageName, binName } : { packageName },\n currentVersion,\n cacheDir,\n pollMs,\n onChange: setState,\n })\n setState(watcher.current())\n return () => watcher.close()\n }, [enabled, packageName, binName, currentVersion, cacheDir, pollMs])\n\n return state\n}\n","// ---------------------------------------------------------------------------\n// The update worker: one detached process that runs an install to the end.\n//\n// A package manager killed mid-install can leave a global install broken.\n// When the TUI, the GUI or `zidane upgrade` runs the install as its own\n// child, quitting it (or a crash, or a closed terminal) can do exactly that.\n// So they start this worker instead, detached in its own session, and only\n// follow it:\n//\n// - `<cacheDir>/update.lock` is held by the worker's pid for the whole run,\n// until its result is written.\n// - `<cacheDir>/update.log` gets one line per step and the package\n// manager's output. The previous run's log is kept as `update.log.1`.\n// Each is capped at {@link UPDATE_LOG_CAP_BYTES}.\n// - `<cacheDir>/update-result.json` is the outcome, written before the lock\n// is released. The applied marker is written by the install itself.\n//\n// The request travels on argv (`--request <base64url JSON>`), versioned by\n// {@link UPDATE_WORKER_PROTOCOL}. Whatever binary runs the worker (the\n// `zidane` CLI's `__update-worker`, or the desktop app's bundled entry under\n// Electron-as-node) calls {@link runUpdateWorkerMain}.\n// ---------------------------------------------------------------------------\n\nimport type { SpawnOptions } from 'node:child_process'\nimport type { UpdateStatus } from './auto-update'\nimport type { AutoInstallOutcome, AutoInstallUpdateOptions } from './auto-update-auto-install'\nimport type { CompanionSpec } from './auto-update-companion'\nimport type { UpdateLockHolder } from './auto-update-lock'\nimport type { UpgradeProgressEvent, UpgradeReport } from './auto-update-service'\nimport { Buffer } from 'node:buffer'\nimport { spawn as nodeSpawn } from 'node:child_process'\nimport { randomUUID } from 'node:crypto'\nimport { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, readSync, renameSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { Writable } from 'node:stream'\nimport { writeJsonFileAtomic } from '../atomic-write'\nimport { clearFailedUpdate, nextFailedUpdate, runAutoInstallLocked, upgradeError, writeFailedUpdate } from './auto-update-auto-install'\nimport { acquireUpdateLock, isUpdateLockStale, readUpdateLock } from './auto-update-lock'\nimport { upgradeInstalledPackages } from './auto-update-service'\n\n/** Version of the request and result files. A worker rejects any other. */\nexport const UPDATE_WORKER_PROTOCOL = 1\n/** File name of the worker's log inside the cache directory. */\nexport const UPDATE_LOG_FILE = 'update.log'\n/** File name of the worker's result inside the cache directory. */\nexport const UPDATE_RESULT_FILE = 'update-result.json'\n/** Cap of each log file. Past it, one truncation line, then nothing. */\nexport const UPDATE_LOG_CAP_BYTES = 1024 * 1024\n\n/** Exit code of a worker that found the lock held by another install. */\nconst EXIT_LOCKED = 3\n/** Exit code of a worker whose request did not parse or has another protocol. */\nconst EXIT_BAD_REQUEST = 2\n\n/** What a worker is asked to do. */\nexport interface UpdateWorkerRequest {\n /** {@link UPDATE_WORKER_PROTOCOL}. */\n v: number\n /** Names this run in the log and the result. */\n id: string\n /** Where the lock, log, result, failure record and marker live. */\n cacheDir: string\n /** Who asked, shown to anyone who finds the lock held: `'desktop app'`. */\n by: string\n /** Packages to keep in step, in order. */\n targets: CompanionSpec[]\n /**\n * An automatic install of the release this check found: eligibility and\n * the failure record apply (`autoInstallUpdate`). Absent: a manual install,\n * which clears the failure record first.\n */\n auto?: UpdateStatus\n /** dist-tag. Defaults to `'latest'`. Manual only. */\n channel?: string\n /** Registry base URL. */\n registry?: string\n /** Install even when the registry check failed or reports up to date. Manual only. */\n force?: boolean\n /** Per package-manager run. Defaults to 10 minutes. */\n timeoutMs?: number\n /**\n * Run the host's `zidane upgrade` with these arguments instead (its own\n * checks, ledger and verification), under the worker's lock. Its stdout\n * lines go to the log as they are, its stderr lines prefixed with `! `.\n */\n upgradeArgv?: string[]\n}\n\n/** How a run ended, as its starter sees it. */\nexport type UpdateWorkerOutcome\n /** An automatic install: installed, skipped (lock held, not eligible, backing off) or failed. */\n = | AutoInstallOutcome\n /** A manual install ran: \\`error\\` says what did not land, \\`null\\` when all did. */\n | { kind: 'ran', report: UpgradeReport, error: string | null }\n /** `upgradeArgv` ran: the exit code `zidane upgrade` returned. */\n | { kind: 'exited', exitCode: number }\n /** The worker could not start or stopped without a result. */\n | { kind: 'lost', error: string }\n\n/**\n * Runs `zidane upgrade <argv>` in the worker; the host wires it (it knows its\n * package, version and companions). Must not take the install lock: the\n * worker holds it.\n */\nexport type UpdateWorkerUpgradeCommand = (argv: readonly string[], stdout: NodeJS.WritableStream, stderr: NodeJS.WritableStream) => Promise<number>\n\n/** `<cacheDir>/update-result.json`. */\nexport interface UpdateWorkerResult {\n /** {@link UPDATE_WORKER_PROTOCOL}. */\n v: number\n /** The request's id. */\n id: string\n /** The worker's pid: matches the lock holder an attached surface saw. */\n pid: number\n /** Who asked. */\n by: string\n /** ms since epoch. */\n startedAt: number\n /** ms since epoch. */\n finishedAt: number\n /** How it ended. */\n outcome: UpdateWorkerOutcome\n}\n\n/**\n * Encodes a request for the worker's argv.\n *\n * @param request - The request.\n */\nexport function encodeUpdateWorkerRequest(request: UpdateWorkerRequest): string {\n return Buffer.from(JSON.stringify(request), 'utf8').toString('base64url')\n}\n\n/**\n * Decodes `--request <value>` from argv. `null` when absent, malformed, or of\n * another protocol version.\n *\n * @param argv - Arguments after the worker's own command.\n */\nexport function decodeUpdateWorkerRequest(argv: readonly string[]): UpdateWorkerRequest | null {\n const at = argv.indexOf('--request')\n const raw = at === -1 ? undefined : argv[at + 1]\n if (!raw)\n return null\n try {\n const parsed = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8')) as Partial<UpdateWorkerRequest>\n if (parsed.v !== UPDATE_WORKER_PROTOCOL || typeof parsed.id !== 'string' || typeof parsed.cacheDir !== 'string'\n || typeof parsed.by !== 'string' || !Array.isArray(parsed.targets)) {\n return null\n }\n return parsed as UpdateWorkerRequest\n }\n catch {\n return null\n }\n}\n\n/**\n * Reads the last worker result. `null` when there is none, it does not parse,\n * or it has another protocol version.\n *\n * @param cacheDir - Directory that holds the result.\n */\nexport function readUpdateWorkerResult(cacheDir: string): UpdateWorkerResult | null {\n try {\n const parsed = JSON.parse(readFileSync(join(cacheDir, UPDATE_RESULT_FILE), 'utf8')) as Partial<UpdateWorkerResult>\n if (parsed.v !== UPDATE_WORKER_PROTOCOL || typeof parsed.id !== 'string' || typeof parsed.pid !== 'number' || !parsed.outcome)\n return null\n return parsed as UpdateWorkerResult\n }\n catch {\n return null\n }\n}\n\n/** Injected for tests. */\nexport interface RunUpdateWorkerDeps {\n /** Defaults to {@link upgradeInstalledPackages}. */\n upgrade?: typeof upgradeInstalledPackages\n /** Defaults to `Date.now`. */\n now?: () => number\n /** Read for the env opt-outs of an automatic run (`CI`, `ZIDANE_NO_UPDATE`). Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv\n /** Defaults to `process.pid`. */\n pid?: number\n /** Runs `request.upgradeArgv`. Without it such a request ends `lost`. */\n upgradeCommand?: UpdateWorkerUpgradeCommand\n}\n\n/**\n * Runs one install inside the worker process: takes the lock under this pid,\n * logs every step, writes the result, then releases the lock. Never throws.\n * `null` when another install holds the lock (nothing is written).\n *\n * @param request - What to install.\n * @param deps - Test seams.\n */\nexport async function runUpdateWorker(request: UpdateWorkerRequest, deps: RunUpdateWorkerDeps = {}): Promise<UpdateWorkerResult | null> {\n const now = deps.now ?? Date.now\n const pid = deps.pid ?? process.pid\n const { cacheDir } = request\n const lock = acquireUpdateLock(cacheDir, request.by, { pid })\n if (!lock.acquired)\n return null\n const startedAt = now()\n const log = openLog(cacheDir)\n log(`# zidane update ${request.id} by ${request.by} (pid ${pid}) ${new Date(startedAt).toISOString()}`)\n const upgradeOptions = {\n onProgress: (event: UpgradeProgressEvent) => log(progressLine(event)),\n ...(request.registry ? { registry: request.registry } : {}),\n ...(request.timeoutMs !== undefined ? { timeoutMs: request.timeoutMs } : {}),\n }\n const upgrade = deps.upgrade ?? upgradeInstalledPackages\n let outcome: UpdateWorkerOutcome\n try {\n if (request.upgradeArgv) {\n outcome = deps.upgradeCommand\n ? { kind: 'exited', exitCode: await deps.upgradeCommand(request.upgradeArgv, lineWriter(log, ''), lineWriter(log, '! ')) }\n : { kind: 'lost', error: 'this worker cannot run zidane upgrade' }\n }\n else if (request.auto) {\n outcome = await runAutoInstallLocked({ status: request.auto, cacheDir, targets: request.targets, by: request.by, upgradeOptions, upgrade, now, ...(deps.env ? { env: deps.env } : {}) })\n }\n else {\n // A manual install is the retry for a failed automatic one.\n clearFailedUpdate(cacheDir)\n const report = await upgrade({\n ...upgradeOptions,\n targets: request.targets,\n cacheDir,\n ...(request.channel ? { channel: request.channel } : {}),\n ...(request.force ? { force: true } : {}),\n })\n outcome = { kind: 'ran', report, error: upgradeError(report) }\n }\n }\n catch (err) {\n outcome = { kind: 'lost', error: err instanceof Error ? err.message : String(err) }\n }\n log(`# ${outcomeLine(outcome)}`)\n const result: UpdateWorkerResult = { v: UPDATE_WORKER_PROTOCOL, id: request.id, pid, by: request.by, startedAt, finishedAt: now(), outcome }\n try {\n writeJsonFileAtomic(join(cacheDir, UPDATE_RESULT_FILE), result, { ensureDir: true })\n }\n catch {\n // The starter sees the exit without a result and reports the run lost.\n }\n lock.release()\n return result\n}\n\n/**\n * Entry of the worker process. Returns the exit code: `0` once a result is\n * written, `3` when another install holds the lock, `2` for a bad request.\n *\n * @param argv - Arguments after the worker's own command (`--request <value>`).\n * @param deps - Overrides for tests: the `upgradeCommand` to run.\n */\nexport async function runUpdateWorkerMain(argv: readonly string[], deps: Pick<RunUpdateWorkerDeps, 'upgradeCommand'> = {}): Promise<number> {\n const request = decodeUpdateWorkerRequest(argv)\n if (!request)\n return EXIT_BAD_REQUEST\n const result = await runUpdateWorker(request, deps)\n return result ? 0 : EXIT_LOCKED\n}\n\n/** Options for {@link startUpdateWorker}. */\nexport interface StartUpdateWorkerOptions {\n /** Argv that runs the worker; `--request <value>` is appended. E.g. `['/usr/local/bin/zidane', '__update-worker']`. */\n command: readonly string[]\n /** Worker environment (PATH decides which package managers it finds). Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv\n /** What to install. */\n request: Omit<UpdateWorkerRequest, 'v' | 'id'>\n /** Each line the worker logs for this run. */\n onLine?: (line: string) => void\n /** Log and result poll interval. Defaults to 250 ms. */\n pollMs?: number\n /** Injected for tests. */\n spawn?: typeof nodeSpawn\n}\n\n/**\n * Starts the worker detached and follows it until it reports. The worker\n * keeps going when this process exits. Never rejects.\n *\n * A held lock answers `skipped / locked` without starting anything. A worker\n * that cannot start, or stops without a result, answers `lost`; for an\n * automatic install that also records the version as a transient failure (in\n * memory when the cache dir can't be written), so it is retried on the\n * backoff, not on every check.\n *\n * @param options - Worker command, environment, request and log callback.\n */\nexport async function startUpdateWorker(options: StartUpdateWorkerOptions): Promise<UpdateWorkerOutcome> {\n const { cacheDir } = options.request\n const held = liveHolder(cacheDir)\n if (held)\n return { kind: 'skipped', reason: 'locked', holder: held }\n const request: UpdateWorkerRequest = { ...options.request, v: UPDATE_WORKER_PROTOCOL, id: randomUUID() }\n const [bin, ...args] = options.command\n const exit = new Promise<{ code: number | null, error?: string }>((resolve) => {\n try {\n const child = (options.spawn ?? nodeSpawn)(bin!, [...args, '--request', encodeUpdateWorkerRequest(request)], {\n detached: true,\n stdio: 'ignore',\n env: options.env ?? process.env,\n } satisfies SpawnOptions)\n child.once('error', err => resolve({ code: null, error: err.message }))\n child.once('exit', code => resolve({ code }))\n child.unref()\n }\n catch (err) {\n resolve({ code: null, error: err instanceof Error ? err.message : String(err) })\n }\n })\n const outcome = await follow({\n cacheDir,\n pollMs: options.pollMs,\n onLine: options.onLine,\n header: request.id,\n done: async () => {\n const result = readUpdateWorkerResult(cacheDir)\n return result?.id === request.id ? result.outcome : null\n },\n exit,\n })\n if (outcome.kind === 'lost' && request.auto?.latest) {\n const version = request.auto.latest\n writeFailedUpdate(cacheDir, nextFailedUpdate(cacheDir, { version, error: outcome.error, transient: true, at: Date.now() }))\n }\n return outcome\n}\n\n/**\n * An `autoInstallUpdate`-shaped function that runs the install in the\n * detached worker, for `useAutoInstallUpdate`'s `install` seam. A lost\n * worker reads as a failed install.\n *\n * @param command - Argv that runs the worker.\n * @param extra - Worker environment and log callback.\n * @param extra.env - Worker environment. Defaults to `process.env`.\n * @param extra.onLine - Each line the worker logs.\n */\nexport function autoInstallViaWorker(command: readonly string[], extra: { env?: NodeJS.ProcessEnv, onLine?: (line: string) => void } = {}): (options: AutoInstallUpdateOptions) => Promise<AutoInstallOutcome> {\n return async (options) => {\n const outcome = await startUpdateWorker({\n command,\n ...extra,\n request: { cacheDir: options.cacheDir, by: options.by, targets: [...options.targets], auto: options.status },\n })\n if (outcome.kind === 'installed' || outcome.kind === 'skipped' || outcome.kind === 'failed')\n return outcome\n const error = outcome.kind === 'lost' ? outcome.error : 'unexpected result from the update worker'\n return { kind: 'failed', version: options.status.latest ?? '', error }\n }\n}\n\n/** Options for {@link followRunningUpdate}. */\nexport interface FollowRunningUpdateOptions {\n /** Directory that holds the lock, log and result. */\n cacheDir: string\n /** Each line logged from now on, starting with the run's lines so far. */\n onLine?: (line: string) => void\n /** Defaults to 250 ms. */\n pollMs?: number\n}\n\n/**\n * Attaches to an install that is already running (a surface that starts while\n * another one's worker installs). `null` when no live install holds the lock.\n * Otherwise resolves once the lock is released: the worker's result, or\n * `null` when the holder was not a worker (`zidane upgrade --in-place`) or\n * left no result.\n *\n * @param options - Cache directory, log callback and poll interval.\n */\nexport function followRunningUpdate(options: FollowRunningUpdateOptions): Promise<UpdateWorkerResult | null> | null {\n const holder = liveHolder(options.cacheDir)\n if (!holder)\n return null\n return (async () => {\n let result: UpdateWorkerResult | null = null\n await follow({\n cacheDir: options.cacheDir,\n pollMs: options.pollMs,\n onLine: options.onLine,\n done: async () => {\n if (liveHolder(options.cacheDir))\n return null\n const last = readUpdateWorkerResult(options.cacheDir)\n result = last?.pid === holder.pid && last.startedAt >= holder.startedAt ? last : null\n return result?.outcome ?? { kind: 'lost', error: 'no result' }\n },\n })\n return result\n })()\n}\n\n/** The holder of a lock that still protects a running install, or `null`. */\nfunction liveHolder(cacheDir: string): UpdateLockHolder | null {\n const holder = readUpdateLock(cacheDir)\n return holder && !isUpdateLockStale(holder) ? holder : null\n}\n\n/**\n * Tail the log and poll `done` until it answers. With `exit`, a worker that\n * exits without `done` answering is `lost` (or `skipped / locked` for exit 3).\n * With `header`, lines before `# zidane update <header>` belong to another\n * run and are skipped.\n */\nasync function follow(input: {\n cacheDir: string\n pollMs?: number\n onLine?: (line: string) => void\n header?: string\n done: () => Promise<UpdateWorkerOutcome | null>\n exit?: Promise<{ code: number | null, error?: string }>\n}): Promise<UpdateWorkerOutcome> {\n const pollMs = input.pollMs ?? 250\n const tail = logTail(join(input.cacheDir, UPDATE_LOG_FILE), input.header)\n let exited: { code: number | null, error?: string } | null = null\n void input.exit?.then((e) => {\n exited = e\n })\n while (true) {\n for (const line of tail())\n input.onLine?.(line)\n const outcome = await input.done()\n if (outcome) {\n for (const line of tail())\n input.onLine?.(line)\n return outcome\n }\n if (exited) {\n // One last look: the result is written before the worker exits.\n const late = await input.done()\n if (late)\n return late\n const { code, error } = exited as { code: number | null, error?: string }\n if (code === EXIT_LOCKED) {\n const holder = readUpdateLock(input.cacheDir)\n return holder ? { kind: 'skipped', reason: 'locked', holder } : { kind: 'lost', error: 'another update held the lock' }\n }\n return { kind: 'lost', error: error ? `could not start the update: ${error}` : `the update stopped (exit ${code ?? 'signal'}) without a result` }\n }\n await new Promise(resolve => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * Reads lines appended to `path` since the last call. A file that shrank or\n * was replaced (rotated) is read again from the start. A half-written last\n * line waits for its newline.\n */\nfunction logTail(path: string, header?: string): () => string[] {\n let offset = 0\n let ino = -1\n let pending = ''\n let started = header === undefined\n return () => {\n let stat\n try {\n stat = statSync(path)\n }\n catch {\n return []\n }\n if (stat.ino !== ino || stat.size < offset) {\n ino = stat.ino\n offset = 0\n pending = ''\n }\n if (stat.size === offset)\n return []\n const buf = Buffer.alloc(stat.size - offset)\n let fd: number | undefined\n try {\n fd = openSync(path, 'r')\n const read = readSync(fd, buf, 0, buf.length, offset)\n offset += read\n pending += buf.subarray(0, read).toString('utf8')\n }\n catch {\n return []\n }\n finally {\n if (fd !== undefined)\n closeSync(fd)\n }\n const parts = pending.split('\\n')\n pending = parts.pop() ?? ''\n const lines: string[] = []\n for (const line of parts) {\n if (!started) {\n started = line.startsWith(`# zidane update ${header} `)\n if (!started)\n continue\n }\n lines.push(line)\n }\n return lines\n }\n}\n\n/** Rotate the previous log away and return an appender capped at {@link UPDATE_LOG_CAP_BYTES}. */\nfunction openLog(cacheDir: string): (line: string) => void {\n const path = join(cacheDir, UPDATE_LOG_FILE)\n try {\n mkdirSync(cacheDir, { recursive: true })\n renameSync(path, `${path}.1`)\n }\n catch {\n // No previous log, or the dir can't be written: logging is best-effort.\n }\n let written = 0\n let full = false\n return (line) => {\n if (full)\n return\n const text = `${line}\\n`\n const size = Buffer.byteLength(text)\n try {\n if (written + size > UPDATE_LOG_CAP_BYTES) {\n full = true\n appendFileSync(path, '# log truncated\\n')\n return\n }\n appendFileSync(path, text)\n written += size\n }\n catch {\n // Best-effort.\n }\n }\n}\n\n/** A stream that logs each complete line, prefixed. */\nfunction lineWriter(log: (line: string) => void, prefix: string): NodeJS.WritableStream {\n let buffer = ''\n const flush = (final: boolean) => {\n const parts = buffer.split(/\\r?\\n/)\n buffer = final ? '' : parts.pop() ?? ''\n for (const part of parts) {\n if (part || !final)\n log(`${prefix}${part}`)\n }\n }\n return new Writable({\n write(chunk: Uint8Array | string, _enc, cb) {\n buffer += chunk.toString()\n flush(false)\n cb()\n },\n final(cb) {\n flush(true)\n cb()\n },\n })\n}\n\nfunction progressLine(event: UpgradeProgressEvent): string {\n switch (event.kind) {\n case 'check': return `checking ${event.packageName}`\n case 'install': return `${event.packageName} ${event.from} → ${event.to}: $ ${event.command.join(' ')}`\n case 'log': return event.line\n case 'done': return `${event.result.packageName}: ${event.result.status}${event.result.error ? ` (${event.result.error})` : ''}`\n }\n}\n\nfunction outcomeLine(outcome: UpdateWorkerOutcome): string {\n switch (outcome.kind) {\n case 'installed': return `installed ${outcome.version}`\n case 'skipped': return `skipped: ${outcome.reason}`\n case 'failed': return `failed: ${outcome.error.replaceAll('\\n', '; ')}`\n case 'ran': return outcome.error ? `failed: ${outcome.error.replaceAll('\\n', '; ')}` : 'done'\n case 'exited': return `exit ${outcome.exitCode}`\n case 'lost': return `failed: ${outcome.error}`\n }\n}\n","// ---------------------------------------------------------------------------\n// Footer hint composition.\n//\n// Pure presentation logic — given the current screen, run state, and a\n// resolved keybinding map, produce the ordered list of {@link Hint}s\n// the bottom-bar Footer should render. Lives in `zidane/chat` (not\n// `zidane/tui`) because it's renderer-agnostic: the only types involved\n// are `Hint`, `Screen`, `SessionMeta`, `Settings['uiMode']`, and `KeyBindings`. A GUI\n// shell can consume the same list and paint it however it likes.\n//\n// Priority order is intentional and load-bearing:\n//\n// pending approval > live pending interaction > resumed pending\n// interaction > busy (streaming) > auth screen > onboarding screen (no\n// hints) > sessions screen > chat screen (idle).\n//\n// The first four return state-specific hint sets unconditionally —\n// `uiMode` is ignored there because those affordances (approve / abort\n// / navigate) are not the noise the minimal mode is targeting. Minimal\n// mode only collapses the chat-idle row.\n// ---------------------------------------------------------------------------\n\nimport type { Hint } from './hints'\nimport type { KeyBindings } from './keybindings'\nimport type { Screen, SessionMeta, Settings } from './types'\nimport { formatBindingForDisplay } from './keybindings'\n\n/**\n * Options bag for {@link buildHints}. Flat shape so call sites read as a\n * checklist of \"what state do I have to surface\" — easy to thread\n * through React `useMemo` deps and to construct fresh in tests.\n */\nexport interface BuildHintsOptions {\n /** Screen the footer is shown on. */\n screen: Screen\n /** True while the agent is running a turn. */\n busy: boolean\n /** True while a safe-mode approval prompt is pending. */\n pending: boolean\n /**\n * True when a LIVE pending interaction is on screen (agent is mid-run,\n * awaiting the tool's Promise). Same nav hints as the safe-mode\n * approval picker — `abortRun` aborts the run.\n */\n pendingInteractionLive: boolean\n /**\n * True when the chat screen is showing a resumed (not live) pending\n * interaction. The agent isn't running; esc leaves the form without\n * persisting a response, so the hint reads \"leave for later\" — the\n * session re-enqueues the same interaction on next activation.\n */\n pendingInteractionResumed: boolean\n /** Active session, or `null` when none is open. */\n currentSession: SessionMeta | null\n /** True when more than one agent profile is available, so the agent switch hint is useful. */\n hasMultipleAgents: boolean\n /**\n * Chat-screen chrome density — `'minimal'` collapses the idle chat\n * hint row to just `agent / model / settings`. Busy and pending\n * states keep their full hint sets because those\n * affordances are load-bearing (abort, navigate, approve).\n */\n uiMode: Settings['uiMode']\n /** Label of the active model, or `null` when none is selected. */\n modelLabel: string | null\n /** Foreground color for the model label. */\n modelColor: string\n /** Current reasoning effort label, or `null` when the model has no reasoning knob. */\n effortLabel: string | null\n /** Foreground color for the effort label. */\n effortColor: string\n /**\n * Foreground for the `/l` chord that introduces the effort label. Unset, it\n * takes the color of the model key it extends, so the pair reads as one binding.\n */\n effortKeyColor?: string\n /**\n * Comma-joined labels of the enabled model options (e.g. `'fast'`), or `null`\n * when none are on. Appended after the effort label in the model chord — when\n * the model has no reasoning knob, it rides the chord on its own.\n */\n modelOptionsLabel?: string | null\n /** Label of the active agent profile. */\n agentLabel: string\n /** Foreground color for the agent label. */\n agentColor: string\n /**\n * Resolved keybindings — every hint key string flows through this so\n * a user-level rebind (e.g. `\"openSettings\": \"ctrl+p\"`) shows up live\n * in the bottom bar without us hard-coding the default `'ctrl+o'`.\n */\n keybindings: KeyBindings\n /**\n * Number of currently-dispatching tool calls. Drives the\n * `<cancelToolCall> cancel` chip in the `busy` hint row and the\n * `cancel task` chip on the idle chat row. Without\n * it, the binding stays inert and there's no point advertising the\n * shortcut. Falls back to \"no chip\" when no tools are in flight\n * (model is mid-stream but hasn't dispatched yet).\n */\n inFlightToolCount: number\n /**\n * Number of currently-active skills. Drives the footer's\n * `✦ N skill(s)` chip on the chat screen — purely informational,\n * a passive surface for \"tool restrictions are in effect\". Hidden\n * when `0` so the row stays uncluttered for the common case.\n */\n activeSkillCount: number\n /** Foreground for the skills chip (typically `COLOR.brand`). */\n skillsChipColor: string\n /**\n * Progress label for the todo chip (`\"2/5\"` from `todoProgressLabel`), or\n * `null` to hide it.\n *\n * As a `Hint` it inherits the row's click-to-keypress\n * contract for free (`dispatchHintKey`), so clicking it opens the list the\n * same way `ctrl+t` does — and it sits below the prompt like every other\n * control.\n */\n todoProgress: string | null\n /**\n * Pre-built `↑ vX.Y.Z` chip from `buildUpdateHint(useUpdateCheck())`,\n * or `null` when no update is available. Always last in the chat-screen\n * row so it's the first to drop when the terminal narrows.\n */\n updateHint: Hint | null\n /**\n * Pre-built prompt-cache toast, non-null only for a few seconds after a\n * `'cache:break'` hook fired (the host owns the visibility timer and the\n * severity split): warn-colored `⚡ cache break in <culprit>: +Nk tok, ~$0.48`\n * for attributable breaks, dim `⚡ cache expired: +Nk tok, ~$0.48` for\n * byte-stable TTL expiry — routine across human think-time, so it must not\n * cry wolf. Sits beside `updateHint` at the droppable end of the row: a\n * transient cost signal, not an action.\n */\n cacheBreakHint?: Hint | null\n /**\n * Sessions-screen list scope — `true` when the list spans all\n * projects. Drives the `tab` hint's label, which advertises the scope\n * the toggle switches TO. Optional so non-sessions callers can omit it.\n */\n sessionsScopeAll?: boolean\n /** Sessions screen: there is another space to switch to. Adds the `cycleSpaceInList` hint (`shift+tab space`; `←→ space` while unbound). */\n sessionsSpaceSwitch?: boolean\n}\n\n/**\n * Build the footer's shortcut hints for the current screen. On the chat\n * screen the model id rides next to its `ctrl+m` shortcut and the agent\n * label rides next to `shift+tab`, each in its accent color — the bar\n * doubles as the status display without needing separate badges. When\n * the active model exposes reasoning, the `ctrl+m` hint grows a\n * secondary `/l` chord with the current effort label, surfacing the\n * effort picker as a discoverable, in-place affordance.\n *\n * @param options - Screen, run state, labels, colors, and keybindings that decide which hints appear.\n */\nexport function buildHints(options: BuildHintsOptions): Hint[] {\n const {\n screen,\n busy,\n pending,\n pendingInteractionLive,\n pendingInteractionResumed,\n currentSession,\n hasMultipleAgents,\n uiMode,\n modelLabel,\n modelColor,\n effortLabel,\n effortColor,\n effortKeyColor,\n modelOptionsLabel,\n agentLabel,\n agentColor,\n keybindings,\n inFlightToolCount,\n activeSkillCount,\n skillsChipColor,\n todoProgress,\n updateHint,\n cacheBreakHint = null,\n sessionsScopeAll,\n sessionsSpaceSwitch,\n } = options\n\n // Todo progress chip. Built BEFORE the priority branches below because it is\n // STATUS, not a shortcut: it has to survive every one of them. It reports\n // todo progress independent of `busy`, and an `in_progress` todo is set\n // precisely while the agent is working and calling `todowrite`. Building\n // it after the early returns would hide it for the whole window it exists\n // to cover.\n //\n // Appended LAST in each branch so `clipHintsToWidth` (which drops from the\n // right) sheds it before the abort/navigate affordances, which must stay.\n const todosChip: Hint | null = todoProgress !== null\n ? { key: keybindings.openTodos, label: `${todoProgress} todos` }\n : null\n const withTodos = (hints: Hint[]): Hint[] => todosChip ? [...hints, todosChip] : hints\n\n // Priority: safe-mode approval > live interaction > resumed\n // interaction > plain streaming. All three pending shapes share the\n // ↑↓ / ↵ nav prefix; only the esc semantics differ.\n if (pending)\n return withTodos([{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: formatBindingForDisplay(keybindings.abortRun), label: 'abort run' }])\n if (pendingInteractionLive)\n return withTodos([{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: formatBindingForDisplay(keybindings.abortRun), label: 'abort run' }])\n if (pendingInteractionResumed)\n return withTodos([{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'leave for later' }])\n if (busy) {\n // The run-wide abort, then the way out. The per-call cancel\n // (`cancelToolCall`) stays bound but unadvertised mid-run: the abort\n // covers the common case, and the chip only crowded the row.\n const baseBusyHints: Hint[] = []\n baseBusyHints.push({ key: formatBindingForDisplay(keybindings.abortRun), label: 'abort' })\n baseBusyHints.push({ key: 'esc', label: 'sessions' })\n // The model stays in view during the run it serves (a fallback shows in\n // its warn colour exactly then); no key, since the picker waits for idle.\n if (modelLabel)\n baseBusyHints.push({ key: '', label: modelLabel, labelColor: modelColor })\n return withTodos(baseBusyHints)\n }\n if (screen === 'auth')\n return [{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'exit' }]\n // The setup wizard shows the keys of each step inside its frame.\n if (screen === 'onboarding')\n return []\n if (screen === 'sessions') {\n return [\n { key: '↑↓', label: 'navigate' },\n { key: '↵', label: 'open' },\n { key: 'tab', label: sessionsScopeAll ? 'current project' : 'all projects' },\n ...(sessionsSpaceSwitch ? [{ key: keybindings.cycleSpaceInList || '←→', label: 'space' }] : []),\n { key: keybindings.openSessionDetails, label: 'session' },\n { key: keybindings.openSettings, label: 'settings' },\n { key: 'esc', label: currentSession ? 'back' : 'exit' },\n ]\n }\n // Chat screen. Prompt-input shortcuts (`↵ send`, `shift+↵ newline`,\n // `↑↓ history`) live in the prompt box's overlay title — the bottom\n // bar only carries the most common agent / model / global affordances. The\n // `cycleAgent` binding shows the active label inline; the\n // `openModelPicker` binding shows the active model id inline; etc.\n // `cycleAgent` is suppressed when there's only one profile and\n // `openSessionDetails` only renders once a session exists.\n //\n // Minimal UI mode: strip the chat-idle bottom bar to just `agent`\n // (when there's >1 profile), `model/effort`, and `settings`. The\n // full keybinding catalog stays reachable from inside settings.\n // Busy / pending / select-turn / queue branches above already exited\n // before this point — minimal only affects the resting chat row.\n //\n // Compose the model hint. When the active model supports reasoning,\n // the primary `<openModelPicker> model` pair grows a secondary\n // `/l effort` chord that doubles as the discoverability hint for the\n // effort picker (`openEffortPicker`) and the status display for the\n // current effort. The chord shares the model accent so the pair\n // reads as one keyboard binding across every theme.\n // The `/l` chord (from `openEffortPicker`) carries reasoning effort plus any enabled model options,\n // all space-separated so they read as one model-info run (e.g.\n // `claude-opus-4-8 medium fast`). When the model has no reasoning knob, the\n // options ride the chord on their own.\n const chordLabel = [effortLabel, modelOptionsLabel].filter(Boolean).join(' ') || null\n const modelHint: Hint | null = modelLabel\n ? {\n key: keybindings.openModelPicker,\n label: modelLabel,\n labelColor: modelColor,\n ...(chordLabel\n ? { extra: { key: shortChord(keybindings.openEffortPicker), keySpec: keybindings.openEffortPicker, ...(effortKeyColor ? { keyColor: effortKeyColor } : {}), label: chordLabel, labelColor: effortColor } }\n : {}),\n }\n : null\n // Skills chip — purely informational, no shortcut wired. We re-use\n // the Hint shape's `key` slot to render the glyph (`✦`) so the row\n // layout (key + label, padded breakpoints) stays consistent with\n // every other entry. The chip is omitted when no skills are active\n // so the row stays uncluttered for the common case.\n const skillsChip: Hint | null = activeSkillCount > 0\n ? {\n key: '✦',\n keyColor: skillsChipColor,\n label: activeSkillCount === 1 ? '1 skill' : `${activeSkillCount} skills`,\n labelColor: skillsChipColor,\n }\n : null\n // Background-task chip — fires when the user is idle (not `busy`)\n // but a task is still running. Gives them a discoverable way to\n // reach `ctrl+k` for kill. The `busy` branch above already covers\n // the dispatching case; this one is for \"agent finished its turn,\n // task still chugging\".\n const cancelTaskChip: Hint | null = inFlightToolCount > 0\n ? {\n key: keybindings.cancelToolCall,\n label: inFlightToolCount === 1 ? 'cancel task' : `cancel task (${inFlightToolCount})`,\n }\n : null\n // Minimal mode short-circuits the full composition. Same agent / model\n // hints as the full row (so a multi-agent setup still cycles, and the\n // model + effort chord stays visible), plus `settings` as the\n // discoverability anchor for everything we just dropped (keybindings\n // included — they have a tab in there).\n if (uiMode === 'minimal') {\n return [\n ...(hasMultipleAgents ? [{ key: keybindings.cycleAgent, label: agentLabel, labelColor: agentColor }] : []),\n ...(modelHint ? [modelHint] : []),\n { key: keybindings.openSettings, label: 'settings' },\n ]\n }\n return [\n ...(hasMultipleAgents ? [{ key: keybindings.cycleAgent, label: agentLabel, labelColor: agentColor }] : []),\n ...(modelHint ? [modelHint] : []),\n ...(skillsChip ? [skillsChip] : []),\n ...(todosChip ? [todosChip] : []),\n ...(cancelTaskChip ? [cancelTaskChip] : []),\n ...(currentSession ? [{ key: keybindings.openSessionDetails, label: 'session' }] : []),\n { key: keybindings.openSettings, label: 'settings' },\n { key: 'esc', label: 'sessions' },\n // Update chip lives at the right-most position so `clipHintsToWidth`\n // drops it first on narrow terminals — discoverability is nice but\n // the action shortcuts must stay.\n ...(cacheBreakHint ? [cacheBreakHint] : []),\n ...(updateHint ? [updateHint] : []),\n ]\n}\n\n/**\n * Shorten a binding spec for display as a \"chord continuation\" — the\n * `/l` after `ctrl+m model` in the chat footer. We only strip the\n * `ctrl+` prefix so user-customized chords (`alt+n`, `meta+shift+n`)\n * still render in full. Falls back to the verbatim spec when no\n * `ctrl+` prefix is found, which keeps the visual contract honest:\n * the rendered key always matches the bound trigger.\n *\n * @param spec - Binding spec, such as `ctrl+n`.\n */\nexport function shortChord(spec: string): string {\n return spec.startsWith('ctrl+') ? `/${spec.slice('ctrl+'.length)}` : spec\n}\n","/**\n * @-completion path display formatter.\n *\n * The `@`-popover walks the project from the git root so the user sees\n * the whole tree regardless of where they launched the TUI. The\n * agent's CWD-resolving tools (`read_file`, `glob`, `edit`, …) read\n * relative paths against `process.cwd()`, which is frequently a\n * subdirectory of that root. Emitting the discovery's raw\n * project-root-relative path into the prompt produces a `<cwd>/<rel>`\n * miss — the bug this helper exists to close.\n *\n * Rule:\n *\n * - File at `cwd` itself → `.`.\n * - File under `cwd` (subtree) → forward-slashed CWD-relative,\n * no `..`.\n * - File above `cwd` but still inside the project → CWD-relative\n * with `..` traversal (e.g. `../EDIT_THIS.md`). Shorter and more\n * scannable than absolute, and the agent's tools resolve both.\n * - File outside the project (input was passed absolute) → returned\n * verbatim. Inputs that arrive as project-root-relative strings\n * are always inside the project by construction, so this branch\n * only kicks in when a direct caller hands the function an\n * absolute target.\n *\n * Pure, no I/O, deterministic.\n */\n\nimport { isAbsolute, posix, relative, resolve, sep } from 'node:path'\n\n/**\n * Convert a project-root-relative path (as emitted by `listProjectFiles`)\n * into the form the agent's tools — and the user — will actually find\n * given the current `cwd`.\n *\n * Inputs:\n * - `projectRelativePath` — forward-slashed, no leading slash. Returned\n * verbatim when it's empty or already absolute (defensive — the\n * absolute-input branch is the de-facto \"outside the project\"\n * escape; the catalog itself never emits absolute paths).\n * - `projectRoot` — absolute path of the discovery anchor.\n * - `cwd` — absolute path of `process.cwd()` at the moment of\n * insertion.\n *\n * Output is always either an absolute path (only when the input arrived\n * absolute) OR a CWD-relative path. The relative form may include\n * `../` segments when the target sits above `cwd` within the project,\n * which keeps cross-tree references short and scannable\n * (e.g. `../EDIT_THIS.md` instead of `/Users/.../zidane/EDIT_THIS.md`).\n *\n * @param projectRelativePath - Forward-slashed path relative to `projectRoot`.\n * @param projectRoot - Absolute path of the discovery anchor.\n * @param cwd - Absolute working directory the result is relative to.\n */\nexport function formatPathForCwd(\n projectRelativePath: string,\n projectRoot: string,\n cwd: string,\n): string {\n if (projectRelativePath.length === 0)\n return projectRelativePath\n if (isAbsolute(projectRelativePath))\n return projectRelativePath\n const target = resolve(projectRoot, projectRelativePath)\n const here = resolve(cwd)\n if (target === here)\n return '.'\n const rel = relative(here, target)\n if (rel.length === 0)\n return '.'\n return sep === '/' ? rel : rel.split(sep).join(posix.sep)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"path-display-ZIWdFaTI.js","names":["nodeSpawn"],"sources":["../src/chat/artifacts-view.ts","../src/chat/auto-update-service.ts","../src/chat/auto-update-auto-install.ts","../src/chat/auto-update-hook.ts","../src/chat/auto-update-worker.ts","../src/chat/footer-hints.ts","../src/chat/path-display.ts"],"sourcesContent":["/**\n * Renderer-agnostic shaping of session artifacts for display — grouping\n * and row presentation. Shared by the TUI rail + modal and the GUI rail, so\n * every surface agrees on what an artifact looks like.\n */\n\nimport type { ExtensionArtifactRow, ExtensionRegistry } from '../extensions'\nimport type { SessionArtifact } from '../session'\nimport { artifactKindLabel, renderArtifactRow } from '../extensions/builtin-artifact-kinds'\n\n/** Whether artifacts are listed for the current session or the whole project. */\nexport type ArtifactScope = 'session' | 'project'\n\n/** One artifact paired with its rendered row. */\nexport interface ArtifactViewRow {\n /** The session artifact. */\n artifact: SessionArtifact\n /** Rendered row for the artifact. */\n row: ExtensionArtifactRow\n}\n\n/** Artifacts of one kind, ready to display. */\nexport interface ArtifactViewGroup {\n /** Artifact kind id. */\n kind: string\n /** Human kind label (`Pull request`), raw kind when unregistered. */\n label: string\n /** Rows of this kind, in the store's order. */\n rows: ArtifactViewRow[]\n}\n\ntype KindRegistry = Pick<ExtensionRegistry, 'artifactKinds'> | undefined\n\n/** Display order for the built-in kinds; anything else follows alphabetically. */\nconst KIND_ORDER = ['pull_request', 'branch', 'commit', 'file']\n\n/**\n * Group by kind (built-ins first in a fixed order, then extension kinds\n * alphabetically); rows inside a group keep the store's newest-first order.\n *\n * @param artifacts - Session artifacts to group.\n * @param registry - Extension registry that supplies kind labels and row renderers.\n */\nexport function groupArtifacts(artifacts: readonly SessionArtifact[], registry?: KindRegistry): ArtifactViewGroup[] {\n const byKind = new Map<string, ArtifactViewRow[]>()\n for (const artifact of artifacts) {\n const list = byKind.get(artifact.kind) ?? []\n list.push({ artifact, row: renderArtifactRow(artifact, registry) })\n byKind.set(artifact.kind, list)\n }\n const kinds = [...byKind.keys()].sort((a, b) => {\n const ai = KIND_ORDER.indexOf(a)\n const bi = KIND_ORDER.indexOf(b)\n if (ai !== -1 || bi !== -1)\n return (ai === -1 ? KIND_ORDER.length : ai) - (bi === -1 ? KIND_ORDER.length : bi)\n return a.localeCompare(b)\n })\n return kinds.map(kind => ({ kind, label: artifactKindLabel(kind, registry), rows: byKind.get(kind)! }))\n}\n\n/**\n * One count per kind, a badge each: `['3 pull requests', '2 files']`; empty\n * for none.\n *\n * @param artifacts - Session artifacts to count.\n * @param registry - Extension registry that supplies kind labels.\n */\nexport function summarizeArtifactCounts(artifacts: readonly SessionArtifact[], registry?: KindRegistry): string[] {\n return groupArtifacts(artifacts, registry)\n .map(g => `${g.rows.length} ${pluralizeKind(g.label, g.rows.length)}`)\n}\n\nfunction pluralizeKind(label: string, count: number): string {\n const lower = label.toLowerCase()\n if (count === 1)\n return lower\n return /(?:s|x|ch|sh)$/.test(lower) ? `${lower}es` : `${lower}s`\n}\n","// ---------------------------------------------------------------------------\n// Structured, host-agnostic upgrade of every INSTALLED zidane package.\n//\n// `runUpdateCommand` is the terminal-facing ledger for `zidane upgrade`; it\n// knows the TUI is the primary and detects that install's package manager\n// from `process.execPath`. A GUI cannot reuse it — its `execPath` is\n// Electron's — and wants results, not text. This module is the shared\n// engine both can build on: every target is located by its bin on PATH\n// (`findInstalledCompanion`), checked against the registry, installed through\n// the package manager that owns THAT bin, and verified on disk afterwards\n// (a PM exiting 0 is not proof; an optional platform binary that 404s is\n// silently skipped).\n//\n// On success the applied marker is written so running surfaces can offer a\n// restart — see `auto-update-restart.ts`.\n// ---------------------------------------------------------------------------\n\nimport type { PackageManagerCommand, UpdateStatus } from './auto-update'\nimport type { CompanionSpec, InstalledCompanion } from './auto-update-companion'\nimport { Writable } from 'node:stream'\nimport {\n checkForUpdate,\n checkTarballAvailable,\n compareSemver,\n detectPackageManager,\n performSelfUpdate,\n reinstallHint,\n SELF_UPDATE_TIMEOUT_MS,\n} from './auto-update'\nimport { findInstalledCompanion } from './auto-update-companion'\nimport { writeUpdateAppliedMarker } from './auto-update-restart'\n\n/** Outcome of upgrading one package. */\nexport interface UpgradePackageResult {\n /** npm package name. */\n packageName: string\n /** Version on disk before the run; `null` when not installed. */\n from: string | null\n /** Version the registry advertises; `null` when the check failed. */\n to: string | null\n /**\n * - `'updated'` — installed and verified on disk.\n * - `'up-to-date'` — nothing to do.\n * - `'not-installed'` — bin not on PATH; skipped silently.\n * - `'unavailable'` — published but the tarball is not downloadable yet.\n * - `'failed'` — registry error, PM failure, or the install did not land; see `error`.\n */\n status: 'updated' | 'up-to-date' | 'not-installed' | 'unavailable' | 'failed'\n /** The PM command that ran (or would run). */\n command?: string[]\n /** Why the upgrade did not complete. Set for `failed` and `unavailable`. */\n error?: string\n /**\n * What failed, for `failed`: the registry check, the package manager could\n * not start, ran past its timeout or exited non-zero, or the version on disk\n * is not the one asked for.\n */\n reason?: 'registry' | 'spawn' | 'timeout' | 'exit' | 'verify'\n}\n\n/** Outcome of {@link upgradeInstalledPackages}. */\nexport interface UpgradeReport {\n /** One result per target package, in order. */\n results: UpgradePackageResult[]\n /** No `failed` / `unavailable` entries. */\n ok: boolean\n /** At least one package was actually installed — a restart is now pending. */\n installedAny: boolean\n}\n\n/** Progress event emitted while packages are upgraded: a version check, an install start, a line of package manager output, or a finished package. */\nexport type UpgradeProgressEvent\n = | { kind: 'check', packageName: string }\n | { kind: 'install', packageName: string, from: string, to: string, command: string[] }\n | { kind: 'log', packageName: string, stream: 'stdout' | 'stderr', line: string }\n | { kind: 'done', result: UpgradePackageResult }\n\n/** Options for {@link upgradeInstalledPackages}. */\nexport interface UpgradeInstalledPackagesOptions {\n /** Packages to keep in step, e.g. `[{ packageName: 'zidane-gui' }, { packageName: 'zidane-tui', binName: 'zidane' }]`. */\n targets: readonly CompanionSpec[]\n /** dist-tag to install. Defaults to `'latest'`. */\n channel?: string\n /** Registry base URL. Defaults to `https://registry.npmjs.org`. */\n registry?: string\n /** Where the registry cache and the applied marker live. */\n cacheDir?: string\n /** Install even when the registry check failed or reports up-to-date. */\n force?: boolean\n /** Abort signal. Targets not yet started are skipped once it aborts. */\n signal?: AbortSignal\n /** Per package-manager run. Defaults to {@link SELF_UPDATE_TIMEOUT_MS}. */\n timeoutMs?: number\n /** Called with check, install, log and done events as the upgrade runs. */\n onProgress?: (event: UpgradeProgressEvent) => void\n /** Injected for tests. */\n findInstalled?: typeof findInstalledCompanion\n /** Injected for tests. Defaults to {@link performSelfUpdate}. */\n performSelfUpdate?: typeof performSelfUpdate\n /** Injected for tests. Defaults to {@link checkTarballAvailable}. */\n checkTarballAvailable?: typeof checkTarballAvailable\n /** Injected for tests. Defaults to {@link checkForUpdate}. */\n checkForUpdate?: typeof checkForUpdate\n /** Injected for tests. Defaults to {@link detectPackageManager}. */\n detectPackageManager?: typeof detectPackageManager\n /** Injected for tests. Defaults to {@link writeUpdateAppliedMarker}. */\n writeMarker?: typeof writeUpdateAppliedMarker\n /** Injected for tests. Defaults to `Date.now`. */\n now?: () => number\n}\n\n/**\n * Upgrades each installed target package through its own package manager.\n * Skips targets whose bin is not on PATH, checks the registry for each one, reports `unavailable` instead of installing when the tarball is not downloadable yet, runs the install, and verifies the new version on disk. Writes the applied marker when something was installed and `cacheDir` is set.\n *\n * @param options - Target packages, registry settings and progress callback.\n * @example\n * ```ts\n * import { upgradeInstalledPackages } from 'zidane/chat'\n *\n * const report = await upgradeInstalledPackages({\n * targets: [{ packageName: 'zidane-tui', binName: 'zidane' }],\n * onProgress: event => console.log(event.kind),\n * })\n * if (!report.ok)\n * console.error(report.results.filter(r => r.status === 'failed'))\n * ```\n */\nexport async function upgradeInstalledPackages(options: UpgradeInstalledPackagesOptions): Promise<UpgradeReport> {\n const channel = options.channel ?? 'latest'\n const find = options.findInstalled ?? findInstalledCompanion\n const check = options.checkForUpdate ?? checkForUpdate\n const available = options.checkTarballAvailable ?? checkTarballAvailable\n const install = options.performSelfUpdate ?? performSelfUpdate\n const detect = options.detectPackageManager ?? detectPackageManager\n const progress = options.onProgress ?? (() => {})\n\n const results: UpgradePackageResult[] = []\n for (const spec of options.targets) {\n if (options.signal?.aborted)\n break\n const installed = find(spec)\n if (!installed) {\n results.push({ packageName: spec.packageName, from: null, to: null, status: 'not-installed' })\n continue\n }\n progress({ kind: 'check', packageName: spec.packageName })\n const status = await check({\n packageName: installed.packageName,\n currentVersion: installed.version,\n channel,\n registry: options.registry,\n cacheDir: options.cacheDir,\n force: true,\n timeoutMs: 8000,\n signal: options.signal,\n })\n const result = await upgradeOne(installed, status)\n progress({ kind: 'done', result })\n results.push(result)\n }\n\n const installedAny = results.some(r => r.status === 'updated')\n if (installedAny && options.cacheDir) {\n const writeMarker = options.writeMarker ?? writeUpdateAppliedMarker\n writeMarker(options.cacheDir, {\n at: (options.now ?? Date.now)(),\n packages: results\n .filter(r => r.status === 'updated' && r.to)\n .map(r => ({ packageName: r.packageName, version: r.to! })),\n })\n }\n return {\n results,\n ok: results.every(r => r.status !== 'failed' && r.status !== 'unavailable'),\n installedAny,\n }\n\n async function upgradeOne(installed: InstalledCompanion, status: UpdateStatus): Promise<UpgradePackageResult> {\n const base = { packageName: installed.packageName, from: installed.version, to: status.latest }\n if (status.source === 'error' && !options.force)\n return { ...base, status: 'failed', reason: 'registry', error: `registry check failed: ${status.error ?? 'unknown error'}` }\n const outdated = status.latest !== null && compareSemver(status.latest, installed.version) > 0\n if (!outdated && !options.force)\n return { ...base, status: 'up-to-date' }\n if (status.latest && !options.force) {\n const availability = await available({ packageName: installed.packageName, version: status.latest, registry: options.registry })\n if (availability === 'missing')\n return { ...base, status: 'unavailable', error: `${installed.packageName} ${status.latest} is published but not downloadable yet. Try again in a few minutes.` }\n }\n // The PM that owns THIS bin — npm/pnpm/yarn/bun/Volta differ per install.\n const command: PackageManagerCommand = detect({ packageName: installed.packageName, channel, binaryPath: installed.binaryPath })\n progress({ kind: 'install', packageName: installed.packageName, from: installed.version, to: status.latest ?? channel, command: command.argv })\n\n const outcome = await install({\n packageName: installed.packageName,\n channel,\n packageManager: command.id,\n signal: options.signal,\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n stdout: lineSink(installed.packageName, 'stdout'),\n stderr: lineSink(installed.packageName, 'stderr'),\n // Quiet PM output: the UI shows one live line, not a progress bar dump.\n env: { npm_config_loglevel: 'error', npm_config_progress: 'false' },\n })\n if (outcome.spawnError)\n return { ...base, status: 'failed', reason: 'spawn', command: command.argv, error: `could not run ${command.argv[0]}: ${outcome.spawnError}` }\n if (outcome.timedOut) {\n const minutes = Math.round((options.timeoutMs ?? SELF_UPDATE_TIMEOUT_MS) / 60_000)\n return { ...base, status: 'failed', reason: 'timeout', command: command.argv, error: `${command.argv[0]} did not finish within ${minutes} min and was stopped` }\n }\n if (outcome.exitCode !== 0)\n return { ...base, status: 'failed', reason: 'exit', command: command.argv, error: `${command.argv[0]} exited with ${outcome.exitCode ?? 'unknown'}` }\n\n // Verify: what is on disk now vs. what we asked for.\n const after = find(installed)\n if (status.latest && after && compareSemver(after.version, status.latest) < 0) {\n return {\n ...base,\n status: 'failed',\n reason: 'verify',\n command: command.argv,\n error: `install finished but ${after.version} is on disk, expected ${status.latest}. Reinstall: ${reinstallHint(command, installed.packageName, channel)}`,\n }\n }\n return { ...base, to: after?.version ?? status.latest, status: 'updated', command: command.argv }\n }\n\n /** Split a PM's byte stream into trimmed lines for the progress sink. */\n function lineSink(packageName: string, stream: 'stdout' | 'stderr'): NodeJS.WritableStream {\n let buffer = ''\n const flush = (final: boolean) => {\n const parts = buffer.split(/\\r?\\n/)\n buffer = final ? '' : parts.pop() ?? ''\n for (const part of parts) {\n const line = part.trim()\n if (line)\n progress({ kind: 'log', packageName, stream, line })\n }\n }\n return new Writable({\n write(chunk: Uint8Array | string, _enc, cb) {\n buffer += chunk.toString()\n flush(false)\n cb()\n },\n final(cb) {\n flush(true)\n cb()\n },\n })\n }\n}\n","// ---------------------------------------------------------------------------\n// Automatic installs (`Settings.autoInstallUpdates`).\n//\n// When a background check finds a newer stable release, the host calls\n// `autoInstallUpdate`. It runs the same engine as the GUI's Install button\n// and `zidane upgrade` (`upgradeInstalledPackages`), under the shared install\n// lock (`auto-update-lock.ts`), and never restarts anything: the applied\n// marker it writes wakes the restart chip.\n//\n// A failed automatic install records its version in\n// `<cacheDir>/update-failed.json`. A failure that may pass on its own (the\n// registry or the tarball not reachable yet, a timeout) is retried after\n// {@link AUTO_INSTALL_RETRY_BACKOFF_MS}; any other failure is not retried\n// for that version. A newer release, or a manual install (which clears the\n// record), always runs. When the cache dir can't be written, the record is\n// kept in memory for the life of the process instead.\n// ---------------------------------------------------------------------------\n\nimport type { UpdateStatus } from './auto-update'\nimport type { CompanionSpec } from './auto-update-companion'\nimport type { UpdateLockHolder, UpdateLockOptions } from './auto-update-lock'\nimport type { UpgradeInstalledPackagesOptions, UpgradeReport } from './auto-update-service'\nimport { readFileSync, unlinkSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { writeJsonFileAtomic } from '../atomic-write'\nimport { parseSemver, updateChecksOptedOut } from './auto-update'\nimport { acquireUpdateLock } from './auto-update-lock'\nimport { upgradeInstalledPackages } from './auto-update-service'\n\n/** File name of the failed-install record inside the cache directory. */\nexport const UPDATE_FAILED_FILE = 'update-failed.json'\n\n/**\n * Waits before retrying a version whose install failed in a way that may pass\n * on its own: after the 1st, 2nd and 3rd such failure. A 4th is final.\n */\nexport const AUTO_INSTALL_RETRY_BACKOFF_MS: readonly number[] = [60 * 60_000, 6 * 60 * 60_000, 24 * 60 * 60_000]\n\n/** The automatic install that failed last. */\nexport interface FailedUpdateRecord {\n /** Release the install tried to reach. */\n version: string\n /** Why it failed, one package per line. */\n error: string\n /** ms since epoch. */\n at: number\n /**\n * The failure may pass on its own (registry or tarball not reachable yet, a\n * timeout): the version is retried after {@link AUTO_INSTALL_RETRY_BACKOFF_MS}.\n * Absent means final for this version.\n */\n transient?: boolean\n /** Failed attempts at this version. Absent counts as 1. */\n attempts?: number\n}\n\n/** Records the cache dir could not hold, per cache dir, for this process. */\nconst unwritable = new Map<string, FailedUpdateRecord>()\n\n/**\n * Reads the failed-install record. `null` when there is none or it does not\n * parse. Falls back to the record this process could not write to disk.\n *\n * @param cacheDir - Directory that holds the record.\n */\nexport function readFailedUpdate(cacheDir: string): FailedUpdateRecord | null {\n try {\n const raw = JSON.parse(readFileSync(join(cacheDir, UPDATE_FAILED_FILE), 'utf8')) as Partial<FailedUpdateRecord>\n if (typeof raw.version === 'string' && typeof raw.error === 'string' && typeof raw.at === 'number') {\n return {\n version: raw.version,\n error: raw.error,\n at: raw.at,\n ...(raw.transient === true ? { transient: true } : {}),\n ...(typeof raw.attempts === 'number' ? { attempts: raw.attempts } : {}),\n }\n }\n return null\n }\n catch {\n return unwritable.get(cacheDir) ?? null\n }\n}\n\n/**\n * Records a failed automatic install. When the cache dir can't be written,\n * the record is kept in memory for this process, so a broken cache never\n * turns into a package-manager run on every check.\n *\n * @param cacheDir - Directory that holds the record. Created when missing.\n * @param record - What failed.\n */\nexport function writeFailedUpdate(cacheDir: string, record: FailedUpdateRecord): void {\n try {\n writeJsonFileAtomic(join(cacheDir, UPDATE_FAILED_FILE), record, { ensureDir: true })\n unwritable.delete(cacheDir)\n }\n catch {\n unwritable.set(cacheDir, record)\n }\n}\n\n/**\n * The record for another failure of `version`: attempts count up when the\n * last record was for the same version.\n *\n * @param cacheDir - Directory that holds the record.\n * @param failure - The version, why it failed, whether it may pass on its own, and when.\n */\nexport function nextFailedUpdate(cacheDir: string, failure: { version: string, error: string, transient: boolean, at: number }): FailedUpdateRecord {\n const previous = readFailedUpdate(cacheDir)\n const attempts = previous?.version === failure.version ? (previous.attempts ?? 1) + 1 : 1\n return { version: failure.version, error: failure.error, at: failure.at, attempts, ...(failure.transient ? { transient: true } : {}) }\n}\n\n/**\n * Forgets the failed install, so the next check may try again. Called by every\n * manual install.\n *\n * @param cacheDir - Directory that holds the record.\n */\nexport function clearFailedUpdate(cacheDir: string): void {\n unwritable.delete(cacheDir)\n try {\n unlinkSync(join(cacheDir, UPDATE_FAILED_FILE))\n }\n catch {\n // Nothing to clear.\n }\n}\n\n/**\n * Whether `version` may be tried again: `true`, `'retry-later'` while a\n * passing failure is backing off, or `'failed-before'` for good.\n */\nfunction retryVerdict(record: FailedUpdateRecord | null, version: string, now: number): true | 'retry-later' | 'failed-before' {\n if (record?.version !== version)\n return true\n const attempts = record.attempts ?? 1\n if (!record.transient || attempts > AUTO_INSTALL_RETRY_BACKOFF_MS.length)\n return 'failed-before'\n return now - record.at >= AUTO_INSTALL_RETRY_BACKOFF_MS[attempts - 1]! ? true : 'retry-later'\n}\n\n/** Why {@link autoInstallUpdate} did nothing. */\nexport type AutoInstallSkipReason\n /** No newer stable release on `latest`, or the env opts out. */\n = | 'not-eligible'\n /** This version failed before; wait for a newer one or a manual install. */\n | 'failed-before'\n /** This version failed in a way that may pass; it is retried after a backoff. */\n | 'retry-later'\n /** Another install holds the lock. The installed-version watcher sees its result. */\n | 'locked'\n\n/** Outcome of {@link autoInstallUpdate}. */\nexport type AutoInstallOutcome\n = | { kind: 'installed', version: string, report: UpgradeReport }\n | { kind: 'skipped', reason: AutoInstallSkipReason, holder?: UpdateLockHolder }\n | { kind: 'failed', version: string, error: string }\n\n/** Options for {@link isAutoInstallCandidate}. */\nexport interface AutoInstallCandidateOptions {\n /** Directory that holds the failed-install record. */\n cacheDir: string\n /** Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv\n /** Defaults to `Date.now`. */\n now?: () => number\n}\n\n/**\n * Whether a check result is one an automatic install may act on: a newer\n * release on the `latest` dist-tag read from the registry just now (not a\n * stale cache), not a prerelease, not opted out by env, and not a version\n * that failed before (see {@link AUTO_INSTALL_RETRY_BACKOFF_MS}).\n *\n * @param status - Result of the background check.\n * @param options - Cache directory and environment.\n * @returns `true`, or why not.\n */\nexport function isAutoInstallCandidate(status: UpdateStatus | null, options: AutoInstallCandidateOptions): true | AutoInstallSkipReason {\n if (!status?.hasUpdate || !status.latest || status.channel !== 'latest')\n return 'not-eligible'\n if (status.source === 'skipped' || status.source === 'error' || status.stale || updateChecksOptedOut(options.env))\n return 'not-eligible'\n const semver = parseSemver(status.latest)\n if (!semver || semver.prerelease.length > 0)\n return 'not-eligible'\n return retryVerdict(readFailedUpdate(options.cacheDir), status.latest, (options.now ?? Date.now)())\n}\n\n/** Options for {@link autoInstallUpdate}. */\nexport interface AutoInstallUpdateOptions {\n /** Result of the background check that found the release. */\n status: UpdateStatus\n /** Where the lock, the failed record, the registry cache and the applied marker live. */\n cacheDir: string\n /** Packages to keep in step. */\n targets: readonly CompanionSpec[]\n /** Who runs the install, recorded in the lock: `'desktop app'`, `'terminal app'`. */\n by: string\n /** Passed through to {@link upgradeInstalledPackages} (progress, PATH-aware probes). */\n upgradeOptions?: Omit<UpgradeInstalledPackagesOptions, 'targets' | 'cacheDir' | 'channel' | 'force'>\n /** Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv\n /** Injected for tests. Defaults to {@link upgradeInstalledPackages}. */\n upgrade?: typeof upgradeInstalledPackages\n /** Injected for tests. */\n lock?: UpdateLockOptions\n /** Injected for tests. Defaults to `Date.now`. */\n now?: () => number\n}\n\n/**\n * Installs the release a background check found, unless something says not\n * to (see {@link isAutoInstallCandidate}) or another install holds the lock.\n * Never throws. Records a failure so the same version is not retried, and\n * clears the record on success.\n *\n * @param options - Check result, cache directory, targets and test seams.\n * @example\n * ```ts\n * const outcome = await autoInstallUpdate({ status, cacheDir, targets, by: 'terminal app' })\n * if (outcome.kind === 'failed')\n * log.warn('automatic update failed', { error: outcome.error })\n * ```\n */\nexport async function autoInstallUpdate(options: AutoInstallUpdateOptions): Promise<AutoInstallOutcome> {\n const candidate = isAutoInstallCandidate(options.status, { cacheDir: options.cacheDir, now: options.now ?? Date.now, ...(options.env ? { env: options.env } : {}) })\n if (candidate !== true)\n return { kind: 'skipped', reason: candidate }\n const lock = acquireUpdateLock(options.cacheDir, options.by, options.lock)\n if (!lock.acquired)\n return { kind: 'skipped', reason: 'locked', holder: lock.holder }\n try {\n return await runAutoInstallLocked(options)\n }\n finally {\n lock.release()\n }\n}\n\n/**\n * {@link autoInstallUpdate} for a caller that already holds the install lock\n * (the update worker, which must keep it until its result is written).\n *\n * @param options - Same as {@link autoInstallUpdate}; `lock` is ignored.\n */\nexport async function runAutoInstallLocked(options: AutoInstallUpdateOptions): Promise<AutoInstallOutcome> {\n const { status, cacheDir } = options\n const now = options.now ?? Date.now\n const candidate = isAutoInstallCandidate(status, { cacheDir, now, ...(options.env ? { env: options.env } : {}) })\n if (candidate !== true)\n return { kind: 'skipped', reason: candidate }\n const version = status.latest!\n try {\n const report = await (options.upgrade ?? upgradeInstalledPackages)({\n ...options.upgradeOptions,\n targets: options.targets,\n cacheDir,\n channel: 'latest',\n })\n const error = upgradeError(report)\n if (error) {\n writeFailedUpdate(cacheDir, nextFailedUpdate(cacheDir, { version, error, transient: isTransientFailure(report), at: now() }))\n return { kind: 'failed', version, error }\n }\n clearFailedUpdate(cacheDir)\n return { kind: 'installed', version, report }\n }\n catch (err) {\n const error = err instanceof Error ? err.message : String(err)\n writeFailedUpdate(cacheDir, nextFailedUpdate(cacheDir, { version, error, transient: false, at: now() }))\n return { kind: 'failed', version, error }\n }\n}\n\n/**\n * Whether every failure in the run may pass on its own: the registry could not\n * be read, the release is not downloadable yet, or the package manager timed\n * out. A package-manager error or a wrong version on disk is not.\n *\n * @param report - Result of {@link upgradeInstalledPackages}.\n */\nexport function isTransientFailure(report: UpgradeReport): boolean {\n const failures = report.results.filter(r => r.status === 'failed' || r.status === 'unavailable')\n return failures.length > 0 && failures.every(r => r.status === 'unavailable' || r.reason === 'registry' || r.reason === 'timeout')\n}\n\n/**\n * Why an upgrade run did not land, one package per line; `null` when it did.\n * A run that found no package on PATH failed too: nothing was installed.\n *\n * @param report - Result of {@link upgradeInstalledPackages}.\n */\nexport function upgradeError(report: UpgradeReport): string | null {\n const failures = report.results.filter(r => r.status === 'failed' || r.status === 'unavailable')\n if (failures.length > 0)\n return failures.map(f => `${f.packageName}: ${f.error ?? f.status}`).join('\\n')\n if (report.results.every(r => r.status === 'not-installed'))\n return 'no zidane packages found on PATH'\n return null\n}\n","// ---------------------------------------------------------------------------\n// `useUpdateCheck` — renderer-agnostic React hook driving the auto-update\n// footer chip + any banner a host wants to wire.\n//\n// Lives in `zidane/chat` (not `zidane/tui`) so a GUI shell can consume the\n// same status. Pure React + the renderer-agnostic core in\n// `./auto-update`; no OpenTUI imports.\n// ---------------------------------------------------------------------------\n\nimport type { UpdateStatus } from './auto-update'\nimport type { AutoInstallOutcome } from './auto-update-auto-install'\nimport type { CompanionSpec } from './auto-update-companion'\nimport type { InstalledVersionState } from './auto-update-restart'\nimport type { Hint } from './hints'\nimport { useEffect, useRef, useState } from 'react'\nimport { checkForUpdate } from './auto-update'\nimport { autoInstallUpdate } from './auto-update-auto-install'\nimport { watchInstalledVersion } from './auto-update-restart'\n\n/** Options for {@link useUpdateCheck}. */\nexport interface UseUpdateCheckOptions {\n /** npm package name. e.g. `'zidane-tui'`. */\n packageName: string\n /** Inlined at build time. Skips the check if empty/falsy. */\n currentVersion: string\n /**\n * Master enable flag — usually `Settings.checkForUpdates`. When `false`\n * the hook short-circuits without scheduling any work AND clears the\n * last-known status so a stale chip doesn't linger after the user\n * disabled the feature.\n */\n enabled: boolean\n /** Directory under which the on-disk cache lives. Usually `paths.userDir`. */\n cacheDir?: string\n /** Registry base URL. Defaults to `https://registry.npmjs.org`. */\n registry?: string\n /** dist-tag to read. Defaults to `'latest'`. */\n channel?: string\n /**\n * Cache lifetime for the read-through. **Defaults to `0`** (always\n * fetch fresh) — the TUI chip is the user's source of truth for \"what\n * version of zidane is on npm right now\"; we'd rather pay one HTTPS\n * call per session than tell them yesterday's news for up to 24 hours.\n *\n * The on-disk cache is still WRITTEN with each fresh result (so\n * `zidane upgrade` and other tooling reads benefit) and still READ to\n * feed the registry's ETag (so the cold path can short-circuit on 304\n * — meaningful bandwidth win when nothing changed). Pass a positive\n * value (e.g. `3_600_000` for 1 h) to enable read-through caching for\n * a GUI shell that mounts the hook in many places.\n */\n cacheTtlMs?: number\n /**\n * Wall-clock delay before the first check fires. Defaults to 1 500 ms so\n * we never compete with the boot path's first paint. The check itself\n * is async, so this only affects WHEN the chip first appears — never\n * blocks anything else.\n */\n delayMs?: number\n /**\n * Check again every this many ms while mounted, so a long session still\n * learns about a release. Defaults to `0`: once per mount.\n */\n recheckMs?: number\n /** Injected for tests. */\n now?: () => number\n /** Injected for tests. */\n fetcher?: typeof fetch\n}\n\n/**\n * Returns the current update status. The check fires:\n * - Once per mount (every TUI launch).\n * - Every `recheckMs` after that, when set.\n * - Every time `enabled` flips from `false` → `true` (user re-enabling\n * the setting expects an immediate refresh).\n *\n * The check is async and deferred by `delayMs` so it never competes with\n * first paint. Result is `null` until the first check resolves; flipping\n * `enabled` to `false` resets to `null` so any chip rendered against\n * `hasUpdate` disappears immediately.\n *\n * The hook owns its own AbortController so an unmount cancels any in-flight\n * registry request cleanly.\n *\n * @param options - Package, current version, enable flag and cache settings for the check.\n */\nexport function useUpdateCheck(options: UseUpdateCheckOptions): UpdateStatus | null {\n const [status, setStatus] = useState<UpdateStatus | null>(null)\n // Ref-mirror so the effect doesn't re-run on every keystroke when the\n // host wraps the result into derived hint state.\n const optsRef = useRef(options)\n optsRef.current = options\n\n useEffect(() => {\n if (!options.enabled || !options.currentVersion) {\n // Disabling the setting must hide a chip that was already painted\n // — otherwise the user sees a stale `↑ vX.Y.Z` linger until the\n // next launch. React bails on identical-state updates so the\n // initial-mount case (status already null) costs nothing.\n setStatus(null)\n return\n }\n const controller = new AbortController()\n const check = () => {\n void checkForUpdate({\n packageName: optsRef.current.packageName,\n currentVersion: optsRef.current.currentVersion,\n cacheDir: optsRef.current.cacheDir,\n registry: optsRef.current.registry,\n channel: optsRef.current.channel,\n // Default to TTL=0 (always fetch fresh). See `cacheTtlMs` docs.\n cacheTtlMs: optsRef.current.cacheTtlMs ?? 0,\n signal: controller.signal,\n now: optsRef.current.now,\n fetcher: optsRef.current.fetcher,\n }).then((s) => {\n if (!controller.signal.aborted)\n setStatus(s)\n })\n }\n const timer = setTimeout(check, options.delayMs ?? 1500)\n const recheckMs = optsRef.current.recheckMs ?? 0\n const recheck = recheckMs > 0 ? setInterval(check, recheckMs) : undefined\n return () => {\n clearTimeout(timer)\n clearInterval(recheck)\n controller.abort()\n }\n // Re-run when the master enable / package / version flip. Other\n // option changes stay inside `optsRef.current`; re-running on every\n // option mutation would cause render storms when the host wraps\n // the result into derived hint state.\n }, [options.enabled, options.packageName, options.currentVersion])\n\n return status\n}\n\n/** Options for {@link useAutoInstallUpdate}. */\nexport interface UseAutoInstallUpdateOptions {\n /** Result of {@link useUpdateCheck}. */\n status: UpdateStatus | null\n /** `Settings.checkForUpdates && Settings.autoInstallUpdates`, and the host allows background installs. */\n enabled: boolean\n /** Where the lock, the failed record and the applied marker live. Nothing runs without it. */\n cacheDir?: string\n /** Packages to keep in step. */\n targets: readonly CompanionSpec[]\n /** Who runs the install, recorded in the lock. */\n by: string\n /** Called once per attempt with what happened. */\n onOutcome?: (outcome: AutoInstallOutcome) => void\n /** Injected for tests. Defaults to {@link autoInstallUpdate}. */\n install?: typeof autoInstallUpdate\n}\n\n/**\n * Installs the release {@link useUpdateCheck} found, in the background, when\n * the user turned `autoInstallUpdates` on. One attempt per check result; the\n * failure record decides whether a version that failed is tried again. The\n * check itself runs after first paint, so this never competes with it.\n * Never restarts anything: the applied marker wakes `useRestartRequired`.\n * Returns the last outcome, `null` before the first attempt.\n *\n * @param options - Check result, switches, cache directory and targets.\n */\nexport function useAutoInstallUpdate(options: UseAutoInstallUpdateOptions): AutoInstallOutcome | null {\n const [outcome, setOutcome] = useState<AutoInstallOutcome | null>(null)\n const optsRef = useRef(options)\n optsRef.current = options\n const tried = useRef(new Set<string>())\n const latest = options.status?.hasUpdate ? options.status.latest : null\n const checkedAt = options.status?.checkedAt ?? null\n\n useEffect(() => {\n const { status, cacheDir } = optsRef.current\n const attempt = `${latest}@${checkedAt}`\n if (!options.enabled || !cacheDir || !status || !latest || tried.current.has(attempt))\n return\n tried.current.add(attempt)\n let live = true\n // Not aborted on unmount: killing a package manager mid-install can\n // leave a broken global install behind.\n void (optsRef.current.install ?? autoInstallUpdate)({\n status,\n cacheDir,\n targets: optsRef.current.targets,\n by: optsRef.current.by,\n }).then((next) => {\n optsRef.current.onOutcome?.(next)\n if (live)\n setOutcome(next)\n })\n return () => {\n live = false\n }\n }, [options.enabled, options.cacheDir, latest, checkedAt])\n\n return outcome\n}\n\n// ---------------------------------------------------------------------------\n// Footer chip helper — pure function so any renderer (TUI Footer, future\n// GUI status bar) can compose against the same `Hint`. The chip is\n// suppressed when there's no update; callers spread the result into their\n// hint array unconditionally.\n//\n// Color contract: the chip rides whatever accent the host passes (typically\n// `theme.colors.accent`). Renderers that want a \"fresh check\" badge can\n// inspect `status.source === 'fresh'` themselves — this helper sticks to\n// the one-line discoverability surface.\n// ---------------------------------------------------------------------------\n\n/** Display options for the update and restart hints. */\nexport interface BuildUpdateHintOptions {\n /** Foreground color for the `↑` glyph + version label. */\n color: string\n /**\n * When true (default), the chip renders the literal version (`↑ v5.6.0`).\n * When false, just the glyph + `update` label — useful when the row is\n * tight and the version would push higher-priority chips off the right.\n */\n showVersion?: boolean\n /** Override the leading glyph. Defaults to `'↑'`. */\n glyph?: string\n}\n\n/**\n * Builds the `↑ vX.Y.Z` hint for an available update.\n * Returns `null` when there is no newer version.\n *\n * @param status - Result of an update check, or `null` before the first check.\n * @param options - Color and label settings for the hint.\n */\nexport function buildUpdateHint(status: UpdateStatus | null, options: BuildUpdateHintOptions): Hint | null {\n if (!status?.hasUpdate || !status.latest)\n return null\n const label = options.showVersion === false\n ? 'update'\n : `v${stripVPrefix(status.latest)}`\n return {\n key: options.glyph ?? '↑',\n keyColor: options.color,\n label,\n labelColor: options.color,\n }\n}\n\n/**\n * `↻ v7.0.31` — a newer build is on disk than the one running (this\n * process is stale; see `auto-update-restart.ts`). Takes precedence over\n * the `↑` chip: once the release is installed, \"available\" is old news and\n * \"restart\" is the one action left. Same color contract as `buildUpdateHint`.\n *\n * @param state - Installed-version state, or `null` when unknown.\n * @param options - Color and label settings for the hint.\n */\nexport function buildRestartHint(\n state: { restartRequired: boolean, installed: string | null } | null,\n options: BuildUpdateHintOptions,\n): Hint | null {\n if (!state?.restartRequired || !state.installed)\n return null\n return {\n key: options.glyph ?? '↻',\n keyColor: options.color,\n label: options.showVersion === false ? 'restart' : `v${stripVPrefix(state.installed)}`,\n labelColor: options.color,\n }\n}\n\nfunction stripVPrefix(v: string): string {\n return v.startsWith('v') ? v.slice(1) : v\n}\n\n// ---------------------------------------------------------------------------\n// `useRestartRequired` — the TUI's consumer of the installed-version watcher\n// (`./auto-update-restart`). The GUI runs the watcher in its main process and\n// forwards the state over IPC instead.\n// ---------------------------------------------------------------------------\n\n/** Options for {@link useRestartRequired}. */\nexport interface UseRestartRequiredOptions {\n /** The running package. `null` disables the hook. */\n spec: CompanionSpec | null\n /** Version the process was built with. */\n currentVersion: string\n /** Directory that holds the applied marker. Omit to rely on polling alone. */\n cacheDir?: string\n /** Master switch — mirrors `Settings.checkForUpdates`. */\n enabled: boolean\n /** Poll interval in milliseconds. Defaults to 5 minutes. `0` disables polling. */\n pollMs?: number\n}\n\n/**\n * `null` until the first probe (or when disabled / no spec), then the live\n * state. Flipping `enabled` off tears the watcher down and clears the state\n * so no stale \"restart\" chip lingers.\n *\n * @param options - Package spec, running version, cache directory, poll interval and the master `enabled` switch.\n */\nexport function useRestartRequired(options: UseRestartRequiredOptions): InstalledVersionState | null {\n const [state, setState] = useState<InstalledVersionState | null>(null)\n const { spec, currentVersion, cacheDir, enabled, pollMs } = options\n const packageName = spec?.packageName\n const binName = spec?.binName\n\n useEffect(() => {\n if (!enabled || !packageName || !currentVersion) {\n setState(null)\n return\n }\n const watcher = watchInstalledVersion({\n spec: binName ? { packageName, binName } : { packageName },\n currentVersion,\n cacheDir,\n pollMs,\n onChange: setState,\n })\n setState(watcher.current())\n return () => watcher.close()\n }, [enabled, packageName, binName, currentVersion, cacheDir, pollMs])\n\n return state\n}\n","// ---------------------------------------------------------------------------\n// The update worker: one detached process that runs an install to the end.\n//\n// A package manager killed mid-install can leave a global install broken.\n// When the TUI, the GUI or `zidane upgrade` runs the install as its own\n// child, quitting it (or a crash, or a closed terminal) can do exactly that.\n// So they start this worker instead, detached in its own session, and only\n// follow it:\n//\n// - `<cacheDir>/update.lock` is held by the worker's pid for the whole run,\n// until its result is written.\n// - `<cacheDir>/update.log` gets one line per step and the package\n// manager's output. The previous run's log is kept as `update.log.1`.\n// Each is capped at {@link UPDATE_LOG_CAP_BYTES}.\n// - `<cacheDir>/update-result.json` is the outcome, written before the lock\n// is released. The applied marker is written by the install itself.\n//\n// The request travels on argv (`--request <base64url JSON>`), versioned by\n// {@link UPDATE_WORKER_PROTOCOL}. Whatever binary runs the worker (the\n// `zidane` CLI's `__update-worker`, or the desktop app's bundled entry under\n// Electron-as-node) calls {@link runUpdateWorkerMain}.\n// ---------------------------------------------------------------------------\n\nimport type { SpawnOptions } from 'node:child_process'\nimport type { UpdateStatus } from './auto-update'\nimport type { AutoInstallOutcome, AutoInstallUpdateOptions } from './auto-update-auto-install'\nimport type { CompanionSpec } from './auto-update-companion'\nimport type { UpdateLockHolder } from './auto-update-lock'\nimport type { UpgradeProgressEvent, UpgradeReport } from './auto-update-service'\nimport { Buffer } from 'node:buffer'\nimport { spawn as nodeSpawn } from 'node:child_process'\nimport { randomUUID } from 'node:crypto'\nimport { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, readSync, renameSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { Writable } from 'node:stream'\nimport { writeJsonFileAtomic } from '../atomic-write'\nimport { clearFailedUpdate, nextFailedUpdate, runAutoInstallLocked, upgradeError, writeFailedUpdate } from './auto-update-auto-install'\nimport { acquireUpdateLock, isUpdateLockStale, readUpdateLock } from './auto-update-lock'\nimport { upgradeInstalledPackages } from './auto-update-service'\n\n/** Version of the request and result files. A worker rejects any other. */\nexport const UPDATE_WORKER_PROTOCOL = 1\n/** File name of the worker's log inside the cache directory. */\nexport const UPDATE_LOG_FILE = 'update.log'\n/** File name of the worker's result inside the cache directory. */\nexport const UPDATE_RESULT_FILE = 'update-result.json'\n/** Cap of each log file. Past it, one truncation line, then nothing. */\nexport const UPDATE_LOG_CAP_BYTES = 1024 * 1024\n\n/** Exit code of a worker that found the lock held by another install. */\nconst EXIT_LOCKED = 3\n/** Exit code of a worker whose request did not parse or has another protocol. */\nconst EXIT_BAD_REQUEST = 2\n\n/** What a worker is asked to do. */\nexport interface UpdateWorkerRequest {\n /** {@link UPDATE_WORKER_PROTOCOL}. */\n v: number\n /** Names this run in the log and the result. */\n id: string\n /** Where the lock, log, result, failure record and marker live. */\n cacheDir: string\n /** Who asked, shown to anyone who finds the lock held: `'desktop app'`. */\n by: string\n /** Packages to keep in step, in order. */\n targets: CompanionSpec[]\n /**\n * An automatic install of the release this check found: eligibility and\n * the failure record apply (`autoInstallUpdate`). Absent: a manual install,\n * which clears the failure record first.\n */\n auto?: UpdateStatus\n /** dist-tag. Defaults to `'latest'`. Manual only. */\n channel?: string\n /** Registry base URL. */\n registry?: string\n /** Install even when the registry check failed or reports up to date. Manual only. */\n force?: boolean\n /** Per package-manager run. Defaults to 10 minutes. */\n timeoutMs?: number\n /**\n * Run the host's `zidane upgrade` with these arguments instead (its own\n * checks, ledger and verification), under the worker's lock. Its stdout\n * lines go to the log as they are, its stderr lines prefixed with `! `.\n */\n upgradeArgv?: string[]\n}\n\n/** How a run ended, as its starter sees it. */\nexport type UpdateWorkerOutcome\n /** An automatic install: installed, skipped (lock held, not eligible, backing off) or failed. */\n = | AutoInstallOutcome\n /** A manual install ran: \\`error\\` says what did not land, \\`null\\` when all did. */\n | { kind: 'ran', report: UpgradeReport, error: string | null }\n /** `upgradeArgv` ran: the exit code `zidane upgrade` returned. */\n | { kind: 'exited', exitCode: number }\n /** The worker could not start or stopped without a result. */\n | { kind: 'lost', error: string }\n\n/**\n * Runs `zidane upgrade <argv>` in the worker; the host wires it (it knows its\n * package, version and companions). Must not take the install lock: the\n * worker holds it.\n */\nexport type UpdateWorkerUpgradeCommand = (argv: readonly string[], stdout: NodeJS.WritableStream, stderr: NodeJS.WritableStream) => Promise<number>\n\n/** `<cacheDir>/update-result.json`. */\nexport interface UpdateWorkerResult {\n /** {@link UPDATE_WORKER_PROTOCOL}. */\n v: number\n /** The request's id. */\n id: string\n /** The worker's pid: matches the lock holder an attached surface saw. */\n pid: number\n /** Who asked. */\n by: string\n /** ms since epoch. */\n startedAt: number\n /** ms since epoch. */\n finishedAt: number\n /** How it ended. */\n outcome: UpdateWorkerOutcome\n}\n\n/**\n * Encodes a request for the worker's argv.\n *\n * @param request - The request.\n */\nexport function encodeUpdateWorkerRequest(request: UpdateWorkerRequest): string {\n return Buffer.from(JSON.stringify(request), 'utf8').toString('base64url')\n}\n\n/**\n * Decodes `--request <value>` from argv. `null` when absent, malformed, or of\n * another protocol version.\n *\n * @param argv - Arguments after the worker's own command.\n */\nexport function decodeUpdateWorkerRequest(argv: readonly string[]): UpdateWorkerRequest | null {\n const at = argv.indexOf('--request')\n const raw = at === -1 ? undefined : argv[at + 1]\n if (!raw)\n return null\n try {\n const parsed = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8')) as Partial<UpdateWorkerRequest>\n if (parsed.v !== UPDATE_WORKER_PROTOCOL || typeof parsed.id !== 'string' || typeof parsed.cacheDir !== 'string'\n || typeof parsed.by !== 'string' || !Array.isArray(parsed.targets)) {\n return null\n }\n return parsed as UpdateWorkerRequest\n }\n catch {\n return null\n }\n}\n\n/**\n * Reads the last worker result. `null` when there is none, it does not parse,\n * or it has another protocol version.\n *\n * @param cacheDir - Directory that holds the result.\n */\nexport function readUpdateWorkerResult(cacheDir: string): UpdateWorkerResult | null {\n try {\n const parsed = JSON.parse(readFileSync(join(cacheDir, UPDATE_RESULT_FILE), 'utf8')) as Partial<UpdateWorkerResult>\n if (parsed.v !== UPDATE_WORKER_PROTOCOL || typeof parsed.id !== 'string' || typeof parsed.pid !== 'number' || !parsed.outcome)\n return null\n return parsed as UpdateWorkerResult\n }\n catch {\n return null\n }\n}\n\n/** Injected for tests. */\nexport interface RunUpdateWorkerDeps {\n /** Defaults to {@link upgradeInstalledPackages}. */\n upgrade?: typeof upgradeInstalledPackages\n /** Defaults to `Date.now`. */\n now?: () => number\n /** Read for the env opt-outs of an automatic run (`CI`, `ZIDANE_NO_UPDATE`). Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv\n /** Defaults to `process.pid`. */\n pid?: number\n /** Runs `request.upgradeArgv`. Without it such a request ends `lost`. */\n upgradeCommand?: UpdateWorkerUpgradeCommand\n}\n\n/**\n * Runs one install inside the worker process: takes the lock under this pid,\n * logs every step, writes the result, then releases the lock. Never throws.\n * `null` when another install holds the lock (nothing is written).\n *\n * @param request - What to install.\n * @param deps - Test seams.\n */\nexport async function runUpdateWorker(request: UpdateWorkerRequest, deps: RunUpdateWorkerDeps = {}): Promise<UpdateWorkerResult | null> {\n const now = deps.now ?? Date.now\n const pid = deps.pid ?? process.pid\n const { cacheDir } = request\n const lock = acquireUpdateLock(cacheDir, request.by, { pid })\n if (!lock.acquired)\n return null\n const startedAt = now()\n const log = openLog(cacheDir)\n log(`# zidane update ${request.id} by ${request.by} (pid ${pid}) ${new Date(startedAt).toISOString()}`)\n const upgradeOptions = {\n onProgress: (event: UpgradeProgressEvent) => log(progressLine(event)),\n ...(request.registry ? { registry: request.registry } : {}),\n ...(request.timeoutMs !== undefined ? { timeoutMs: request.timeoutMs } : {}),\n }\n const upgrade = deps.upgrade ?? upgradeInstalledPackages\n let outcome: UpdateWorkerOutcome\n try {\n if (request.upgradeArgv) {\n outcome = deps.upgradeCommand\n ? { kind: 'exited', exitCode: await deps.upgradeCommand(request.upgradeArgv, lineWriter(log, ''), lineWriter(log, '! ')) }\n : { kind: 'lost', error: 'this worker cannot run zidane upgrade' }\n }\n else if (request.auto) {\n outcome = await runAutoInstallLocked({ status: request.auto, cacheDir, targets: request.targets, by: request.by, upgradeOptions, upgrade, now, ...(deps.env ? { env: deps.env } : {}) })\n }\n else {\n // A manual install is the retry for a failed automatic one.\n clearFailedUpdate(cacheDir)\n const report = await upgrade({\n ...upgradeOptions,\n targets: request.targets,\n cacheDir,\n ...(request.channel ? { channel: request.channel } : {}),\n ...(request.force ? { force: true } : {}),\n })\n outcome = { kind: 'ran', report, error: upgradeError(report) }\n }\n }\n catch (err) {\n outcome = { kind: 'lost', error: err instanceof Error ? err.message : String(err) }\n }\n log(`# ${outcomeLine(outcome)}`)\n const result: UpdateWorkerResult = { v: UPDATE_WORKER_PROTOCOL, id: request.id, pid, by: request.by, startedAt, finishedAt: now(), outcome }\n try {\n writeJsonFileAtomic(join(cacheDir, UPDATE_RESULT_FILE), result, { ensureDir: true })\n }\n catch {\n // The starter sees the exit without a result and reports the run lost.\n }\n lock.release()\n return result\n}\n\n/**\n * Entry of the worker process. Returns the exit code: `0` once a result is\n * written, `3` when another install holds the lock, `2` for a bad request.\n *\n * @param argv - Arguments after the worker's own command (`--request <value>`).\n * @param deps - Overrides for tests: the `upgradeCommand` to run.\n */\nexport async function runUpdateWorkerMain(argv: readonly string[], deps: Pick<RunUpdateWorkerDeps, 'upgradeCommand'> = {}): Promise<number> {\n const request = decodeUpdateWorkerRequest(argv)\n if (!request)\n return EXIT_BAD_REQUEST\n const result = await runUpdateWorker(request, deps)\n return result ? 0 : EXIT_LOCKED\n}\n\n/** Options for {@link startUpdateWorker}. */\nexport interface StartUpdateWorkerOptions {\n /** Argv that runs the worker; `--request <value>` is appended. E.g. `['/usr/local/bin/zidane', '__update-worker']`. */\n command: readonly string[]\n /** Worker environment (PATH decides which package managers it finds). Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv\n /** What to install. */\n request: Omit<UpdateWorkerRequest, 'v' | 'id'>\n /** Each line the worker logs for this run. */\n onLine?: (line: string) => void\n /** Log and result poll interval. Defaults to 250 ms. */\n pollMs?: number\n /** Injected for tests. */\n spawn?: typeof nodeSpawn\n}\n\n/**\n * Starts the worker detached and follows it until it reports. The worker\n * keeps going when this process exits. Never rejects.\n *\n * A held lock answers `skipped / locked` without starting anything. A worker\n * that cannot start, or stops without a result, answers `lost`; for an\n * automatic install that also records the version as a transient failure (in\n * memory when the cache dir can't be written), so it is retried on the\n * backoff, not on every check.\n *\n * @param options - Worker command, environment, request and log callback.\n */\nexport async function startUpdateWorker(options: StartUpdateWorkerOptions): Promise<UpdateWorkerOutcome> {\n const { cacheDir } = options.request\n const held = liveHolder(cacheDir)\n if (held)\n return { kind: 'skipped', reason: 'locked', holder: held }\n const request: UpdateWorkerRequest = { ...options.request, v: UPDATE_WORKER_PROTOCOL, id: randomUUID() }\n const [bin, ...args] = options.command\n const exit = new Promise<{ code: number | null, error?: string }>((resolve) => {\n try {\n const child = (options.spawn ?? nodeSpawn)(bin!, [...args, '--request', encodeUpdateWorkerRequest(request)], {\n detached: true,\n stdio: 'ignore',\n env: options.env ?? process.env,\n } satisfies SpawnOptions)\n child.once('error', err => resolve({ code: null, error: err.message }))\n child.once('exit', code => resolve({ code }))\n child.unref()\n }\n catch (err) {\n resolve({ code: null, error: err instanceof Error ? err.message : String(err) })\n }\n })\n const outcome = await follow({\n cacheDir,\n pollMs: options.pollMs,\n onLine: options.onLine,\n header: request.id,\n done: async () => {\n const result = readUpdateWorkerResult(cacheDir)\n return result?.id === request.id ? result.outcome : null\n },\n exit,\n })\n if (outcome.kind === 'lost' && request.auto?.latest) {\n const version = request.auto.latest\n writeFailedUpdate(cacheDir, nextFailedUpdate(cacheDir, { version, error: outcome.error, transient: true, at: Date.now() }))\n }\n return outcome\n}\n\n/**\n * An `autoInstallUpdate`-shaped function that runs the install in the\n * detached worker, for `useAutoInstallUpdate`'s `install` seam. A lost\n * worker reads as a failed install.\n *\n * @param command - Argv that runs the worker.\n * @param extra - Worker environment and log callback.\n * @param extra.env - Worker environment. Defaults to `process.env`.\n * @param extra.onLine - Each line the worker logs.\n */\nexport function autoInstallViaWorker(command: readonly string[], extra: { env?: NodeJS.ProcessEnv, onLine?: (line: string) => void } = {}): (options: AutoInstallUpdateOptions) => Promise<AutoInstallOutcome> {\n return async (options) => {\n const outcome = await startUpdateWorker({\n command,\n ...extra,\n request: { cacheDir: options.cacheDir, by: options.by, targets: [...options.targets], auto: options.status },\n })\n if (outcome.kind === 'installed' || outcome.kind === 'skipped' || outcome.kind === 'failed')\n return outcome\n const error = outcome.kind === 'lost' ? outcome.error : 'unexpected result from the update worker'\n return { kind: 'failed', version: options.status.latest ?? '', error }\n }\n}\n\n/** Options for {@link followRunningUpdate}. */\nexport interface FollowRunningUpdateOptions {\n /** Directory that holds the lock, log and result. */\n cacheDir: string\n /** Each line logged from now on, starting with the run's lines so far. */\n onLine?: (line: string) => void\n /** Defaults to 250 ms. */\n pollMs?: number\n}\n\n/**\n * Attaches to an install that is already running (a surface that starts while\n * another one's worker installs). `null` when no live install holds the lock.\n * Otherwise resolves once the lock is released: the worker's result, or\n * `null` when the holder was not a worker (`zidane upgrade --in-place`) or\n * left no result.\n *\n * @param options - Cache directory, log callback and poll interval.\n */\nexport function followRunningUpdate(options: FollowRunningUpdateOptions): Promise<UpdateWorkerResult | null> | null {\n const holder = liveHolder(options.cacheDir)\n if (!holder)\n return null\n return (async () => {\n let result: UpdateWorkerResult | null = null\n await follow({\n cacheDir: options.cacheDir,\n pollMs: options.pollMs,\n onLine: options.onLine,\n done: async () => {\n if (liveHolder(options.cacheDir))\n return null\n const last = readUpdateWorkerResult(options.cacheDir)\n result = last?.pid === holder.pid && last.startedAt >= holder.startedAt ? last : null\n return result?.outcome ?? { kind: 'lost', error: 'no result' }\n },\n })\n return result\n })()\n}\n\n/** The holder of a lock that still protects a running install, or `null`. */\nfunction liveHolder(cacheDir: string): UpdateLockHolder | null {\n const holder = readUpdateLock(cacheDir)\n return holder && !isUpdateLockStale(holder) ? holder : null\n}\n\n/**\n * Tail the log and poll `done` until it answers. With `exit`, a worker that\n * exits without `done` answering is `lost` (or `skipped / locked` for exit 3).\n * With `header`, lines before `# zidane update <header>` belong to another\n * run and are skipped.\n */\nasync function follow(input: {\n cacheDir: string\n pollMs?: number\n onLine?: (line: string) => void\n header?: string\n done: () => Promise<UpdateWorkerOutcome | null>\n exit?: Promise<{ code: number | null, error?: string }>\n}): Promise<UpdateWorkerOutcome> {\n const pollMs = input.pollMs ?? 250\n const tail = logTail(join(input.cacheDir, UPDATE_LOG_FILE), input.header)\n let exited: { code: number | null, error?: string } | null = null\n void input.exit?.then((e) => {\n exited = e\n })\n while (true) {\n for (const line of tail())\n input.onLine?.(line)\n const outcome = await input.done()\n if (outcome) {\n for (const line of tail())\n input.onLine?.(line)\n return outcome\n }\n if (exited) {\n // One last look: the result is written before the worker exits.\n const late = await input.done()\n if (late)\n return late\n const { code, error } = exited as { code: number | null, error?: string }\n if (code === EXIT_LOCKED) {\n const holder = readUpdateLock(input.cacheDir)\n return holder ? { kind: 'skipped', reason: 'locked', holder } : { kind: 'lost', error: 'another update held the lock' }\n }\n return { kind: 'lost', error: error ? `could not start the update: ${error}` : `the update stopped (exit ${code ?? 'signal'}) without a result` }\n }\n await new Promise(resolve => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * Reads lines appended to `path` since the last call. A file that shrank or\n * was replaced (rotated) is read again from the start. A half-written last\n * line waits for its newline.\n */\nfunction logTail(path: string, header?: string): () => string[] {\n let offset = 0\n let ino = -1\n let pending = ''\n let started = header === undefined\n return () => {\n let stat\n try {\n stat = statSync(path)\n }\n catch {\n return []\n }\n if (stat.ino !== ino || stat.size < offset) {\n ino = stat.ino\n offset = 0\n pending = ''\n }\n if (stat.size === offset)\n return []\n const buf = Buffer.alloc(stat.size - offset)\n let fd: number | undefined\n try {\n fd = openSync(path, 'r')\n const read = readSync(fd, buf, 0, buf.length, offset)\n offset += read\n pending += buf.subarray(0, read).toString('utf8')\n }\n catch {\n return []\n }\n finally {\n if (fd !== undefined)\n closeSync(fd)\n }\n const parts = pending.split('\\n')\n pending = parts.pop() ?? ''\n const lines: string[] = []\n for (const line of parts) {\n if (!started) {\n started = line.startsWith(`# zidane update ${header} `)\n if (!started)\n continue\n }\n lines.push(line)\n }\n return lines\n }\n}\n\n/** Rotate the previous log away and return an appender capped at {@link UPDATE_LOG_CAP_BYTES}. */\nfunction openLog(cacheDir: string): (line: string) => void {\n const path = join(cacheDir, UPDATE_LOG_FILE)\n try {\n mkdirSync(cacheDir, { recursive: true })\n renameSync(path, `${path}.1`)\n }\n catch {\n // No previous log, or the dir can't be written: logging is best-effort.\n }\n let written = 0\n let full = false\n return (line) => {\n if (full)\n return\n const text = `${line}\\n`\n const size = Buffer.byteLength(text)\n try {\n if (written + size > UPDATE_LOG_CAP_BYTES) {\n full = true\n appendFileSync(path, '# log truncated\\n')\n return\n }\n appendFileSync(path, text)\n written += size\n }\n catch {\n // Best-effort.\n }\n }\n}\n\n/** A stream that logs each complete line, prefixed. */\nfunction lineWriter(log: (line: string) => void, prefix: string): NodeJS.WritableStream {\n let buffer = ''\n const flush = (final: boolean) => {\n const parts = buffer.split(/\\r?\\n/)\n buffer = final ? '' : parts.pop() ?? ''\n for (const part of parts) {\n if (part || !final)\n log(`${prefix}${part}`)\n }\n }\n return new Writable({\n write(chunk: Uint8Array | string, _enc, cb) {\n buffer += chunk.toString()\n flush(false)\n cb()\n },\n final(cb) {\n flush(true)\n cb()\n },\n })\n}\n\nfunction progressLine(event: UpgradeProgressEvent): string {\n switch (event.kind) {\n case 'check': return `checking ${event.packageName}`\n case 'install': return `${event.packageName} ${event.from} → ${event.to}: $ ${event.command.join(' ')}`\n case 'log': return event.line\n case 'done': return `${event.result.packageName}: ${event.result.status}${event.result.error ? ` (${event.result.error})` : ''}`\n }\n}\n\nfunction outcomeLine(outcome: UpdateWorkerOutcome): string {\n switch (outcome.kind) {\n case 'installed': return `installed ${outcome.version}`\n case 'skipped': return `skipped: ${outcome.reason}`\n case 'failed': return `failed: ${outcome.error.replaceAll('\\n', '; ')}`\n case 'ran': return outcome.error ? `failed: ${outcome.error.replaceAll('\\n', '; ')}` : 'done'\n case 'exited': return `exit ${outcome.exitCode}`\n case 'lost': return `failed: ${outcome.error}`\n }\n}\n","// ---------------------------------------------------------------------------\n// Footer hint composition.\n//\n// Pure presentation logic — given the current screen, run state, and a\n// resolved keybinding map, produce the ordered list of {@link Hint}s\n// the bottom-bar Footer should render. Lives in `zidane/chat` (not\n// `zidane/tui`) because it's renderer-agnostic: the only types involved\n// are `Hint`, `Screen`, `SessionMeta`, `Settings['uiMode']`, and `KeyBindings`. A GUI\n// shell can consume the same list and paint it however it likes.\n//\n// Priority order is intentional and load-bearing:\n//\n// pending approval > live pending interaction > resumed pending\n// interaction > busy (streaming) > auth screen > onboarding screen (no\n// hints) > sessions screen > chat screen (idle).\n//\n// The first four return state-specific hint sets unconditionally —\n// `uiMode` is ignored there because those affordances (approve / abort\n// / navigate) are not the noise the minimal mode is targeting. Minimal\n// mode only collapses the chat-idle row.\n// ---------------------------------------------------------------------------\n\nimport type { Hint } from './hints'\nimport type { KeyBindings } from './keybindings'\nimport type { Screen, SessionMeta, Settings } from './types'\nimport { formatBindingForDisplay } from './keybindings'\n\n/**\n * Options bag for {@link buildHints}. Flat shape so call sites read as a\n * checklist of \"what state do I have to surface\" — easy to thread\n * through React `useMemo` deps and to construct fresh in tests.\n */\nexport interface BuildHintsOptions {\n /** Screen the footer is shown on. */\n screen: Screen\n /** True while the agent is running a turn. */\n busy: boolean\n /** True while a safe-mode approval prompt is pending. */\n pending: boolean\n /**\n * True when a LIVE pending interaction is on screen (agent is mid-run,\n * awaiting the tool's Promise). Same nav hints as the safe-mode\n * approval picker — `abortRun` aborts the run.\n */\n pendingInteractionLive: boolean\n /**\n * True when the chat screen is showing a resumed (not live) pending\n * interaction. The agent isn't running; esc leaves the form without\n * persisting a response, so the hint reads \"leave for later\" — the\n * session re-enqueues the same interaction on next activation.\n */\n pendingInteractionResumed: boolean\n /** Active session, or `null` when none is open. */\n currentSession: SessionMeta | null\n /** True when more than one agent profile is available, so the agent switch hint is useful. */\n hasMultipleAgents: boolean\n /**\n * Chat-screen chrome density — `'minimal'` collapses the idle chat\n * hint row to just `agent / model / settings`. Busy and pending\n * states keep their full hint sets because those\n * affordances are load-bearing (abort, navigate, approve).\n */\n uiMode: Settings['uiMode']\n /** Label of the active model, or `null` when none is selected. */\n modelLabel: string | null\n /** Foreground color for the model label. */\n modelColor: string\n /** Current reasoning effort label, or `null` when the model has no reasoning knob. */\n effortLabel: string | null\n /** Foreground color for the effort label. */\n effortColor: string\n /**\n * Foreground for the `/l` chord that introduces the effort label. Unset, it\n * takes the color of the model key it extends, so the pair reads as one binding.\n */\n effortKeyColor?: string\n /**\n * Comma-joined labels of the enabled model options (e.g. `'fast'`), or `null`\n * when none are on. Appended after the effort label in the model chord — when\n * the model has no reasoning knob, it rides the chord on its own.\n */\n modelOptionsLabel?: string | null\n /** Label of the active agent profile. */\n agentLabel: string\n /** Foreground color for the agent label. */\n agentColor: string\n /**\n * Resolved keybindings — every hint key string flows through this so\n * a user-level rebind (e.g. `\"openSettings\": \"ctrl+p\"`) shows up live\n * in the bottom bar without us hard-coding the default `'ctrl+o'`.\n */\n keybindings: KeyBindings\n /**\n * Number of currently-dispatching tool calls. Drives the\n * `<cancelToolCall> cancel` chip in the `busy` hint row and the\n * `cancel task` chip on the idle chat row. Without\n * it, the binding stays inert and there's no point advertising the\n * shortcut. Falls back to \"no chip\" when no tools are in flight\n * (model is mid-stream but hasn't dispatched yet).\n */\n inFlightToolCount: number\n /**\n * Number of currently-active skills. Drives the footer's\n * `✦ N skill(s)` chip on the chat screen — purely informational,\n * a passive surface for \"tool restrictions are in effect\". Hidden\n * when `0` so the row stays uncluttered for the common case.\n */\n activeSkillCount: number\n /** Foreground for the skills chip (typically `COLOR.brand`). */\n skillsChipColor: string\n /**\n * Progress label for the todo chip (`\"2/5\"` from `todoProgressLabel`), or\n * `null` to hide it.\n *\n * As a `Hint` it inherits the row's click-to-keypress\n * contract for free (`dispatchHintKey`), so clicking it opens the list the\n * same way `ctrl+t` does — and it sits below the prompt like every other\n * control.\n */\n todoProgress: string | null\n /**\n * Pre-built `↑ vX.Y.Z` chip from `buildUpdateHint(useUpdateCheck())`,\n * or `null` when no update is available. Always last in the chat-screen\n * row so it's the first to drop when the terminal narrows.\n */\n updateHint: Hint | null\n /**\n * Pre-built prompt-cache toast, non-null only for a few seconds after a\n * `'cache:break'` hook fired (the host owns the visibility timer and the\n * severity split): warn-colored `⚡ cache break in <culprit>: +Nk tok, ~$0.48`\n * for attributable breaks, dim `⚡ cache expired: +Nk tok, ~$0.48` for\n * byte-stable TTL expiry — routine across human think-time, so it must not\n * cry wolf. Sits beside `updateHint` at the droppable end of the row: a\n * transient cost signal, not an action.\n */\n cacheBreakHint?: Hint | null\n /**\n * Sessions-screen list scope — `true` when the list spans all\n * projects. Drives the `tab` hint's label, which advertises the scope\n * the toggle switches TO. Optional so non-sessions callers can omit it.\n */\n sessionsScopeAll?: boolean\n /** Sessions screen: there is another space to switch to. Adds the `cycleSpaceInList` hint (`shift+tab space`; `←→ space` while unbound). */\n sessionsSpaceSwitch?: boolean\n}\n\n/**\n * Build the footer's shortcut hints for the current screen. On the chat\n * screen the model id rides next to its `ctrl+m` shortcut and the agent\n * label rides next to `shift+tab`, each in its accent color — the bar\n * doubles as the status display without needing separate badges. When\n * the active model exposes reasoning, the `ctrl+m` hint grows a\n * secondary `/l` chord with the current effort label, surfacing the\n * effort picker as a discoverable, in-place affordance.\n *\n * @param options - Screen, run state, labels, colors, and keybindings that decide which hints appear.\n */\nexport function buildHints(options: BuildHintsOptions): Hint[] {\n const {\n screen,\n busy,\n pending,\n pendingInteractionLive,\n pendingInteractionResumed,\n currentSession,\n hasMultipleAgents,\n uiMode,\n modelLabel,\n modelColor,\n effortLabel,\n effortColor,\n effortKeyColor,\n modelOptionsLabel,\n agentLabel,\n agentColor,\n keybindings,\n inFlightToolCount,\n activeSkillCount,\n skillsChipColor,\n todoProgress,\n updateHint,\n cacheBreakHint = null,\n sessionsScopeAll,\n sessionsSpaceSwitch,\n } = options\n\n // Todo progress chip. Built BEFORE the priority branches below because it is\n // STATUS, not a shortcut: it has to survive every one of them. It reports\n // todo progress independent of `busy`, and an `in_progress` todo is set\n // precisely while the agent is working and calling `todowrite`. Building\n // it after the early returns would hide it for the whole window it exists\n // to cover.\n //\n // Appended LAST in each branch so `clipHintsToWidth` (which drops from the\n // right) sheds it before the abort/navigate affordances, which must stay.\n const todosChip: Hint | null = todoProgress !== null\n ? { key: keybindings.openTodos, label: `${todoProgress} todos` }\n : null\n const withTodos = (hints: Hint[]): Hint[] => todosChip ? [...hints, todosChip] : hints\n\n // Priority: safe-mode approval > live interaction > resumed\n // interaction > plain streaming. All three pending shapes share the\n // ↑↓ / ↵ nav prefix; only the esc semantics differ.\n if (pending)\n return withTodos([{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: formatBindingForDisplay(keybindings.abortRun), label: 'abort run' }])\n if (pendingInteractionLive)\n return withTodos([{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: formatBindingForDisplay(keybindings.abortRun), label: 'abort run' }])\n if (pendingInteractionResumed)\n return withTodos([{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'leave for later' }])\n if (busy) {\n // The run-wide abort, then the way out. The per-call cancel\n // (`cancelToolCall`) stays bound but unadvertised mid-run: the abort\n // covers the common case, and the chip only crowded the row.\n const baseBusyHints: Hint[] = []\n baseBusyHints.push({ key: formatBindingForDisplay(keybindings.abortRun), label: 'abort' })\n baseBusyHints.push({ key: 'esc', label: 'sessions' })\n // The model stays in view during the run it serves (a fallback shows in\n // its warn colour exactly then); no key, since the picker waits for idle.\n if (modelLabel)\n baseBusyHints.push({ key: '', label: modelLabel, labelColor: modelColor })\n return withTodos(baseBusyHints)\n }\n if (screen === 'auth')\n return [{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'exit' }]\n // The setup wizard shows the keys of each step inside its frame.\n if (screen === 'onboarding')\n return []\n if (screen === 'sessions') {\n return [\n { key: '↑↓', label: 'navigate' },\n { key: '↵', label: 'open' },\n { key: 'tab', label: sessionsScopeAll ? 'current project' : 'all projects' },\n ...(sessionsSpaceSwitch ? [{ key: keybindings.cycleSpaceInList || '←→', label: 'space' }] : []),\n { key: keybindings.openSessionDetails, label: 'session' },\n { key: keybindings.openSettings, label: 'settings' },\n { key: 'esc', label: currentSession ? 'back' : 'exit' },\n ]\n }\n // Chat screen. Prompt-input shortcuts (`↵ send`, `shift+↵ newline`,\n // `↑↓ history`) live in the prompt box's overlay title — the bottom\n // bar only carries the most common agent / model / global affordances. The\n // `cycleAgent` binding shows the active label inline; the\n // `openModelPicker` binding shows the active model id inline; etc.\n // `cycleAgent` is suppressed when there's only one profile and\n // `openSessionDetails` only renders once a session exists.\n //\n // Minimal UI mode: strip the chat-idle bottom bar to just `agent`\n // (when there's >1 profile), `model/effort`, and `settings`. The\n // full keybinding catalog stays reachable from inside settings.\n // Busy / pending / select-turn / queue branches above already exited\n // before this point — minimal only affects the resting chat row.\n //\n // Compose the model hint. When the active model supports reasoning,\n // the primary `<openModelPicker> model` pair grows a secondary\n // `/l effort` chord that doubles as the discoverability hint for the\n // effort picker (`openEffortPicker`) and the status display for the\n // current effort. The chord shares the model accent so the pair\n // reads as one keyboard binding across every theme.\n // The `/l` chord (from `openEffortPicker`) carries reasoning effort plus any enabled model options,\n // all space-separated so they read as one model-info run (e.g.\n // `claude-opus-4-8 medium fast`). When the model has no reasoning knob, the\n // options ride the chord on their own.\n const chordLabel = [effortLabel, modelOptionsLabel].filter(Boolean).join(' ') || null\n const modelHint: Hint | null = modelLabel\n ? {\n key: keybindings.openModelPicker,\n label: modelLabel,\n labelColor: modelColor,\n ...(chordLabel\n ? { extra: { key: shortChord(keybindings.openEffortPicker), keySpec: keybindings.openEffortPicker, ...(effortKeyColor ? { keyColor: effortKeyColor } : {}), label: chordLabel, labelColor: effortColor } }\n : {}),\n }\n : null\n // Skills chip — purely informational, no shortcut wired. We re-use\n // the Hint shape's `key` slot to render the glyph (`✦`) so the row\n // layout (key + label, padded breakpoints) stays consistent with\n // every other entry. The chip is omitted when no skills are active\n // so the row stays uncluttered for the common case.\n const skillsChip: Hint | null = activeSkillCount > 0\n ? {\n key: '✦',\n keyColor: skillsChipColor,\n label: activeSkillCount === 1 ? '1 skill' : `${activeSkillCount} skills`,\n labelColor: skillsChipColor,\n }\n : null\n // Background-task chip — fires when the user is idle (not `busy`)\n // but a task is still running. Gives them a discoverable way to\n // reach `ctrl+k` for kill. The `busy` branch above already covers\n // the dispatching case; this one is for \"agent finished its turn,\n // task still chugging\".\n const cancelTaskChip: Hint | null = inFlightToolCount > 0\n ? {\n key: keybindings.cancelToolCall,\n label: inFlightToolCount === 1 ? 'cancel task' : `cancel task (${inFlightToolCount})`,\n }\n : null\n // Minimal mode short-circuits the full composition. Same agent / model\n // hints as the full row (so a multi-agent setup still cycles, and the\n // model + effort chord stays visible), plus `settings` as the\n // discoverability anchor for everything we just dropped (keybindings\n // included — they have a tab in there).\n if (uiMode === 'minimal') {\n return [\n ...(hasMultipleAgents ? [{ key: keybindings.cycleAgent, label: agentLabel, labelColor: agentColor }] : []),\n ...(modelHint ? [modelHint] : []),\n { key: keybindings.openSettings, label: 'settings' },\n ]\n }\n return [\n ...(hasMultipleAgents ? [{ key: keybindings.cycleAgent, label: agentLabel, labelColor: agentColor }] : []),\n ...(modelHint ? [modelHint] : []),\n ...(skillsChip ? [skillsChip] : []),\n ...(todosChip ? [todosChip] : []),\n ...(cancelTaskChip ? [cancelTaskChip] : []),\n ...(currentSession ? [{ key: keybindings.openSessionDetails, label: 'session' }] : []),\n { key: keybindings.openSettings, label: 'settings' },\n { key: 'esc', label: 'sessions' },\n // Update chip lives at the right-most position so `clipHintsToWidth`\n // drops it first on narrow terminals — discoverability is nice but\n // the action shortcuts must stay.\n ...(cacheBreakHint ? [cacheBreakHint] : []),\n ...(updateHint ? [updateHint] : []),\n ]\n}\n\n/**\n * Shorten a binding spec for display as a \"chord continuation\" — the\n * `/l` after `ctrl+m model` in the chat footer. We only strip the\n * `ctrl+` prefix so user-customized chords (`alt+n`, `meta+shift+n`)\n * still render in full. Falls back to the verbatim spec when no\n * `ctrl+` prefix is found, which keeps the visual contract honest:\n * the rendered key always matches the bound trigger.\n *\n * @param spec - Binding spec, such as `ctrl+n`.\n */\nexport function shortChord(spec: string): string {\n return spec.startsWith('ctrl+') ? `/${spec.slice('ctrl+'.length)}` : spec\n}\n","/**\n * @-completion path display formatter.\n *\n * The `@`-popover walks the project from the git root so the user sees\n * the whole tree regardless of where they launched the TUI. The\n * agent's CWD-resolving tools (`read_file`, `glob`, `edit`, …) read\n * relative paths against `process.cwd()`, which is frequently a\n * subdirectory of that root. Emitting the discovery's raw\n * project-root-relative path into the prompt produces a `<cwd>/<rel>`\n * miss — the bug this helper exists to close.\n *\n * Rule:\n *\n * - File at `cwd` itself → `.`.\n * - File under `cwd` (subtree) → forward-slashed CWD-relative,\n * no `..`.\n * - File above `cwd` but still inside the project → CWD-relative\n * with `..` traversal (e.g. `../EDIT_THIS.md`). Shorter and more\n * scannable than absolute, and the agent's tools resolve both.\n * - File outside the project (input was passed absolute) → returned\n * verbatim. Inputs that arrive as project-root-relative strings\n * are always inside the project by construction, so this branch\n * only kicks in when a direct caller hands the function an\n * absolute target.\n *\n * Pure, no I/O, deterministic.\n */\n\nimport { isAbsolute, posix, relative, resolve, sep } from 'node:path'\n\n/**\n * Convert a project-root-relative path (as emitted by `listProjectFiles`)\n * into the form the agent's tools — and the user — will actually find\n * given the current `cwd`.\n *\n * Inputs:\n * - `projectRelativePath` — forward-slashed, no leading slash. Returned\n * verbatim when it's empty or already absolute (defensive — the\n * absolute-input branch is the de-facto \"outside the project\"\n * escape; the catalog itself never emits absolute paths).\n * - `projectRoot` — absolute path of the discovery anchor.\n * - `cwd` — absolute path of `process.cwd()` at the moment of\n * insertion.\n *\n * Output is always either an absolute path (only when the input arrived\n * absolute) OR a CWD-relative path. The relative form may include\n * `../` segments when the target sits above `cwd` within the project,\n * which keeps cross-tree references short and scannable\n * (e.g. `../EDIT_THIS.md` instead of `/Users/.../zidane/EDIT_THIS.md`).\n *\n * @param projectRelativePath - Forward-slashed path relative to `projectRoot`.\n * @param projectRoot - Absolute path of the discovery anchor.\n * @param cwd - Absolute working directory the result is relative to.\n */\nexport function formatPathForCwd(\n projectRelativePath: string,\n projectRoot: string,\n cwd: string,\n): string {\n if (projectRelativePath.length === 0)\n return projectRelativePath\n if (isAbsolute(projectRelativePath))\n return projectRelativePath\n const target = resolve(projectRoot, projectRelativePath)\n const here = resolve(cwd)\n if (target === here)\n return '.'\n const rel = relative(here, target)\n if (rel.length === 0)\n return '.'\n return sep === '/' ? rel : rel.split(sep).join(posix.sep)\n}\n"],"mappings":"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"}
|
package/dist/providers/google.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as GOOGLE_DEFAULT_MODEL, n as GOOGLE_EXTRA_MODELS, r as classifyGoogleError, i as google, a as googleCompatible, o as googleVertex, s as resolveGoogleModel, c as resolveVertexAuth, u as toGeminiContents } from "../google-
|
|
1
|
+
export { t as GOOGLE_DEFAULT_MODEL, n as GOOGLE_EXTRA_MODELS, r as classifyGoogleError, i as google, a as googleCompatible, o as googleVertex, s as resolveGoogleModel, c as resolveVertexAuth, u as toGeminiContents } from "../google-D1zjAViO.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as validateQuery, g as projectNameOf, l as validateSort } from "./validate-Cv5unNYq.js";
|
|
2
|
-
import { $t as DisposedError, lt as assertUsable } from "./registry-
|
|
2
|
+
import { $t as DisposedError, lt as assertUsable } from "./registry-DTotchq6.js";
|
|
3
3
|
import { d as querySessions, x as SessionQueryError } from "./references-BEKthBnn.js";
|
|
4
4
|
//#region src/extensions/runtime/domains/sessions.ts
|
|
5
5
|
/**
|
|
@@ -805,4 +805,4 @@ function watchProviderAccounts(catalog, integrations, sources = []) {
|
|
|
805
805
|
//#endregion
|
|
806
806
|
export { createModelsCatalog as a, addTransform as c, reloadWith as d, createCatalogSet as f, fieldsReadBy as g, createSessionsDomain as h, watchProviderAccounts as i, extensionOrigin as l, clonePlain as m, createProvidersCatalog as n, createModelsDomain as o, once as p, createProvidersDomain as r, modelKey as s, accountModelKey as t, owned as u };
|
|
807
807
|
|
|
808
|
-
//# sourceMappingURL=providers-
|
|
808
|
+
//# sourceMappingURL=providers-CsYrxuX1.js.map
|