@noir-ai/cli 1.7.2-beta.1 → 1.7.3-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/bin.js +19 -10
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-6BCKFJ5Z.js → chunk-3NT7RW6K.js} +65 -1
  4. package/dist/{chunk-6BCKFJ5Z.js.map → chunk-3NT7RW6K.js.map} +1 -1
  5. package/dist/{chunk-RYY2FEIQ.js → chunk-6NRDGRCF.js} +8 -3
  6. package/dist/chunk-6NRDGRCF.js.map +1 -0
  7. package/dist/{chunk-YZGQDUJZ.js → chunk-B7OKXQKF.js} +2 -2
  8. package/dist/{chunk-7WMO6THL.js → chunk-KQYVVGOH.js} +3 -3
  9. package/dist/{chunk-ZA2URQAP.js → chunk-VW6KKJV4.js} +29 -9
  10. package/dist/chunk-VW6KKJV4.js.map +1 -0
  11. package/dist/{create-5D5BTGQ7.js → create-RHFPB5Q2.js} +4 -4
  12. package/dist/index.js +3 -3
  13. package/dist/{install-RB2M4VUA.js → install-M54KESYS.js} +3 -3
  14. package/dist/{sync-MAQAT33P.js → sync-ZGCGL3OU.js} +3 -3
  15. package/dist/tui/{chunk-A27BSG6W.js → chunk-A2Q74DOV.js} +65 -1
  16. package/dist/tui/{chunk-A27BSG6W.js.map → chunk-A2Q74DOV.js.map} +1 -1
  17. package/dist/tui/{chunk-OCKOVYUK.js → chunk-A2YMK3HL.js} +8 -3
  18. package/dist/tui/chunk-A2YMK3HL.js.map +1 -0
  19. package/dist/tui/{chunk-AYJJCWTI.js → chunk-KBM2UXAC.js} +3 -3
  20. package/dist/tui/{chunk-EH3XT7OC.js → chunk-KQXGWFXJ.js} +29 -9
  21. package/dist/tui/chunk-KQXGWFXJ.js.map +1 -0
  22. package/dist/tui/{chunk-X2VUST7G.js → chunk-MGRKJMHG.js} +2 -2
  23. package/dist/tui/{create-KNHRFLW5.js → create-DH2VPDVS.js} +4 -4
  24. package/dist/tui/index.js +19 -10
  25. package/dist/tui/index.js.map +1 -1
  26. package/dist/tui/{install-MPHQ65JD.js → install-BHXP6LG5.js} +3 -3
  27. package/dist/tui/{sync-RUHBKBMB.js → sync-DT4JKXZK.js} +3 -3
  28. package/dist/tui/{update-QPRYOSQN.js → update-FEYBGMVO.js} +4 -4
  29. package/dist/{update-6GUYY2OQ.js → update-M74XT6CS.js} +4 -4
  30. package/package.json +9 -9
  31. package/dist/chunk-RYY2FEIQ.js.map +0 -1
  32. package/dist/chunk-ZA2URQAP.js.map +0 -1
  33. package/dist/tui/chunk-EH3XT7OC.js.map +0 -1
  34. package/dist/tui/chunk-OCKOVYUK.js.map +0 -1
  35. /package/dist/{chunk-YZGQDUJZ.js.map → chunk-B7OKXQKF.js.map} +0 -0
  36. /package/dist/{chunk-7WMO6THL.js.map → chunk-KQYVVGOH.js.map} +0 -0
  37. /package/dist/{create-5D5BTGQ7.js.map → create-RHFPB5Q2.js.map} +0 -0
  38. /package/dist/{install-RB2M4VUA.js.map → install-M54KESYS.js.map} +0 -0
  39. /package/dist/{sync-MAQAT33P.js.map → sync-ZGCGL3OU.js.map} +0 -0
  40. /package/dist/tui/{chunk-AYJJCWTI.js.map → chunk-KBM2UXAC.js.map} +0 -0
  41. /package/dist/tui/{chunk-X2VUST7G.js.map → chunk-MGRKJMHG.js.map} +0 -0
  42. /package/dist/tui/{create-KNHRFLW5.js.map → create-DH2VPDVS.js.map} +0 -0
  43. /package/dist/tui/{install-MPHQ65JD.js.map → install-BHXP6LG5.js.map} +0 -0
  44. /package/dist/tui/{sync-RUHBKBMB.js.map → sync-DT4JKXZK.js.map} +0 -0
  45. /package/dist/tui/{update-QPRYOSQN.js.map → update-FEYBGMVO.js.map} +0 -0
  46. /package/dist/{update-6GUYY2OQ.js.map → update-M74XT6CS.js.map} +0 -0
@@ -2,11 +2,11 @@ import {
2
2
  buildMigrationPlan,
3
3
  install,
4
4
  installManagedNode
5
- } from "./chunk-OCKOVYUK.js";
6
- import "./chunk-A27BSG6W.js";
5
+ } from "./chunk-A2YMK3HL.js";
6
+ import "./chunk-A2Q74DOV.js";
7
7
  export {
8
8
  buildMigrationPlan,
9
9
  install,
10
10
  installManagedNode
11
11
  };
12
- //# sourceMappingURL=install-MPHQ65JD.js.map
12
+ //# sourceMappingURL=install-BHXP6LG5.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  buildConflictOpts,
3
3
  checkWritePathDedup
4
- } from "./chunk-X2VUST7G.js";
4
+ } from "./chunk-MGRKJMHG.js";
5
5
  import {
6
6
  resolveInteractive
7
- } from "./chunk-A27BSG6W.js";
7
+ } from "./chunk-A2Q74DOV.js";
8
8
 
9
9
  // src/sync.ts
10
10
  import { existsSync } from "fs";
@@ -80,4 +80,4 @@ function resolveSyncHost(root, opts) {
80
80
  export {
81
81
  sync
82
82
  };
83
- //# sourceMappingURL=sync-RUHBKBMB.js.map
83
+ //# sourceMappingURL=sync-DT4JKXZK.js.map
@@ -3,13 +3,13 @@ import {
3
3
  buildUpdateTarget,
4
4
  runAsyncUpdateCheck,
5
5
  update
6
- } from "./chunk-EH3XT7OC.js";
7
- import "./chunk-OCKOVYUK.js";
8
- import "./chunk-A27BSG6W.js";
6
+ } from "./chunk-KQXGWFXJ.js";
7
+ import "./chunk-A2YMK3HL.js";
8
+ import "./chunk-A2Q74DOV.js";
9
9
  export {
10
10
  DEFAULT_UPDATE_CONFIG,
11
11
  buildUpdateTarget,
12
12
  runAsyncUpdateCheck,
13
13
  update
14
14
  };
15
- //# sourceMappingURL=update-QPRYOSQN.js.map
15
+ //# sourceMappingURL=update-FEYBGMVO.js.map
@@ -4,13 +4,13 @@ import {
4
4
  buildUpdateTarget,
5
5
  runAsyncUpdateCheck,
6
6
  update
7
- } from "./chunk-ZA2URQAP.js";
8
- import "./chunk-RYY2FEIQ.js";
9
- import "./chunk-6BCKFJ5Z.js";
7
+ } from "./chunk-VW6KKJV4.js";
8
+ import "./chunk-6NRDGRCF.js";
9
+ import "./chunk-3NT7RW6K.js";
10
10
  export {
11
11
  DEFAULT_UPDATE_CONFIG,
12
12
  buildUpdateTarget,
13
13
  runAsyncUpdateCheck,
14
14
  update
15
15
  };
16
- //# sourceMappingURL=update-6GUYY2OQ.js.map
16
+ //# sourceMappingURL=update-M74XT6CS.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noir-ai/cli",
3
- "version": "1.7.2-beta.1",
3
+ "version": "1.7.3-beta.1",
4
4
  "description": "Noir CLI — the `noir` command tree (commander + @clack/prompts), the shell entry point to the Noir AI toolkit.",
5
5
  "license": "MIT",
6
6
  "author": "agaaaptr",
@@ -55,14 +55,14 @@
55
55
  "ora": "^8.1.0",
56
56
  "picocolors": "^1.1.1",
57
57
  "react": "^19.2.0",
58
- "@noir-ai/adapters": "1.7.2-beta.1",
59
- "@noir-ai/context": "1.7.2-beta.1",
60
- "@noir-ai/core": "1.7.2-beta.1",
61
- "@noir-ai/daemon": "1.7.2-beta.1",
62
- "@noir-ai/create": "1.7.2-beta.1",
63
- "@noir-ai/model": "1.7.2-beta.1",
64
- "@noir-ai/skills": "1.7.2-beta.1",
65
- "@noir-ai/store": "1.7.2-beta.1"
58
+ "@noir-ai/adapters": "1.7.3-beta.1",
59
+ "@noir-ai/context": "1.7.3-beta.1",
60
+ "@noir-ai/core": "1.7.3-beta.1",
61
+ "@noir-ai/create": "1.7.3-beta.1",
62
+ "@noir-ai/daemon": "1.7.3-beta.1",
63
+ "@noir-ai/model": "1.7.3-beta.1",
64
+ "@noir-ai/skills": "1.7.3-beta.1",
65
+ "@noir-ai/store": "1.7.3-beta.1"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@types/node": "^26.1.1",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/install.ts"],"sourcesContent":["import { mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n atomicWriteFile,\n type DetectResult,\n detectActiveMethod,\n detectInstallMethods,\n loadProjectInfo,\n MANAGED_NODE_VERSION,\n NOIR_VERSION,\n noirHome,\n type ProvisionedNode,\n type ProvisionOptions,\n provisionManagedNode,\n readInstallRecord,\n runManagerCmd,\n writeInstallRecord,\n} from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail, info, success, warn } from '../output.js';\n\nexport interface InstallOptions extends CliOptions {\n list?: boolean;\n uninstallPrev?: boolean;\n spec?: string;\n /** Dismiss the migration banner for the current CLI version (persists in\n * install.json's `dismissedVersions`). Typically passed with `--list`. */\n dismiss?: boolean;\n}\n\nexport interface MigrationPlan {\n detected: DetectResult[];\n currentMethod: string;\n targetSpec: string;\n installedVersion: string | null;\n shouldMigrate: boolean;\n isDowngrade: boolean;\n /**\n * I2 — `update.minVersion` floor (config.ts:238). True when the resolved\n * target is a concrete version below the configured floor. Channel targets\n * (`latest`/`beta`) resolve to the newest at install time and never trip it.\n * Enforced in {@link install} with the same warn/refuse pattern as the\n * downgrade guard.\n */\n belowMinVersion: boolean;\n nativeVersion: string;\n prevUninstallCmd: string | null;\n autoUninstall: boolean; // always false unless --uninstall-prev\n}\n\n/** Per-segment numeric semver comparison. Returns true when `a < b`. */\nfunction semverLt(a: string, b: string): boolean {\n const pa = a.split('.').map(Number);\n const pb = b.split('.').map(Number);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x < y;\n }\n return false;\n}\n\n/** Pure: build the migration plan from detection + target. No I/O. */\nexport function buildMigrationPlan(opts: {\n detected: DetectResult[];\n currentMethod: string;\n targetSpec: string;\n installedVersion: string | null;\n /**\n * The `update.minVersion` floor from config (default `'1.6.0'`). When\n * omitted, the config default applies. See I2.\n */\n minVersion?: string;\n}): MigrationPlan {\n const detected = opts.detected;\n const shouldMigrate = detected.length > 0 && opts.currentMethod !== 'native';\n const nativeVersion = opts.targetSpec;\n let isDowngrade = false;\n if (opts.installedVersion && nativeVersion !== 'latest' && nativeVersion !== 'beta') {\n isDowngrade = semverLt(nativeVersion, opts.installedVersion);\n }\n // I2 — minVersion floor: only a concrete version below the floor trips it;\n // 'latest'/'beta' resolve to the newest at install time (always >= floor).\n const floor = opts.minVersion ?? '1.6.0';\n const belowMinVersion =\n nativeVersion !== 'latest' && nativeVersion !== 'beta' && semverLt(nativeVersion, floor);\n return {\n detected,\n currentMethod: opts.currentMethod,\n targetSpec: opts.targetSpec,\n installedVersion: opts.installedVersion,\n shouldMigrate,\n isDowngrade,\n belowMinVersion,\n nativeVersion,\n prevUninstallCmd: detected[0]?.uninstallCmd ?? null,\n autoUninstall: false,\n };\n}\n\nexport interface InstallManagedNodeOptions {\n channel?: string;\n version?: string;\n env: NodeJS.ProcessEnv;\n /**\n * Forwarded into {@link provisionManagedNode} (P1). Carries the offline mock\n * seams (`fetch`, `exec`, `target`, `signal`) so the install pipeline is\n * unit-testable without network — same pattern as `provisionManagedNode`'s\n * own suite in `packages/core/test/node-provision.test.ts`.\n */\n provision?: Pick<ProvisionOptions, 'fetch' | 'exec' | 'target' | 'signal'>;\n}\n\n/** Normalized record of which runtime backed an install (P2). */\nexport interface InstallManagedNodeResult {\n ok: boolean;\n version: string | null;\n /** What runtime backed this install: `'managed'` (downloaded into ~/.noir)\n * or `'system'` (used the system-Node fallback). */\n runtimeSource?: ProvisionedNode['source'];\n error?: string;\n}\n\n/**\n * Provision the managed Node runtime (P1) and install `@noir-ai/cli` into an\n * isolated `~/.noir/cli` prefix, shimmed from `~/.noir/bin/noir`.\n *\n * Replaces the prior \"not provisioned\" fail branch: instead of requiring the\n * user to pre-run `install.sh`/`install.ps1`, this now delegates to\n * {@link provisionManagedNode} (download + verify + extract, fail-closed), then\n * installs the CLI with the provisioned npm. On a system-Node fallback the\n * fallback node/npm paths are used and a warning is emitted; the install\n * record's `managedRuntimeVersion` reflects which path was taken so `noir\n * doctor` can report it accurately.\n */\nexport async function installManagedNode(\n opts: InstallManagedNodeOptions,\n): Promise<InstallManagedNodeResult> {\n const spec = opts.version ?? opts.channel ?? 'latest';\n const home = noirHome();\n const cliDir = join(home, 'cli');\n const binDir = join(home, 'bin');\n mkdirSync(cliDir, { recursive: true });\n mkdirSync(binDir, { recursive: true });\n\n // Provision the managed Node runtime (P1). Idempotent — re-runs are no-ops.\n // On any failure, provisionManagedNode either falls back to a system Node\n // >=22 (returns { source: 'system' }) or throws — we surface the throw as an\n // {ok:false} envelope rather than letting it kill the CLI.\n let runtime: ProvisionedNode;\n try {\n runtime = await provisionManagedNode({\n ...(opts.provision ?? {}),\n env: opts.env,\n });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n return { ok: false, version: null, error: `managed-Node provision failed: ${reason}` };\n }\n if (runtime.source === 'system') {\n warn(\n `managed-Node download failed; using system Node ${runtime.version} at ${runtime.nodeBin}.`,\n );\n }\n\n const { nodeBin, npmBin } = runtime;\n\n // Install @noir-ai/cli into the isolated prefix using the provisioned npm.\n const { code, stderr } = await runManagerCmd(\n [npmBin, 'install', '-g', `@noir-ai/cli@${spec}`, `--prefix=${cliDir}`],\n { env: opts.env, timeoutMs: 120_000 },\n );\n if (code !== 0) {\n return { ok: false, version: null, error: `npm install failed: ${stderr.slice(0, 300)}` };\n }\n\n // Shim: ~/.noir/bin/noir -> provisioned node + isolated prefix.\n const shim = join(binDir, 'noir');\n const shimBody = `#!/usr/bin/env bash\\n\"${nodeBin}\" \"${join(cliDir, 'lib', 'node_modules', '@noir-ai', 'cli', 'dist', 'bin.js')}\" \"$@\"\\n`;\n atomicWriteFile(shim, shimBody);\n // (POSIX shim; Windows uses a .cmd wrapper -- install.sh/install.ps1, P3.)\n\n // Resolve installed version via the provisioned node.\n const ver = await runManagerCmd(\n [\n nodeBin,\n join(cliDir, 'lib', 'node_modules', '@noir-ai', 'cli', 'dist', 'bin.js'),\n '--version',\n ],\n { env: opts.env, timeoutMs: 30_000 },\n );\n const version = ver.code === 0 ? ver.stdout.trim() : null;\n writeInstallRecord({\n method: 'native',\n version: version ?? '0.0.0',\n channel: opts.channel ?? 'latest',\n installedAt: new Date().toISOString(),\n // Reflect which runtime backs this install: the pinned managed version\n // (e.g. '22.23.2') for managed, or 'system-<v>' for the fallback so doctor\n // can distinguish a fallback install from a real managed one.\n managedRuntimeVersion:\n runtime.source === 'managed' ? MANAGED_NODE_VERSION : `system-${runtime.version}`,\n });\n return { ok: true, version, runtimeSource: runtime.source };\n}\n\nexport async function install(opts: InstallOptions = {}): Promise<void> {\n // --dismiss: append the current CLI version to install.json's\n // `dismissedVersions` so the migration banner stops showing for this\n // version. Idempotent (no duplicate entries). Typically combined with\n // `--list`; works standalone too. Requires an existing install record —\n // if there's none, there's nothing to dismiss (no banner was shown).\n if (opts.dismiss === true) {\n const rec = readInstallRecord();\n if (!rec) {\n info('No install record found; nothing to dismiss.');\n return;\n }\n const dismissed = new Set(rec.dismissedVersions ?? []);\n if (!dismissed.has(NOIR_VERSION)) dismissed.add(NOIR_VERSION);\n writeInstallRecord({ ...rec, dismissedVersions: [...dismissed] });\n success(`Migration banner dismissed for ${NOIR_VERSION}.`);\n if (opts.list !== true) return; // --dismiss alone is done; --list continues below.\n }\n\n if (opts.list === true) {\n const detected = await detectInstallMethods(process.env);\n const jsonOutput = { ok: true, data: { detected } };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify(jsonOutput)}\\n`);\n return;\n }\n info('Detected installs:');\n for (const d of detected) info(` ${d.method} (${d.version ?? 'unknown'})`);\n return;\n }\n\n const currentMethod = detectActiveMethod();\n const detected = await detectInstallMethods(process.env);\n const installedRecord = readInstallRecord();\n // I2 — read the `update.minVersion` floor from the project config (falls\n // back to the config default '1.6.0' when the project isn't initialized or\n // the block is absent). Same try/catch pattern as home.ts's update-config read.\n let minVersion = '1.6.0';\n try {\n minVersion = loadProjectInfo(process.cwd()).config.update.minVersion;\n } catch {\n // Not initialized yet — the config default applies.\n }\n const plan = buildMigrationPlan({\n detected,\n currentMethod,\n targetSpec: opts.spec ?? 'latest',\n installedVersion: installedRecord?.version ?? null,\n minVersion,\n });\n\n // I2 — refuse to install a version below the configured minVersion floor\n // (same warn/refuse pattern as the installed-version downgrade guard).\n if (plan.belowMinVersion) {\n warn(`Target ${plan.nativeVersion} is below the minVersion floor (${minVersion}).`);\n if (opts.noInput === true || opts.input === false) {\n fail(\n EXIT.USAGE,\n `refusing install of ${plan.nativeVersion} (below minVersion ${minVersion})`,\n opts,\n );\n }\n }\n\n if (plan.isDowngrade) {\n warn(`Target ${plan.nativeVersion} is OLDER than installed ${plan.installedVersion}.`);\n // (Interactive confirm is skipped under --no-input; we hard-stop to be safe.)\n if (opts.noInput === true || opts.input === false) {\n fail(\n EXIT.USAGE,\n `refusing downgrade to ${plan.nativeVersion} (installed ${plan.installedVersion})`,\n opts,\n );\n }\n // @clack confirm gate (lazy import) -- same pattern as home.ts.\n }\n\n const result = await installManagedNode({\n channel: plan.nativeVersion === 'beta' ? 'beta' : undefined,\n version: plan.nativeVersion.startsWith('v')\n ? plan.nativeVersion\n : plan.nativeVersion === 'latest' || plan.nativeVersion === 'beta'\n ? undefined\n : plan.nativeVersion,\n env: process.env,\n });\n if (!result.ok) {\n fail(EXIT.ERROR, result.error ?? 'native install failed', opts);\n }\n\n info(`Installed native: ${result.version}.`);\n if (plan.prevUninstallCmd && opts.uninstallPrev !== true) {\n warn(`To finish the migration, uninstall the previous install: ${plan.prevUninstallCmd}`);\n warn(\n ' Or re-run with --uninstall-prev to do it now. Rollback: reinstall via the previous manager.',\n );\n } else if (plan.prevUninstallCmd && opts.uninstallPrev === true) {\n const { code } = await runManagerCmd(plan.prevUninstallCmd.split(' '), { env: process.env });\n if (code === 0) success('Previous install removed.');\n else warn('Previous install NOT removed (non-zero exit). You can remove it manually.');\n }\n // Claude Code #41806/#27910 mitigation: the old npm bin may still resolve\n // first in $PATH after migration (the shell hashes command paths, and a\n // stale global install can shadow ~/.noir/bin). Hint the user to refresh.\n info('If `noir` still points at the old install, run: hash -r && which -a noir');\n success(`noir ${result.version} installed via native. Run \\`noir doctor\\` to verify.`);\n}\n"],"mappings":"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","names":["detected"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/update.ts"],"sourcesContent":["import {\n detectActiveMethod,\n fetchLatestVersion,\n loadProjectInfo,\n readInstallRecord,\n readUpdateCache,\n runManagerCmd,\n shouldCheckForUpdate,\n type UpdateConfigLike,\n writeUpdateCache,\n} from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail, info, success, warn } from '../output.js';\nimport { installManagedNode } from './install.js';\n\nexport const DEFAULT_UPDATE_CONFIG: UpdateConfigLike = {\n checkEnabled: true,\n checkIntervalHours: 24,\n channel: 'latest',\n minVersion: '1.6.0',\n display: 'notice',\n};\n\nexport interface UpdateOptions extends CliOptions {\n check?: boolean;\n spec?: string;\n}\n\nexport interface UpdateTarget {\n method: string;\n targetSpec: string;\n currentVersion: string | null;\n latestKnown: string | null;\n isUpgrade: boolean;\n /**\n * I2 — `update.minVersion` floor (config.ts:238). True when the latest-known\n * version the registry returned is below the configured floor (e.g. a\n * beta/prerelease channel lag, or a yanked dist-tag). Enforced in\n * {@link update} with the same warn/refuse pattern as the downgrade guard.\n */\n belowMinVersion: boolean;\n}\n\nexport function buildUpdateTarget(opts: {\n method: string;\n channel: string;\n spec?: string;\n currentVersion: string | null;\n latestKnown: string | null;\n /**\n * The `update.minVersion` floor from config (default `'1.6.0'`). When\n * omitted, the config default applies. See I2.\n */\n minVersion?: string;\n}): UpdateTarget {\n const targetSpec = opts.spec ?? opts.channel;\n // Semver downgrade guard (T6 hardening): only treat the latest-known version\n // as an upgrade when it is STRICTLY NEWER than the current one. The prior\n // inequality check (`latestKnown !== currentVersion`) would treat a registry\n // that returned an OLDER version (e.g. beta/prerelease channel lag, or a\n // pinned `--spec` older than installed) as an \"upgrade\" and silently\n // downgrade the install.\n const isUpgrade =\n opts.latestKnown != null &&\n opts.currentVersion != null &&\n semverGt(opts.latestKnown, opts.currentVersion);\n // I2 — minVersion floor: refuse a registry-offered version below the floor.\n // null latestKnown is handled separately (registry-unreachable branch).\n const floor = opts.minVersion ?? '1.6.0';\n const belowMinVersion = opts.latestKnown != null && semverLt(opts.latestKnown, floor);\n return {\n method: opts.method,\n targetSpec,\n currentVersion: opts.currentVersion,\n latestKnown: opts.latestKnown,\n isUpgrade,\n belowMinVersion,\n };\n}\n\n/**\n * Per-segment numeric semver comparison (major.minor.patch). Returns true when\n * `a` is strictly greater than `b`. Pre-release suffixes are ignored — Noir\n * versioning is `MAJOR.MINOR.PATCH` only, and the registry's dist-tag is the\n * source of truth for \"latest\". Non-numeric segments coerce to 0. Used here\n * instead of a full semver library to keep the CLI's dependency surface\n * unchanged (the same helper lives in install.ts for the downgrade guard).\n */\nfunction semverGt(a: string, b: string): boolean {\n const pa = a.split('.').map((s) => Number(s) || 0);\n const pb = b.split('.').map((s) => Number(s) || 0);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x > y;\n }\n return false;\n}\n\n/**\n * Per-segment numeric semver `<` (I2 — minVersion floor). Same comparison\n * discipline as {@link semverGt} / install.ts's downgrade guard: numeric per\n * segment, non-numeric → 0. Returns true when `a` is strictly less than `b`.\n */\nfunction semverLt(a: string, b: string): boolean {\n const pa = a.split('.').map((s) => Number(s) || 0);\n const pb = b.split('.').map((s) => Number(s) || 0);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x < y;\n }\n return false;\n}\n\nexport async function update(opts: UpdateOptions = {}): Promise<void> {\n // NOIR_DISABLE_UPDATES is the hard kill-switch for the SELF-UPDATE surface\n // (distinct from NOIR_DISABLE_UPDATE_CHECK, which only gags the background\n // version check). When set, `noir update` refuses to run at all — enterprises\n // can pin this in the environment to enforce \"updates flow only through the\n // package manager / image rebuild, never from inside the CLI\". `fail` is\n // `: never` (always throws) — the explicit `return` is defensive.\n if (process.env.NOIR_DISABLE_UPDATES !== undefined) {\n fail(\n EXIT.USAGE,\n 'self-update is disabled (NOIR_DISABLE_UPDATES is set); update via your package manager or image rebuild.',\n opts,\n );\n return;\n }\n\n const method = detectActiveMethod();\n const rec = readInstallRecord();\n const currentVersion = rec?.version ?? null;\n\n if (opts.check === true) {\n const latest = await fetchLatestVersion('latest');\n info(\n latest\n ? `Latest: ${latest} (you have ${currentVersion ?? 'unknown'})`\n : 'Could not reach the registry.',\n opts,\n );\n return;\n }\n\n // Fetch latest (network; timeout-bound).\n const latest = await fetchLatestVersion('latest');\n // T6 hardening: when the registry was unreachable (fetch returned null), say\n // so explicitly — mirroring `--check` — instead of falling through to the\n // \"up to date\" branch (which the old inequality-based isUpgrade would print\n // because `null != currentVersion`).\n if (latest === null) {\n info('Could not reach the registry.', opts);\n return;\n }\n // I2 — read the `update.minVersion` floor from the project config (falls\n // back to the config default '1.6.0' when the project isn't initialized or\n // the block is absent). Same try/catch pattern as home.ts's update-config read.\n let minVersion = DEFAULT_UPDATE_CONFIG.minVersion;\n try {\n minVersion = loadProjectInfo(process.cwd()).config.update.minVersion;\n } catch {\n // Not initialized yet — the config default applies.\n }\n const target = buildUpdateTarget({\n method,\n channel: 'latest',\n spec: opts.spec,\n currentVersion,\n latestKnown: latest,\n minVersion,\n });\n\n // I2 — refuse to install a registry-offered version below the configured\n // minVersion floor (same warn/refuse pattern as install.ts's downgrade guard).\n // Checked before the isUpgrade branch: a below-floor offer is unsafe even if\n // it happens to be newer than the (very old) current install.\n if (target.belowMinVersion) {\n warn(`Latest known ${target.latestKnown} is below the minVersion floor (${minVersion}).`);\n fail(\n EXIT.USAGE,\n `refusing update to ${target.latestKnown} (below minVersion ${minVersion})`,\n opts,\n );\n }\n\n if (!target.isUpgrade) {\n info(`noir ${currentVersion} is up to date.`, opts);\n return;\n }\n\n if (method === 'native') {\n const res = await installManagedNode({\n version: target.targetSpec === 'latest' ? undefined : target.targetSpec,\n env: process.env,\n });\n if (!res.ok) fail(EXIT.ERROR, res.error ?? 'update failed', opts);\n success(`Updated to ${res.version}.`);\n return;\n }\n\n // npm/pnpm/yarn/bun/homebrew/scoop -> reinstall via the same manager.\n const cmd = updateCmdFor(method, target.targetSpec);\n if (!cmd)\n fail(EXIT.USAGE, `cannot auto-update a ${method} install; use the manager directly`, opts);\n const { code, stderr } = await runManagerCmd(cmd, { env: process.env });\n if (code !== 0) fail(EXIT.ERROR, `update failed: ${stderr.slice(0, 300)}`, opts);\n success('Updated.');\n}\n\nfunction updateCmdFor(method: string, spec: string): string[] | null {\n switch (method) {\n case 'npm':\n return ['npm', 'install', '-g', `@noir-ai/cli@${spec}`];\n case 'pnpm':\n return ['pnpm', 'add', '-g', `@noir-ai/cli@${spec}`];\n case 'yarn':\n return ['yarn', 'global', 'add', `@noir-ai/cli@${spec}`];\n case 'bun':\n return ['bun', 'add', '-g', `@noir-ai/cli@${spec}`];\n case 'homebrew':\n return ['brew', 'upgrade', 'noir'];\n case 'scoop':\n return ['scoop', 'update', 'noir'];\n default:\n return null;\n }\n}\n\n/** Non-blocking, time-boxed startup check. Writes the cache on success; silent on any failure. */\nexport async function runAsyncUpdateCheck(opts: {\n env: NodeJS.ProcessEnv;\n configUpdate: UpdateConfigLike;\n quiet: boolean;\n}): Promise<void> {\n try {\n const cache = readUpdateCache();\n if (!shouldCheckForUpdate({ env: opts.env, configUpdate: opts.configUpdate, cache })) return;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 2000);\n const latest = await fetchLatestVersion(opts.configUpdate.channel, controller.signal);\n clearTimeout(timer);\n if (latest != null) {\n writeUpdateCache({\n lastCheckAt: new Date().toISOString(),\n latestVersion: latest,\n channel: opts.configUpdate.channel,\n });\n }\n // Non-blocking: never print in quiet/CI/non-TTY; a mismatch only nudges.\n } catch {\n // silent on any failure — this is a non-blocking background check\n }\n}\n"],"mappings":"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","names":["latest"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/commands/update.ts"],"sourcesContent":["import {\n detectActiveMethod,\n fetchLatestVersion,\n loadProjectInfo,\n readInstallRecord,\n readUpdateCache,\n runManagerCmd,\n shouldCheckForUpdate,\n type UpdateConfigLike,\n writeUpdateCache,\n} from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail, info, success, warn } from '../output.js';\nimport { installManagedNode } from './install.js';\n\nexport const DEFAULT_UPDATE_CONFIG: UpdateConfigLike = {\n checkEnabled: true,\n checkIntervalHours: 24,\n channel: 'latest',\n minVersion: '1.6.0',\n display: 'notice',\n};\n\nexport interface UpdateOptions extends CliOptions {\n check?: boolean;\n spec?: string;\n}\n\nexport interface UpdateTarget {\n method: string;\n targetSpec: string;\n currentVersion: string | null;\n latestKnown: string | null;\n isUpgrade: boolean;\n /**\n * I2 — `update.minVersion` floor (config.ts:238). True when the latest-known\n * version the registry returned is below the configured floor (e.g. a\n * beta/prerelease channel lag, or a yanked dist-tag). Enforced in\n * {@link update} with the same warn/refuse pattern as the downgrade guard.\n */\n belowMinVersion: boolean;\n}\n\nexport function buildUpdateTarget(opts: {\n method: string;\n channel: string;\n spec?: string;\n currentVersion: string | null;\n latestKnown: string | null;\n /**\n * The `update.minVersion` floor from config (default `'1.6.0'`). When\n * omitted, the config default applies. See I2.\n */\n minVersion?: string;\n}): UpdateTarget {\n const targetSpec = opts.spec ?? opts.channel;\n // Semver downgrade guard (T6 hardening): only treat the latest-known version\n // as an upgrade when it is STRICTLY NEWER than the current one. The prior\n // inequality check (`latestKnown !== currentVersion`) would treat a registry\n // that returned an OLDER version (e.g. beta/prerelease channel lag, or a\n // pinned `--spec` older than installed) as an \"upgrade\" and silently\n // downgrade the install.\n const isUpgrade =\n opts.latestKnown != null &&\n opts.currentVersion != null &&\n semverGt(opts.latestKnown, opts.currentVersion);\n // I2 — minVersion floor: refuse a registry-offered version below the floor.\n // null latestKnown is handled separately (registry-unreachable branch).\n const floor = opts.minVersion ?? '1.6.0';\n const belowMinVersion = opts.latestKnown != null && semverLt(opts.latestKnown, floor);\n return {\n method: opts.method,\n targetSpec,\n currentVersion: opts.currentVersion,\n latestKnown: opts.latestKnown,\n isUpgrade,\n belowMinVersion,\n };\n}\n\n/**\n * Per-segment numeric semver comparison (major.minor.patch). Returns true when\n * `a` is strictly greater than `b`. Pre-release suffixes are ignored — Noir\n * versioning is `MAJOR.MINOR.PATCH` only, and the registry's dist-tag is the\n * source of truth for \"latest\". Non-numeric segments coerce to 0. Used here\n * instead of a full semver library to keep the CLI's dependency surface\n * unchanged (the same helper lives in install.ts for the downgrade guard).\n */\nfunction semverGt(a: string, b: string): boolean {\n const pa = a.split('.').map((s) => Number(s) || 0);\n const pb = b.split('.').map((s) => Number(s) || 0);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x > y;\n }\n return false;\n}\n\n/**\n * Per-segment numeric semver `<` (I2 — minVersion floor). Same comparison\n * discipline as {@link semverGt} / install.ts's downgrade guard: numeric per\n * segment, non-numeric → 0. Returns true when `a` is strictly less than `b`.\n */\nfunction semverLt(a: string, b: string): boolean {\n const pa = a.split('.').map((s) => Number(s) || 0);\n const pb = b.split('.').map((s) => Number(s) || 0);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x < y;\n }\n return false;\n}\n\nexport async function update(opts: UpdateOptions = {}): Promise<void> {\n // NOIR_DISABLE_UPDATES is the hard kill-switch for the SELF-UPDATE surface\n // (distinct from NOIR_DISABLE_UPDATE_CHECK, which only gags the background\n // version check). When set, `noir update` refuses to run at all — enterprises\n // can pin this in the environment to enforce \"updates flow only through the\n // package manager / image rebuild, never from inside the CLI\". `fail` is\n // `: never` (always throws) — the explicit `return` is defensive.\n if (process.env.NOIR_DISABLE_UPDATES !== undefined) {\n fail(\n EXIT.USAGE,\n 'self-update is disabled (NOIR_DISABLE_UPDATES is set); update via your package manager or image rebuild.',\n opts,\n );\n return;\n }\n\n const method = detectActiveMethod();\n const rec = readInstallRecord();\n const currentVersion = rec?.version ?? null;\n\n if (opts.check === true) {\n const latest = await fetchLatestVersion('latest');\n info(\n latest\n ? `Latest: ${latest} (you have ${currentVersion ?? 'unknown'})`\n : 'Could not reach the registry.',\n opts,\n );\n return;\n }\n\n // Fetch latest (network; timeout-bound).\n const latest = await fetchLatestVersion('latest');\n // T6 hardening: when the registry was unreachable (fetch returned null), say\n // so explicitly — mirroring `--check` — instead of falling through to the\n // \"up to date\" branch (which the old inequality-based isUpgrade would print\n // because `null != currentVersion`).\n if (latest === null) {\n info('Could not reach the registry.', opts);\n return;\n }\n // I2 — read the `update.minVersion` floor from the project config (falls\n // back to the config default '1.6.0' when the project isn't initialized or\n // the block is absent). Same try/catch pattern as home.ts's update-config read.\n let minVersion = DEFAULT_UPDATE_CONFIG.minVersion;\n try {\n minVersion = loadProjectInfo(process.cwd()).config.update.minVersion;\n } catch {\n // Not initialized yet — the config default applies.\n }\n const target = buildUpdateTarget({\n method,\n channel: 'latest',\n spec: opts.spec,\n currentVersion,\n latestKnown: latest,\n minVersion,\n });\n\n // I2 — refuse to install a registry-offered version below the configured\n // minVersion floor (same warn/refuse pattern as install.ts's downgrade guard).\n // Checked before the isUpgrade branch: a below-floor offer is unsafe even if\n // it happens to be newer than the (very old) current install.\n if (target.belowMinVersion) {\n warn(`Latest known ${target.latestKnown} is below the minVersion floor (${minVersion}).`);\n fail(\n EXIT.USAGE,\n `refusing update to ${target.latestKnown} (below minVersion ${minVersion})`,\n opts,\n );\n }\n\n if (!target.isUpgrade) {\n info(`noir ${currentVersion} is up to date.`, opts);\n return;\n }\n\n if (method === 'native') {\n const res = await installManagedNode({\n version: target.targetSpec === 'latest' ? undefined : target.targetSpec,\n env: process.env,\n });\n if (!res.ok) fail(EXIT.ERROR, res.error ?? 'update failed', opts);\n success(`Updated to ${res.version}.`);\n return;\n }\n\n // npm/pnpm/yarn/bun/homebrew/scoop -> reinstall via the same manager.\n const cmd = updateCmdFor(method, target.targetSpec);\n if (!cmd)\n fail(EXIT.USAGE, `cannot auto-update a ${method} install; use the manager directly`, opts);\n const { code, stderr } = await runManagerCmd(cmd, { env: process.env });\n if (code !== 0) fail(EXIT.ERROR, `update failed: ${stderr.slice(0, 300)}`, opts);\n success('Updated.');\n}\n\nfunction updateCmdFor(method: string, spec: string): string[] | null {\n switch (method) {\n case 'npm':\n return ['npm', 'install', '-g', `@noir-ai/cli@${spec}`];\n case 'pnpm':\n return ['pnpm', 'add', '-g', `@noir-ai/cli@${spec}`];\n case 'yarn':\n return ['yarn', 'global', 'add', `@noir-ai/cli@${spec}`];\n case 'bun':\n return ['bun', 'add', '-g', `@noir-ai/cli@${spec}`];\n case 'homebrew':\n return ['brew', 'upgrade', 'noir'];\n case 'scoop':\n return ['scoop', 'update', 'noir'];\n default:\n return null;\n }\n}\n\n/** Non-blocking, time-boxed startup check. Writes the cache on success; silent on any failure. */\nexport async function runAsyncUpdateCheck(opts: {\n env: NodeJS.ProcessEnv;\n configUpdate: UpdateConfigLike;\n quiet: boolean;\n}): Promise<void> {\n try {\n const cache = readUpdateCache();\n if (!shouldCheckForUpdate({ env: opts.env, configUpdate: opts.configUpdate, cache })) return;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 2000);\n const latest = await fetchLatestVersion(opts.configUpdate.channel, controller.signal);\n clearTimeout(timer);\n if (latest != null) {\n writeUpdateCache({\n lastCheckAt: new Date().toISOString(),\n latestVersion: latest,\n channel: opts.configUpdate.channel,\n });\n }\n // Non-blocking: never print in quiet/CI/non-TTY; a mismatch only nudges.\n } catch {\n // silent on any failure — this is a non-blocking background check\n }\n}\n"],"mappings":"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","names":["latest"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/commands/install.ts"],"sourcesContent":["import { mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n atomicWriteFile,\n type DetectResult,\n detectActiveMethod,\n detectInstallMethods,\n loadProjectInfo,\n MANAGED_NODE_VERSION,\n NOIR_VERSION,\n noirHome,\n type ProvisionedNode,\n type ProvisionOptions,\n provisionManagedNode,\n readInstallRecord,\n runManagerCmd,\n writeInstallRecord,\n} from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail, info, success, warn } from '../output.js';\n\nexport interface InstallOptions extends CliOptions {\n list?: boolean;\n uninstallPrev?: boolean;\n spec?: string;\n /** Dismiss the migration banner for the current CLI version (persists in\n * install.json's `dismissedVersions`). Typically passed with `--list`. */\n dismiss?: boolean;\n}\n\nexport interface MigrationPlan {\n detected: DetectResult[];\n currentMethod: string;\n targetSpec: string;\n installedVersion: string | null;\n shouldMigrate: boolean;\n isDowngrade: boolean;\n /**\n * I2 — `update.minVersion` floor (config.ts:238). True when the resolved\n * target is a concrete version below the configured floor. Channel targets\n * (`latest`/`beta`) resolve to the newest at install time and never trip it.\n * Enforced in {@link install} with the same warn/refuse pattern as the\n * downgrade guard.\n */\n belowMinVersion: boolean;\n nativeVersion: string;\n prevUninstallCmd: string | null;\n autoUninstall: boolean; // always false unless --uninstall-prev\n}\n\n/** Per-segment numeric semver comparison. Returns true when `a < b`. */\nfunction semverLt(a: string, b: string): boolean {\n const pa = a.split('.').map(Number);\n const pb = b.split('.').map(Number);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x < y;\n }\n return false;\n}\n\n/** Pure: build the migration plan from detection + target. No I/O. */\nexport function buildMigrationPlan(opts: {\n detected: DetectResult[];\n currentMethod: string;\n targetSpec: string;\n installedVersion: string | null;\n /**\n * The `update.minVersion` floor from config (default `'1.6.0'`). When\n * omitted, the config default applies. See I2.\n */\n minVersion?: string;\n}): MigrationPlan {\n const detected = opts.detected;\n const shouldMigrate = detected.length > 0 && opts.currentMethod !== 'native';\n const nativeVersion = opts.targetSpec;\n let isDowngrade = false;\n if (opts.installedVersion && nativeVersion !== 'latest' && nativeVersion !== 'beta') {\n isDowngrade = semverLt(nativeVersion, opts.installedVersion);\n }\n // I2 — minVersion floor: only a concrete version below the floor trips it;\n // 'latest'/'beta' resolve to the newest at install time (always >= floor).\n const floor = opts.minVersion ?? '1.6.0';\n const belowMinVersion =\n nativeVersion !== 'latest' && nativeVersion !== 'beta' && semverLt(nativeVersion, floor);\n return {\n detected,\n currentMethod: opts.currentMethod,\n targetSpec: opts.targetSpec,\n installedVersion: opts.installedVersion,\n shouldMigrate,\n isDowngrade,\n belowMinVersion,\n nativeVersion,\n prevUninstallCmd: detected[0]?.uninstallCmd ?? null,\n autoUninstall: false,\n };\n}\n\nexport interface InstallManagedNodeOptions {\n channel?: string;\n version?: string;\n env: NodeJS.ProcessEnv;\n /**\n * Forwarded into {@link provisionManagedNode} (P1). Carries the offline mock\n * seams (`fetch`, `exec`, `target`, `signal`) so the install pipeline is\n * unit-testable without network — same pattern as `provisionManagedNode`'s\n * own suite in `packages/core/test/node-provision.test.ts`.\n */\n provision?: Pick<ProvisionOptions, 'fetch' | 'exec' | 'target' | 'signal'>;\n}\n\n/** Normalized record of which runtime backed an install (P2). */\nexport interface InstallManagedNodeResult {\n ok: boolean;\n version: string | null;\n /** What runtime backed this install: `'managed'` (downloaded into ~/.noir)\n * or `'system'` (used the system-Node fallback). */\n runtimeSource?: ProvisionedNode['source'];\n error?: string;\n}\n\n/**\n * Provision the managed Node runtime (P1) and install `@noir-ai/cli` into an\n * isolated `~/.noir/cli` prefix, shimmed from `~/.noir/bin/noir`.\n *\n * Replaces the prior \"not provisioned\" fail branch: instead of requiring the\n * user to pre-run `install.sh`/`install.ps1`, this now delegates to\n * {@link provisionManagedNode} (download + verify + extract, fail-closed), then\n * installs the CLI with the provisioned npm. On a system-Node fallback the\n * fallback node/npm paths are used and a warning is emitted; the install\n * record's `managedRuntimeVersion` reflects which path was taken so `noir\n * doctor` can report it accurately.\n */\nexport async function installManagedNode(\n opts: InstallManagedNodeOptions,\n): Promise<InstallManagedNodeResult> {\n const spec = opts.version ?? opts.channel ?? 'latest';\n const home = noirHome();\n const cliDir = join(home, 'cli');\n const binDir = join(home, 'bin');\n mkdirSync(cliDir, { recursive: true });\n mkdirSync(binDir, { recursive: true });\n\n // Provision the managed Node runtime (P1). Idempotent — re-runs are no-ops.\n // On any failure, provisionManagedNode either falls back to a system Node\n // >=22 (returns { source: 'system' }) or throws — we surface the throw as an\n // {ok:false} envelope rather than letting it kill the CLI.\n let runtime: ProvisionedNode;\n try {\n runtime = await provisionManagedNode({\n ...(opts.provision ?? {}),\n env: opts.env,\n });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n return { ok: false, version: null, error: `managed-Node provision failed: ${reason}` };\n }\n if (runtime.source === 'system') {\n warn(\n `managed-Node download failed; using system Node ${runtime.version} at ${runtime.nodeBin}.`,\n );\n }\n\n const { nodeBin, npmBin } = runtime;\n\n // Install @noir-ai/cli into the isolated prefix using the provisioned npm.\n const { code, stderr } = await runManagerCmd(\n [npmBin, 'install', '-g', `@noir-ai/cli@${spec}`, `--prefix=${cliDir}`],\n { env: opts.env, timeoutMs: 120_000 },\n );\n if (code !== 0) {\n return { ok: false, version: null, error: `npm install failed: ${stderr.slice(0, 300)}` };\n }\n\n // Shim: ~/.noir/bin/noir -> provisioned node + isolated prefix.\n const shim = join(binDir, 'noir');\n const shimBody = `#!/usr/bin/env bash\\n\"${nodeBin}\" \"${join(cliDir, 'lib', 'node_modules', '@noir-ai', 'cli', 'dist', 'bin.js')}\" \"$@\"\\n`;\n atomicWriteFile(shim, shimBody);\n // (POSIX shim; Windows uses a .cmd wrapper -- install.sh/install.ps1, P3.)\n\n // Resolve installed version via the provisioned node.\n const ver = await runManagerCmd(\n [\n nodeBin,\n join(cliDir, 'lib', 'node_modules', '@noir-ai', 'cli', 'dist', 'bin.js'),\n '--version',\n ],\n { env: opts.env, timeoutMs: 30_000 },\n );\n const version = ver.code === 0 ? ver.stdout.trim() : null;\n writeInstallRecord({\n method: 'native',\n version: version ?? '0.0.0',\n channel: opts.channel ?? 'latest',\n installedAt: new Date().toISOString(),\n // Reflect which runtime backs this install: the pinned managed version\n // (e.g. '22.23.2') for managed, or 'system-<v>' for the fallback so doctor\n // can distinguish a fallback install from a real managed one.\n managedRuntimeVersion:\n runtime.source === 'managed' ? MANAGED_NODE_VERSION : `system-${runtime.version}`,\n });\n return { ok: true, version, runtimeSource: runtime.source };\n}\n\nexport async function install(opts: InstallOptions = {}): Promise<void> {\n // --dismiss: append the current CLI version to install.json's\n // `dismissedVersions` so the migration banner stops showing for this\n // version. Idempotent (no duplicate entries). Typically combined with\n // `--list`; works standalone too. Requires an existing install record —\n // if there's none, there's nothing to dismiss (no banner was shown).\n if (opts.dismiss === true) {\n const rec = readInstallRecord();\n if (!rec) {\n info('No install record found; nothing to dismiss.');\n return;\n }\n const dismissed = new Set(rec.dismissedVersions ?? []);\n if (!dismissed.has(NOIR_VERSION)) dismissed.add(NOIR_VERSION);\n writeInstallRecord({ ...rec, dismissedVersions: [...dismissed] });\n success(`Migration banner dismissed for ${NOIR_VERSION}.`);\n if (opts.list !== true) return; // --dismiss alone is done; --list continues below.\n }\n\n if (opts.list === true) {\n const detected = await detectInstallMethods(process.env);\n const jsonOutput = { ok: true, data: { detected } };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify(jsonOutput)}\\n`);\n return;\n }\n info('Detected installs:');\n for (const d of detected) info(` ${d.method} (${d.version ?? 'unknown'})`);\n return;\n }\n\n const currentMethod = detectActiveMethod();\n const detected = await detectInstallMethods(process.env);\n const installedRecord = readInstallRecord();\n // I2 — read the `update.minVersion` floor from the project config (falls\n // back to the config default '1.6.0' when the project isn't initialized or\n // the block is absent). Same try/catch pattern as home.ts's update-config read.\n let minVersion = '1.6.0';\n try {\n minVersion = loadProjectInfo(process.cwd()).config.update.minVersion;\n } catch {\n // Not initialized yet — the config default applies.\n }\n const plan = buildMigrationPlan({\n detected,\n currentMethod,\n targetSpec: opts.spec ?? 'latest',\n installedVersion: installedRecord?.version ?? null,\n minVersion,\n });\n\n // I2 — refuse to install a version below the configured minVersion floor\n // (same warn/refuse pattern as the installed-version downgrade guard).\n if (plan.belowMinVersion) {\n warn(`Target ${plan.nativeVersion} is below the minVersion floor (${minVersion}).`);\n if (opts.noInput === true || opts.input === false) {\n fail(\n EXIT.USAGE,\n `refusing install of ${plan.nativeVersion} (below minVersion ${minVersion})`,\n opts,\n );\n }\n }\n\n if (plan.isDowngrade) {\n warn(`Target ${plan.nativeVersion} is OLDER than installed ${plan.installedVersion}.`);\n // (Interactive confirm is skipped under --no-input; we hard-stop to be safe.)\n if (opts.noInput === true || opts.input === false) {\n fail(\n EXIT.USAGE,\n `refusing downgrade to ${plan.nativeVersion} (installed ${plan.installedVersion})`,\n opts,\n );\n }\n // @clack confirm gate (lazy import) -- same pattern as home.ts.\n }\n\n const result = await installManagedNode({\n channel: plan.nativeVersion === 'beta' ? 'beta' : undefined,\n version: plan.nativeVersion.startsWith('v')\n ? plan.nativeVersion\n : plan.nativeVersion === 'latest' || plan.nativeVersion === 'beta'\n ? undefined\n : plan.nativeVersion,\n env: process.env,\n });\n if (!result.ok) {\n fail(EXIT.ERROR, result.error ?? 'native install failed', opts);\n }\n\n info(`Installed native: ${result.version}.`);\n if (plan.prevUninstallCmd && opts.uninstallPrev !== true) {\n warn(`To finish the migration, uninstall the previous install: ${plan.prevUninstallCmd}`);\n warn(\n ' Or re-run with --uninstall-prev to do it now. Rollback: reinstall via the previous manager.',\n );\n } else if (plan.prevUninstallCmd && opts.uninstallPrev === true) {\n const { code } = await runManagerCmd(plan.prevUninstallCmd.split(' '), { env: process.env });\n if (code === 0) success('Previous install removed.');\n else warn('Previous install NOT removed (non-zero exit). You can remove it manually.');\n }\n // Claude Code #41806/#27910 mitigation: the old npm bin may still resolve\n // first in $PATH after migration (the shell hashes command paths, and a\n // stale global install can shadow ~/.noir/bin). Hint the user to refresh.\n info('If `noir` still points at the old install, run: hash -r && which -a noir');\n success(`noir ${result.version} installed via native. Run \\`noir doctor\\` to verify.`);\n}\n"],"mappings":"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","names":["detected"]}