@neuralnomads/codenomad-dev 0.20.0-dev-20260925-25e07c74 → 0.20.0-dev-20260926-14b1eaa4

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 (78) hide show
  1. package/README.md +6 -6
  2. package/dist/opencode-update/incomplete-installation.js +25 -0
  3. package/dist/opencode-update/incomplete-installation.js.map +1 -0
  4. package/dist/opencode-update/incomplete-installation.test.js +99 -0
  5. package/dist/opencode-update/incomplete-installation.test.js.map +1 -0
  6. package/dist/opencode-update/shared-installation.js +10 -4
  7. package/dist/opencode-update/shared-installation.js.map +1 -1
  8. package/package.json +1 -1
  9. package/public/assets/App-BkbhMmhi.js +58 -0
  10. package/public/assets/App-BkbhMmhi.js.map +1 -0
  11. package/public/assets/{FilesTab-CIZ1puTy.js → FilesTab-CFnmfRgo.js} +3 -3
  12. package/public/assets/{FilesTab-CIZ1puTy.js.map → FilesTab-CFnmfRgo.js.map} +1 -1
  13. package/public/assets/{GitChangesTab-DwMgRf-D.js → GitChangesTab-Dp95vrpR.js} +3 -3
  14. package/public/assets/{GitChangesTab-DwMgRf-D.js.map → GitChangesTab-Dp95vrpR.js.map} +1 -1
  15. package/public/assets/{SplitFilePanel-BCLtSYpn.js → SplitFilePanel-DVyHD97U.js} +2 -2
  16. package/public/assets/{SplitFilePanel-BCLtSYpn.js.map → SplitFilePanel-DVyHD97U.js.map} +1 -1
  17. package/public/assets/{StatusTab-I954HOlT.js → StatusTab-B1TtVq0n.js} +2 -2
  18. package/public/assets/{StatusTab-I954HOlT.js.map → StatusTab-B1TtVq0n.js.map} +1 -1
  19. package/public/assets/{align-justify-ClApgjuX.js → align-justify-DNke0hi-.js} +2 -2
  20. package/public/assets/{align-justify-ClApgjuX.js.map → align-justify-DNke0hi-.js.map} +1 -1
  21. package/public/assets/{bundle-full-Bv-Zo-BN.js → bundle-full-CV_LTEPl.js} +2 -2
  22. package/public/assets/{bundle-full-Bv-Zo-BN.js.map → bundle-full-CV_LTEPl.js.map} +1 -1
  23. package/public/assets/{client-state-BUfw9mlB.js → client-state-9tEzHtfW.js} +2 -2
  24. package/public/assets/{client-state-BUfw9mlB.js.map → client-state-9tEzHtfW.js.map} +1 -1
  25. package/public/assets/{diff-viewer-DTYmoMJJ.js → diff-viewer-CeL3QyjI.js} +2 -2
  26. package/public/assets/{diff-viewer-DTYmoMJJ.js.map → diff-viewer-CeL3QyjI.js.map} +1 -1
  27. package/public/assets/{index-CvvEjI09.js → index-97B_EqpY.js} +2 -2
  28. package/public/assets/{index-CvvEjI09.js.map → index-97B_EqpY.js.map} +1 -1
  29. package/public/assets/{index-B7Rzw0N8.js → index-B-_YmFJc.js} +2 -2
  30. package/public/assets/{index-B7Rzw0N8.js.map → index-B-_YmFJc.js.map} +1 -1
  31. package/public/assets/{index--pzZg96p.js → index-C5ZxyV3U.js} +2 -2
  32. package/public/assets/{index--pzZg96p.js.map → index-C5ZxyV3U.js.map} +1 -1
  33. package/public/assets/index-CDY1e4Uc.js +2 -0
  34. package/public/assets/{index-cCIfI5Gs.js.map → index-CDY1e4Uc.js.map} +1 -1
  35. package/public/assets/{index-ByEDZSKg.js → index-CuhtAbYJ.js} +3 -3
  36. package/public/assets/{index-ByEDZSKg.js.map → index-CuhtAbYJ.js.map} +1 -1
  37. package/public/assets/{index-kCzCI98C.js → index-CvKnyKel.js} +2 -2
  38. package/public/assets/{index-kCzCI98C.js.map → index-CvKnyKel.js.map} +1 -1
  39. package/public/assets/{index-q1q2jGr4.js → index-DX4zzTPL.js} +2 -2
  40. package/public/assets/{index-q1q2jGr4.js.map → index-DX4zzTPL.js.map} +1 -1
  41. package/public/assets/{index-QPxBkmUK.js → index-DY9ZO9ld.js} +2 -2
  42. package/public/assets/{index-QPxBkmUK.js.map → index-DY9ZO9ld.js.map} +1 -1
  43. package/public/assets/{index-CIsPS6rK.js → index-DqE-g5em.js} +2 -2
  44. package/public/assets/{index-CIsPS6rK.js.map → index-DqE-g5em.js.map} +1 -1
  45. package/public/assets/{index-Cq3k5jiZ.js → index-QXZuSZ1s.js} +2 -2
  46. package/public/assets/{index-Cq3k5jiZ.js.map → index-QXZuSZ1s.js.map} +1 -1
  47. package/public/assets/{index-DFYhjWFw.js → index-Ropj5M3p.js} +2 -2
  48. package/public/assets/{index-DFYhjWFw.js.map → index-Ropj5M3p.js.map} +1 -1
  49. package/public/assets/{index-BpxnUbDV.js → index-XQu8RsTR.js} +2 -2
  50. package/public/assets/{index-BpxnUbDV.js.map → index-XQu8RsTR.js.map} +1 -1
  51. package/public/assets/{loading-DUGpBMNV.js → loading-DMhZHPMZ.js} +2 -2
  52. package/public/assets/{loading-DUGpBMNV.js.map → loading-DMhZHPMZ.js.map} +1 -1
  53. package/public/assets/{main-CcoQlXKa.js → main-Dk_Mjdcc.js} +17 -17
  54. package/public/assets/main-Dk_Mjdcc.js.map +1 -0
  55. package/public/assets/{markdown-B2LqHvL9.js → markdown-FNqTCR-J.js} +4 -4
  56. package/public/assets/{markdown-B2LqHvL9.js.map → markdown-FNqTCR-J.js.map} +1 -1
  57. package/public/assets/{monaco-viewer-BQXd7g3w.js → monaco-viewer-C2EN1a6C.js} +13 -13
  58. package/public/assets/monaco-viewer-C2EN1a6C.js.map +1 -0
  59. package/public/assets/{startup-state-settings-card-BCKRPH7W.js → startup-state-settings-card-D5atekej.js} +2 -2
  60. package/public/assets/{startup-state-settings-card-BCKRPH7W.js.map → startup-state-settings-card-D5atekej.js.map} +1 -1
  61. package/public/assets/{terminal-square-BgSAfXCt.js → terminal-square-xxCQL1v1.js} +2 -2
  62. package/public/assets/{terminal-square-BgSAfXCt.js.map → terminal-square-xxCQL1v1.js.map} +1 -1
  63. package/public/assets/{tool-call-Df4j6aME.js → tool-call-DT-_bBwM.js} +4 -4
  64. package/public/assets/{tool-call-Df4j6aME.js.map → tool-call-DT-_bBwM.js.map} +1 -1
  65. package/public/assets/{unified-picker-DZfkh5jj.js → unified-picker-CPPucpt3.js} +2 -2
  66. package/public/assets/{unified-picker-DZfkh5jj.js.map → unified-picker-CPPucpt3.js.map} +1 -1
  67. package/public/assets/{wrap-text-BFsJjS2I.js → wrap-text-CCC9kCcP.js} +2 -2
  68. package/public/assets/{wrap-text-BFsJjS2I.js.map → wrap-text-CCC9kCcP.js.map} +1 -1
  69. package/public/index.html +4 -4
  70. package/public/loading.html +4 -4
  71. package/public/sw.js +1 -1
  72. package/public/sw.js.map +1 -1
  73. package/public/ui-version.json +1 -1
  74. package/public/assets/App-DJjznRZ_.js +0 -58
  75. package/public/assets/App-DJjznRZ_.js.map +0 -1
  76. package/public/assets/index-cCIfI5Gs.js +0 -2
  77. package/public/assets/main-CcoQlXKa.js.map +0 -1
  78. package/public/assets/monaco-viewer-BQXd7g3w.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # CodeNomad Server
2
2
 
3
- **CodeNomad Server** is the high-performance engine behind the CodeNomad cockpit. It transforms your machine into a robust development host, managing the lifecycle of multiple OpenCode instances and providing the low-latency data streams that long-haul builders demand. It bridges your local filesystem with the UI, ensuring that whether you are on localhost or a remote tunnel, you have the speed, clarity, and control of a native workspace.
3
+ **CodeNomad Server** connects the desktop and browser UI to your projects and one shared OpenCode V2 background service. It provides workspace access, Git operations, authentication, and live event delivery. OpenCode owns the sessions, messages, and agent execution; closing CodeNomad leaves the shared OpenCode service running.
4
4
 
5
5
  ## Features & Capabilities
6
6
 
@@ -13,18 +13,18 @@
13
13
 
14
14
  ### ⚡️ Workspace Power
15
15
 
16
- - **Multi-Instance**: Juggle multiple OpenCode sessions side-by-side with per-instance tabs.
17
- - **Long-Context Native**: Scroll through massive transcripts without hitches.
16
+ - **Multiple projects**: Work across project tabs and sessions over the same OpenCode service.
17
+ - **Long conversations**: Bounded transcript loading, full-history search, and timeline navigation.
18
18
  - **Deep Task Awareness**: Monitor background tasks and child sessions without losing your flow.
19
19
  - **Command Palette**: A single, global palette to jump tabs, launch tools, and fire shortcuts.
20
20
 
21
21
  ## Prerequisites
22
22
 
23
23
  - **Git**: A prerequisite in the backend process's `PATH` for repository identity, Git operations and worktree management. Without Git, directory-only conversations in the explicitly opened physical folder remain available so the user can ask the agent for installation help. Owned session prompts/custom commands update the native `codenomad.git-availability` instruction; it reports the backend platform and is removed when Git becomes available. Git/worktree operations still fail explicitly with `503`/`git_required`. Install from https://git-scm.com/downloads and restart the backend after changing `PATH`. A Windows per-user installation is sufficient; Git inside WSL alone is insufficient for a Windows backend.
24
- - **OpenCode V2**: Install a compatible `opencode2` CLI. CodeNomad uses the CLI's official `service status`, `service start`, and `service get password` lifecycle to connect to OpenCode's externally owned global daemon, then validates its authenticated loopback `/api/status` response rather than enforcing an exact CLI version string.
24
+ - **OpenCode V2**: For CodeNomad 0.20.0, install `opencode` **2.0.7 or later**, with **2.0.16 recommended and qualified**. The npm package is `@opencode/cli`; see the [V2 installation guide](https://opencode.ai/v2/docs/). CodeNomad prefers the CLI on `PATH` and also supports an explicitly selected executable. It uses the official service lifecycle and validates authenticated daemon metadata and API compatibility. V1 and former V2 beta runtimes are unsupported; custom/prerelease versions are unverified and still undergo contract validation. See [`runtime-support.ts`](src/opencode/runtime-support.ts) for the current requirements.
25
25
  - **OpenCode data**: The global daemon owns its platform-default storage, database, and service registration. Configured startup environment applies only when CodeNomad starts a missing daemon; an existing daemon is unchanged.
26
26
  - **Windows to WSL**: A configured WSL UNC binary uses Linux `service status`, `service start`, and `service get password`; Windows must have WSL localhost forwarding enabled to reach its loopback service.
27
- - Node.js 18+ and npm (for running or building from source).
27
+ - **Node.js and npm**: Use Node.js 24 LTS for the standalone server; source and release builds use [`.node-version`](../../.node-version) (currently 24.20.0). Desktop distributions bundle Node/npm.
28
28
  - A workspace folder on disk you want to serve.
29
29
  - Optional: a Chromium-based browser if you want `--launch` to open the UI automatically.
30
30
 
@@ -225,7 +225,7 @@ When running as a server CodeNomad can also be installed as a PWA from any suppo
225
225
  - **OpenCode V2 sessions, messages, and service registration**: OpenCode's platform-default global locations.
226
226
  - **Desktop restore state**: `~/.codenomad/client-state/v2/`
227
227
 
228
- CodeNomad owns no private OpenCode port, database, service registration, or daemon PID. Configured allowed `server.environmentVariables` and the current `NODE_EXTRA_CA_CERTS` apply only when CodeNomad starts a missing daemon. Existing daemons are unchanged; legacy `OPENCODE_DB` and `XDG_STATE_HOME` ownership variables are ignored. WSL lifecycle commands run inside Linux and never inspect or signal Linux PIDs from Windows.
228
+ CodeNomad owns no private OpenCode port, database, service registration, or daemon PID. Allowed profile environment variables and the current `NODE_EXTRA_CA_CERTS` are passed when starting a missing daemon; connecting to an existing daemon does not change its process environment. Separately, CodeNomad applies the profile's complete execution-host environment to the native session before each prompt, custom command, or session shell request, including removal of previously configured values. Legacy `OPENCODE_DB` and `XDG_STATE_HOME` ownership variables are ignored. See [Session Environment](../../dev-docs/SESSION_ENVIRONMENT.md). WSL lifecycle commands run inside Linux and never inspect or signal Linux PIDs from Windows.
229
229
 
230
230
  Explicit **Stop Workspace** evicts that location and its resources from the global service without stopping the daemon. Closing a UI tab or native window only detaches local state and never evicts. Backend shutdown clears only CodeNomad's in-memory connection state and never stops the global service.
231
231
 
@@ -0,0 +1,25 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ /** npm can remove a failed package but leave its terminal shims behind. Only
4
+ * recognize its exact native-executable shim in the chosen user prefix; never
5
+ * adopt a custom wrapper, another prefix, or an existing executable for repair. */
6
+ export function isIncompleteWindowsNpmShim(command, prefix, binary) {
7
+ if (path.resolve(path.dirname(command)) !== path.resolve(prefix)
8
+ || !/^opencode2?\.cmd$/i.test(path.basename(command)) || existsSync(binary))
9
+ return false;
10
+ try {
11
+ const script = readFileSync(command, "utf8");
12
+ if (script.length > 4096)
13
+ return false;
14
+ const expected = [
15
+ "@ECHO off", "GOTO start", ":find_dp0", "SET dp0=%~dp0", "EXIT /b", ":start", "SETLOCAL", "CALL :find_dp0",
16
+ '"%dp0%\\node_modules\\@opencode\\cli\\bin\\opencode.exe" %*',
17
+ ];
18
+ const lines = script.trim().split(/\r?\n/).map(line => line.trim().replace(/\s+%\*$/, " %*"));
19
+ return lines.length === expected.length && lines.every((line, index) => line === expected[index]);
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ //# sourceMappingURL=incomplete-installation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incomplete-installation.js","sourceRoot":"","sources":["../../src/opencode-update/incomplete-installation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B;;mFAEmF;AACnF,MAAM,UAAU,0BAA0B,CAAC,OAAe,EAAE,MAAc,EAAE,MAAc;IACxF,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;WAC3D,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAA;IAC3F,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC5C,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI;YAAE,OAAO,KAAK,CAAA;QACtC,MAAM,QAAQ,GAAG;YACf,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB;YAC1G,6DAA6D;SAC9D,CAAA;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAA;QAC7F,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;IACnG,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAA;IAAC,CAAC;AAC1B,CAAC","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\"\nimport path from \"node:path\"\n\n/** npm can remove a failed package but leave its terminal shims behind. Only\n * recognize its exact native-executable shim in the chosen user prefix; never\n * adopt a custom wrapper, another prefix, or an existing executable for repair. */\nexport function isIncompleteWindowsNpmShim(command: string, prefix: string, binary: string): boolean {\n if (path.resolve(path.dirname(command)) !== path.resolve(prefix)\n || !/^opencode2?\\.cmd$/i.test(path.basename(command)) || existsSync(binary)) return false\n try {\n const script = readFileSync(command, \"utf8\")\n if (script.length > 4096) return false\n const expected = [\n \"@ECHO off\", \"GOTO start\", \":find_dp0\", \"SET dp0=%~dp0\", \"EXIT /b\", \":start\", \"SETLOCAL\", \"CALL :find_dp0\",\n '\"%dp0%\\\\node_modules\\\\@opencode\\\\cli\\\\bin\\\\opencode.exe\" %*',\n ]\n const lines = script.trim().split(/\\r?\\n/).map(line => line.trim().replace(/\\s+%\\*$/, \" %*\"))\n return lines.length === expected.length && lines.every((line, index) => line === expected[index])\n } catch { return false }\n}\n"]}
@@ -0,0 +1,99 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { mkdtemp, mkdir, writeFile, readFile, rm } from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { isIncompleteWindowsNpmShim } from "./incomplete-installation";
7
+ import { installSharedOpenCode, npmExecutable, resolveDefaultInstallation, sharedInstallPrefix } from "./shared-installation";
8
+ import { OpenCodeUpdateService } from "./service";
9
+ const shim = '@ECHO off\r\nGOTO start\r\n:find_dp0\r\nSET dp0=%~dp0\r\nEXIT /b\r\n:start\r\nSETLOCAL\r\nCALL :find_dp0\r\n"%dp0%\\node_modules\\@opencode\\cli\\bin\\opencode.exe" %*\r\n';
10
+ test("failed npm installation remains repairable with orphan shims and no package manifest", async () => {
11
+ const root = await mkdtemp(path.join(os.tmpdir(), "opencode-incomplete-"));
12
+ try {
13
+ const prefix = path.join(root, "npm");
14
+ await mkdir(prefix);
15
+ const command = path.join(prefix, "opencode2.cmd");
16
+ await writeFile(command, shim);
17
+ await writeFile(path.join(prefix, "opencode.cmd"), shim);
18
+ const host = { home: root, platform: "win32", env: { PATH: "", npm_config_prefix: prefix } };
19
+ assert.equal(sharedInstallPrefix(host), prefix, "initial failure before PATH registration must allow retry");
20
+ host.env.PATH = prefix;
21
+ assert.equal(sharedInstallPrefix(host), prefix);
22
+ assert.deepEqual(resolveDefaultInstallation(host), { path: npmExecutable(prefix, "win32"), source: "user" }, "probe the missing executable instead of reporting an invalid cmd wrapper");
23
+ const binary = npmExecutable(prefix, "win32");
24
+ await mkdir(path.dirname(binary), { recursive: true });
25
+ await writeFile(binary, "existing unverified executable");
26
+ assert.equal(sharedInstallPrefix(host), undefined, "existing unverified binaries are not incomplete installs");
27
+ }
28
+ finally {
29
+ await rm(root, { recursive: true, force: true });
30
+ }
31
+ });
32
+ test("an explicit retry repairs leftover launchers and restores update status", { skip: process.platform !== "win32" }, async () => {
33
+ const root = await mkdtemp(path.join(os.tmpdir(), "opencode-retry-"));
34
+ try {
35
+ const prefix = path.join(root, "npm");
36
+ await mkdir(prefix);
37
+ await writeFile(path.join(prefix, "opencode2.cmd"), shim);
38
+ const host = { home: root, env: { PATH: prefix, npm_config_prefix: prefix } };
39
+ const binary = npmExecutable(prefix);
40
+ const probe = async (file) => {
41
+ try {
42
+ return { valid: true, version: await readFile(file.endsWith(".cmd") ? binary : file, "utf8") };
43
+ }
44
+ catch {
45
+ return { valid: false, missing: true };
46
+ }
47
+ };
48
+ let installs = 0;
49
+ const service = new OpenCodeUpdateService({
50
+ resolveBinary: () => ({ ...resolveDefaultInstallation(host), label: "fixture" }),
51
+ probeBinary: probe,
52
+ resolveLatestVersion: async () => "2.0.16",
53
+ canUpgradeBinary: () => Boolean(sharedInstallPrefix(host)),
54
+ upgradeBinary: async (_binary, version) => {
55
+ await installSharedOpenCode(version, { ...host, npm: "fixture-npm.js", probe, registerPath: async () => { },
56
+ execute: async () => {
57
+ installs++;
58
+ await mkdir(path.dirname(binary), { recursive: true });
59
+ await writeFile(binary, version);
60
+ await writeFile(path.join(path.dirname(binary), "../package.json"), JSON.stringify({
61
+ name: "@opencode/cli", bin: { opencode2: "./bin/opencode.exe" },
62
+ }));
63
+ },
64
+ });
65
+ return { success: true, version };
66
+ },
67
+ });
68
+ const before = await service.getStatus();
69
+ assert.equal(before.state, "missing");
70
+ assert.equal(before.canUpgrade, true, "the install button remains available after the failed npm cleanup");
71
+ await service.upgrade();
72
+ assert.equal(installs, 1);
73
+ assert.equal(sharedInstallPrefix(host), prefix);
74
+ assert.equal(resolveDefaultInstallation(host).source, "path");
75
+ assert.equal((await service.getStatus()).state, "ready");
76
+ }
77
+ finally {
78
+ await rm(root, { recursive: true, force: true });
79
+ }
80
+ });
81
+ test("incomplete-installation recovery does not adopt custom wrappers or other prefixes", async () => {
82
+ const root = await mkdtemp(path.join(os.tmpdir(), "opencode-incomplete-authority-"));
83
+ try {
84
+ const command = path.join(root, "opencode2.cmd");
85
+ const binary = npmExecutable(root, "win32");
86
+ await writeFile(command, shim);
87
+ assert.equal(isIncompleteWindowsNpmShim(command, root, binary), true);
88
+ assert.equal(isIncompleteWindowsNpmShim(command, path.join(root, "other"), binary), false);
89
+ for (const custom of [shim + 'echo custom\r\n', shim.replace('opencode.exe', 'custom.exe'),
90
+ '@echo off\r\n"%~dp0\\node_modules\\@opencode\\cli\\bin\\opencode.exe" %*\r\n']) {
91
+ await writeFile(command, custom);
92
+ assert.equal(isIncompleteWindowsNpmShim(command, root, binary), false);
93
+ }
94
+ }
95
+ finally {
96
+ await rm(root, { recursive: true, force: true });
97
+ }
98
+ });
99
+ //# sourceMappingURL=incomplete-installation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incomplete-installation.test.js","sourceRoot":"","sources":["../../src/opencode-update/incomplete-installation.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\"\nimport test from \"node:test\"\nimport { mkdtemp, mkdir, writeFile, readFile, rm } from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\nimport { isIncompleteWindowsNpmShim } from \"./incomplete-installation\"\nimport { installSharedOpenCode, npmExecutable, resolveDefaultInstallation, sharedInstallPrefix } from \"./shared-installation\"\nimport { OpenCodeUpdateService } from \"./service\"\n\nconst shim = '@ECHO off\\r\\nGOTO start\\r\\n:find_dp0\\r\\nSET dp0=%~dp0\\r\\nEXIT /b\\r\\n:start\\r\\nSETLOCAL\\r\\nCALL :find_dp0\\r\\n\"%dp0%\\\\node_modules\\\\@opencode\\\\cli\\\\bin\\\\opencode.exe\" %*\\r\\n'\n\ntest(\"failed npm installation remains repairable with orphan shims and no package manifest\", async () => {\n const root = await mkdtemp(path.join(os.tmpdir(), \"opencode-incomplete-\"))\n try {\n const prefix = path.join(root, \"npm\")\n await mkdir(prefix)\n const command = path.join(prefix, \"opencode2.cmd\")\n await writeFile(command, shim)\n await writeFile(path.join(prefix, \"opencode.cmd\"), shim)\n const host = { home: root, platform: \"win32\" as const, env: { PATH: \"\", npm_config_prefix: prefix } }\n assert.equal(sharedInstallPrefix(host), prefix, \"initial failure before PATH registration must allow retry\")\n host.env.PATH = prefix\n assert.equal(sharedInstallPrefix(host), prefix)\n assert.deepEqual(resolveDefaultInstallation(host), { path: npmExecutable(prefix, \"win32\"), source: \"user\" },\n \"probe the missing executable instead of reporting an invalid cmd wrapper\")\n const binary = npmExecutable(prefix, \"win32\")\n await mkdir(path.dirname(binary), { recursive: true })\n await writeFile(binary, \"existing unverified executable\")\n assert.equal(sharedInstallPrefix(host), undefined, \"existing unverified binaries are not incomplete installs\")\n } finally { await rm(root, { recursive: true, force: true }) }\n})\n\ntest(\"an explicit retry repairs leftover launchers and restores update status\", { skip: process.platform !== \"win32\" }, async () => {\n const root = await mkdtemp(path.join(os.tmpdir(), \"opencode-retry-\"))\n try {\n const prefix = path.join(root, \"npm\")\n await mkdir(prefix)\n await writeFile(path.join(prefix, \"opencode2.cmd\"), shim)\n const host = { home: root, env: { PATH: prefix, npm_config_prefix: prefix } }\n const binary = npmExecutable(prefix)\n const probe = async (file: string) => {\n try { return { valid: true, version: await readFile(file.endsWith(\".cmd\") ? binary : file, \"utf8\") } }\n catch { return { valid: false, missing: true } }\n }\n let installs = 0\n const service = new OpenCodeUpdateService({\n resolveBinary: () => ({ ...resolveDefaultInstallation(host), label: \"fixture\" }),\n probeBinary: probe,\n resolveLatestVersion: async () => \"2.0.16\",\n canUpgradeBinary: () => Boolean(sharedInstallPrefix(host)),\n upgradeBinary: async (_binary, version) => {\n await installSharedOpenCode(version, { ...host, npm: \"fixture-npm.js\", probe, registerPath: async () => {},\n execute: async () => {\n installs++\n await mkdir(path.dirname(binary), { recursive: true })\n await writeFile(binary, version)\n await writeFile(path.join(path.dirname(binary), \"../package.json\"), JSON.stringify({\n name: \"@opencode/cli\", bin: { opencode2: \"./bin/opencode.exe\" },\n }))\n },\n })\n return { success: true, version }\n },\n })\n const before = await service.getStatus()\n assert.equal(before.state, \"missing\")\n assert.equal(before.canUpgrade, true, \"the install button remains available after the failed npm cleanup\")\n await service.upgrade()\n assert.equal(installs, 1)\n assert.equal(sharedInstallPrefix(host), prefix)\n assert.equal(resolveDefaultInstallation(host).source, \"path\")\n assert.equal((await service.getStatus()).state, \"ready\")\n } finally { await rm(root, { recursive: true, force: true }) }\n})\n\ntest(\"incomplete-installation recovery does not adopt custom wrappers or other prefixes\", async () => {\n const root = await mkdtemp(path.join(os.tmpdir(), \"opencode-incomplete-authority-\"))\n try {\n const command = path.join(root, \"opencode2.cmd\")\n const binary = npmExecutable(root, \"win32\")\n await writeFile(command, shim)\n assert.equal(isIncompleteWindowsNpmShim(command, root, binary), true)\n assert.equal(isIncompleteWindowsNpmShim(command, path.join(root, \"other\"), binary), false)\n for (const custom of [shim + 'echo custom\\r\\n', shim.replace('opencode.exe', 'custom.exe'),\n '@echo off\\r\\n\"%~dp0\\\\node_modules\\\\@opencode\\\\cli\\\\bin\\\\opencode.exe\" %*\\r\\n']) {\n await writeFile(command, custom)\n assert.equal(isIncompleteWindowsNpmShim(command, root, binary), false)\n }\n } finally { await rm(root, { recursive: true, force: true }) }\n})\n"]}
@@ -9,6 +9,7 @@ import { assertSupportedOpenCode } from "../opencode/runtime-support";
9
9
  import { compareVersionStrings } from "../releases/release-monitor";
10
10
  import { assertExecutableWritable, withInstallationLock } from "./installation-lock";
11
11
  import { upgradeSharedOpenCode } from "./native-upgrade";
12
+ import { isIncompleteWindowsNpmShim } from "./incomplete-installation";
12
13
  export function userNpmPrefix(host = {}) {
13
14
  const home = host.home ?? os.homedir();
14
15
  const env = host.env ?? process.env;
@@ -114,11 +115,15 @@ export function findPathOpenCode(host = {}) {
114
115
  }
115
116
  }
116
117
  export function resolveDefaultInstallation(host = {}) {
118
+ const platform = host.platform ?? process.platform;
119
+ const prefix = userNpmPrefix(host);
120
+ const binary = npmCommand(prefix, platform)?.binary ?? npmExecutable(prefix, platform);
117
121
  const command = findPathOpenCode(host);
118
- if (command)
122
+ if (command) {
123
+ if (platform === "win32" && isIncompleteWindowsNpmShim(command, prefix, binary))
124
+ return { path: binary, source: "user" };
119
125
  return { path: command, source: "path" };
120
- const prefix = userNpmPrefix(host);
121
- const binary = npmCommand(prefix, host.platform ?? process.platform)?.binary ?? npmExecutable(prefix, host.platform);
126
+ }
122
127
  if (existsSync(binary) && !isRetiredInstallation(binary))
123
128
  return { path: binary, source: "user" };
124
129
  return { path: "opencode2" };
@@ -136,7 +141,8 @@ export function sharedInstallPrefix(host = {}) {
136
141
  return userPrefix;
137
142
  const prefix = platform === "win32" ? path.dirname(command) : path.dirname(path.dirname(command));
138
143
  try {
139
- if (!npmLauncher(command, prefix, platform))
144
+ if (!npmLauncher(command, prefix, platform)
145
+ && !(platform === "win32" && isIncompleteWindowsNpmShim(command, userPrefix, npmExecutable(userPrefix, platform))))
140
146
  return undefined;
141
147
  accessSync(prefix, constants.W_OK);
142
148
  return prefix;
@@ -1 +1 @@
1
- {"version":3,"file":"shared-installation.js","sourceRoot":"","sources":["../../src/opencode-update/shared-installation.ts"],"names":[],"mappings":"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","sourcesContent":["import { accessSync, constants, existsSync, readFileSync, realpathSync, statSync } from \"node:fs\"\nimport os from \"node:os\"\nimport path from \"node:path\"\nimport { bundledNpm, executeInstaller } from \"./npm-runtime\"\nimport { isRetiredInstallation } from \"./retired-installation\"\nimport { registerUserPath } from \"./user-path\"\nimport { probeBinaryVersionAsync, buildSpawnSpec } from \"../workspaces/spawn\"\nimport { assertSupportedOpenCode } from \"../opencode/runtime-support\"\nimport { compareVersionStrings } from \"../releases/release-monitor\"\nimport { assertExecutableWritable, withInstallationLock } from \"./installation-lock\"\nimport { upgradeSharedOpenCode } from \"./native-upgrade\"\n\nexport interface InstallationHost {\n home?: string\n platform?: NodeJS.Platform\n env?: NodeJS.ProcessEnv\n}\n\nexport function userNpmPrefix(host: InstallationHost = {}): string {\n const home = host.home ?? os.homedir()\n const env = host.env ?? process.env\n const configured = Object.entries(env).find(([key]) => key.toLowerCase() === \"npm_config_prefix\")?.[1]\n if (configured && path.isAbsolute(configured)) return configured\n return (host.platform ?? process.platform) === \"win32\"\n ? path.join(env.APPDATA || path.join(home, \"AppData\", \"Roaming\"), \"npm\") : path.join(home, \".local\")\n}\n\nexport function npmCommandDirectory(prefix: string, platform = process.platform): string {\n return platform === \"win32\" ? prefix : path.join(prefix, \"bin\")\n}\n\nexport function npmExecutable(prefix: string, platform = process.platform): string {\n return path.join(prefix, ...(platform === \"win32\" ? [] : [\"lib\"]), \"node_modules\", \"@opencode\", \"cli\", \"bin\", \"opencode.exe\")\n}\n\nfunction npmPackage(prefix: string, platform: NodeJS.Platform) {\n try {\n const file = path.join(path.dirname(npmExecutable(prefix, platform)), \"..\", \"package.json\")\n const manifest = JSON.parse(readFileSync(file, \"utf8\")) as { name?: string; bin?: Record<string, string> }\n return manifest.name === \"@opencode/cli\" ? manifest : undefined\n } catch { return undefined }\n}\n\nfunction npmLauncher(command: string, prefix: string, platform: NodeJS.Platform): string | undefined {\n const manifest = npmPackage(prefix, platform)\n if (!manifest?.bin) return undefined\n const name = path.basename(command).replace(platform === \"win32\" ? /\\.cmd$/i : /$^/, \"\")\n const relative = manifest.bin[name]\n if (typeof relative !== \"string\" || !/^\\.\\/bin\\/[^/\\\\]+\\.exe$/.test(relative)) return undefined\n const packageRoot = path.join(path.dirname(npmExecutable(prefix, platform)), \"..\")\n const binary = path.resolve(packageRoot, relative)\n try {\n if (!statSync(binary).isFile()) return undefined\n const resolved = platform === \"win32\" ? buildSpawnSpec(command, [], { platform }).command : realpathSync(command)\n return realpathSync(resolved) === realpathSync(binary) ? binary : undefined\n } catch { return undefined }\n}\n\nfunction retiredNpmAlias(command: string, prefix: string, platform: NodeJS.Platform): boolean {\n const name = platform === \"win32\" ? path.basename(command).replace(/\\.cmd$/i, \"\") : path.basename(command)\n const relative = npmPackage(prefix, platform)?.bin?.[name]\n if (typeof relative !== \"string\" || !/^\\.\\/bin\\/[^/\\\\]+\\.cjs$/.test(relative)) return false\n try {\n const target = path.resolve(path.dirname(npmExecutable(prefix, platform)), \"..\", relative)\n if (platform === \"win32\") {\n const script = readFileSync(command, \"utf8\")\n if (script.length > 64 * 1024) return false\n const escaped = path.basename(target).replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n return statSync(target).isFile() && new RegExp(`[\"'](?:%dp0%|%~dp0)[\\\\\\\\/]node_modules[\\\\\\\\/]@opencode[\\\\\\\\/]cli[\\\\\\\\/]bin[\\\\\\\\/]${escaped}[\"']\\\\s+%\\\\*`, \"i\").test(script)\n }\n return realpathSync(command) === realpathSync(target)\n } catch { return false }\n}\n\nfunction npmCommand(prefix: string, platform: NodeJS.Platform): { command: string; binary: string } | undefined {\n const directory = npmCommandDirectory(prefix, platform)\n for (const name of [\"opencode2\", \"opencode\"]) {\n const command = path.join(directory, platform === \"win32\" ? `${name}.cmd` : name)\n const binary = npmLauncher(command, prefix, platform)\n if (binary) return { command, binary }\n }\n}\n\nexport function findPathOpenCode(host: InstallationHost = {}): string | undefined {\n const env = host.env ?? process.env\n const platform = host.platform ?? process.platform\n const key = Object.keys(env).find(key => key.toLowerCase() === \"path\")\n const extensions = platform === \"win32\" ? (env.PATHEXT || \".COM;.EXE;.BAT;.CMD\").toLowerCase().split(\";\") : [\"\"]\n for (const entry of (env[key ?? \"PATH\"] || \"\").split(platform === \"win32\" ? \";\" : \":\")) {\n const directory = entry.replace(/^\"|\"$/g, \"\")\n if (!directory || !path.isAbsolute(directory)) continue\n const prefix = platform === \"win32\" ? directory : path.dirname(directory)\n const inNpmBin = path.resolve(directory) === path.resolve(npmCommandDirectory(prefix, platform))\n for (const name of [\"opencode2\", \"opencode\"]) for (const extension of extensions) {\n const candidate = path.join(directory, `${name}${extension}`)\n try {\n if (!statSync(candidate).isFile()) continue\n if (isRetiredInstallation(candidate)) continue\n accessSync(candidate, platform === \"win32\" ? constants.F_OK : constants.X_OK)\n if (inNpmBin && npmPackage(prefix, platform)) {\n if (retiredNpmAlias(candidate, prefix, platform)) continue\n }\n return candidate\n } catch { /* Continue in PATH order. */ }\n }\n }\n}\n\nexport function resolveDefaultInstallation(host: InstallationHost = {}): { path: string; source?: \"path\" | \"user\" } {\n const command = findPathOpenCode(host)\n if (command) return { path: command, source: \"path\" }\n const prefix = userNpmPrefix(host)\n const binary = npmCommand(prefix, host.platform ?? process.platform)?.binary ?? npmExecutable(prefix, host.platform)\n if (existsSync(binary) && !isRetiredInstallation(binary)) return { path: binary, source: \"user\" }\n return { path: \"opencode2\" }\n}\n\n/** Only a verified npm installation may be updated via npm. Homebrew/curl and\n * explicit custom executable choices retain their own installation authority. */\nexport function sharedInstallPrefix(host: InstallationHost = {}): string | undefined {\n const platform = host.platform ?? process.platform\n const userPrefix = userNpmPrefix(host)\n if (isRetiredInstallation(userPrefix)) return undefined\n const command = findPathOpenCode(host) ?? findPathOpenCode({ ...host,\n env: { PATH: npmCommandDirectory(userPrefix, platform), PATHEXT: \".EXE;.CMD;.BAT\" } })\n if (!command) return userPrefix\n const prefix = platform === \"win32\" ? path.dirname(command) : path.dirname(path.dirname(command))\n try {\n if (!npmLauncher(command, prefix, platform)) return undefined\n accessSync(prefix, constants.W_OK)\n return prefix\n } catch { return undefined }\n}\n\nexport async function installSharedOpenCode(version: string, options: InstallationHost & {\n node?: string; npm?: string\n execute?: typeof executeInstaller\n probe?: typeof probeBinaryVersionAsync\n registerPath?: (directory: string) => Promise<void>\n} = {}): Promise<string> {\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(version)) throw new Error(\"Installation requires an exact stable OpenCode version\")\n assertSupportedOpenCode(version)\n const platform = options.platform ?? process.platform\n const prefix = sharedInstallPrefix(options)\n if (!prefix) throw new Error(\"The PATH executable is not a writable npm installation\")\n const node = options.node ?? process.execPath\n const npm = options.npm ?? bundledNpm(node)\n if (!npm) throw new Error(\"npm is unavailable beside the CodeNomad Node runtime\")\n const binary = npmExecutable(prefix, platform)\n const probe = options.probe ?? probeBinaryVersionAsync\n return withInstallationLock(prefix, async () => {\n const oldBinary = npmCommand(prefix, platform)?.binary ?? binary\n const existing = await probe(oldBinary)\n if (existing.valid && (!existing.version || !/^(?:\\d+\\.\\d+\\.\\d+|0\\.0\\.0-beta-\\d+)$/.test(existing.version))) {\n throw new Error(\"Cannot replace an unverified OpenCode version automatically\")\n }\n const target = existing.valid && existing.version && /^\\d+\\.\\d+\\.\\d+$/.test(existing.version) && compareVersionStrings(existing.version, version) > 0 ? existing.version : version\n const directory = npmCommandDirectory(prefix, platform)\n const commandHost = { ...options, env: { PATH: directory, PATHEXT: \".EXE;.CMD;.BAT\" } }\n if (!existing.valid || existing.version !== target || !npmCommand(prefix, platform) || oldBinary !== binary) {\n const env = { ...(options.env ?? process.env) }\n // 2.0.15 -> 2.0.16 is qualified with a live Windows service. Older\n // installers and same-version launcher repair keep the npm preflight.\n if (existing.valid && existing.version && /^\\d+\\.\\d+\\.\\d+$/.test(existing.version)\n && compareVersionStrings(existing.version, \"2.0.15\") >= 0 && existing.version !== target\n && npmCommand(prefix, platform)) {\n await upgradeSharedOpenCode({ binary: oldBinary, version: target, prefix, node, npm, env, platform, execute: options.execute })\n } else {\n await assertExecutableWritable(oldBinary, platform)\n const key = Object.keys(env).find(key => key.toLowerCase() === \"path\") ?? \"PATH\"\n env[key] = `${path.dirname(node)}${platform === \"win32\" ? \";\" : \":\"}${env[key] || \"\"}`\n await (options.execute ?? executeInstaller)(node, [npm, \"install\", \"--global\", \"--prefix\", prefix,\n \"--no-audit\", \"--no-fund\", \"--registry=https://registry.npmjs.org\", `@opencode/cli@${target}`], env)\n }\n }\n const result = await probe(binary)\n if (!result.valid || !result.version || !/^\\d+\\.\\d+\\.\\d+$/.test(result.version) || compareVersionStrings(result.version, target) < 0) {\n throw new Error(\"Installed OpenCode version verification failed\")\n }\n if (!findPathOpenCode(commandHost) || sharedInstallPrefix(commandHost) !== prefix) {\n throw new Error(\"npm did not publish the verified OpenCode terminal command\")\n }\n await (options.registerPath ?? (directory => registerUserPath(directory, options)))(directory)\n return binary\n })\n}\n"]}
1
+ {"version":3,"file":"shared-installation.js","sourceRoot":"","sources":["../../src/opencode-update/shared-installation.ts"],"names":[],"mappings":"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","sourcesContent":["import { accessSync, constants, existsSync, readFileSync, realpathSync, statSync } from \"node:fs\"\nimport os from \"node:os\"\nimport path from \"node:path\"\nimport { bundledNpm, executeInstaller } from \"./npm-runtime\"\nimport { isRetiredInstallation } from \"./retired-installation\"\nimport { registerUserPath } from \"./user-path\"\nimport { probeBinaryVersionAsync, buildSpawnSpec } from \"../workspaces/spawn\"\nimport { assertSupportedOpenCode } from \"../opencode/runtime-support\"\nimport { compareVersionStrings } from \"../releases/release-monitor\"\nimport { assertExecutableWritable, withInstallationLock } from \"./installation-lock\"\nimport { upgradeSharedOpenCode } from \"./native-upgrade\"\nimport { isIncompleteWindowsNpmShim } from \"./incomplete-installation\"\n\nexport interface InstallationHost {\n home?: string\n platform?: NodeJS.Platform\n env?: NodeJS.ProcessEnv\n}\n\nexport function userNpmPrefix(host: InstallationHost = {}): string {\n const home = host.home ?? os.homedir()\n const env = host.env ?? process.env\n const configured = Object.entries(env).find(([key]) => key.toLowerCase() === \"npm_config_prefix\")?.[1]\n if (configured && path.isAbsolute(configured)) return configured\n return (host.platform ?? process.platform) === \"win32\"\n ? path.join(env.APPDATA || path.join(home, \"AppData\", \"Roaming\"), \"npm\") : path.join(home, \".local\")\n}\n\nexport function npmCommandDirectory(prefix: string, platform = process.platform): string {\n return platform === \"win32\" ? prefix : path.join(prefix, \"bin\")\n}\n\nexport function npmExecutable(prefix: string, platform = process.platform): string {\n return path.join(prefix, ...(platform === \"win32\" ? [] : [\"lib\"]), \"node_modules\", \"@opencode\", \"cli\", \"bin\", \"opencode.exe\")\n}\n\nfunction npmPackage(prefix: string, platform: NodeJS.Platform) {\n try {\n const file = path.join(path.dirname(npmExecutable(prefix, platform)), \"..\", \"package.json\")\n const manifest = JSON.parse(readFileSync(file, \"utf8\")) as { name?: string; bin?: Record<string, string> }\n return manifest.name === \"@opencode/cli\" ? manifest : undefined\n } catch { return undefined }\n}\n\nfunction npmLauncher(command: string, prefix: string, platform: NodeJS.Platform): string | undefined {\n const manifest = npmPackage(prefix, platform)\n if (!manifest?.bin) return undefined\n const name = path.basename(command).replace(platform === \"win32\" ? /\\.cmd$/i : /$^/, \"\")\n const relative = manifest.bin[name]\n if (typeof relative !== \"string\" || !/^\\.\\/bin\\/[^/\\\\]+\\.exe$/.test(relative)) return undefined\n const packageRoot = path.join(path.dirname(npmExecutable(prefix, platform)), \"..\")\n const binary = path.resolve(packageRoot, relative)\n try {\n if (!statSync(binary).isFile()) return undefined\n const resolved = platform === \"win32\" ? buildSpawnSpec(command, [], { platform }).command : realpathSync(command)\n return realpathSync(resolved) === realpathSync(binary) ? binary : undefined\n } catch { return undefined }\n}\n\nfunction retiredNpmAlias(command: string, prefix: string, platform: NodeJS.Platform): boolean {\n const name = platform === \"win32\" ? path.basename(command).replace(/\\.cmd$/i, \"\") : path.basename(command)\n const relative = npmPackage(prefix, platform)?.bin?.[name]\n if (typeof relative !== \"string\" || !/^\\.\\/bin\\/[^/\\\\]+\\.cjs$/.test(relative)) return false\n try {\n const target = path.resolve(path.dirname(npmExecutable(prefix, platform)), \"..\", relative)\n if (platform === \"win32\") {\n const script = readFileSync(command, \"utf8\")\n if (script.length > 64 * 1024) return false\n const escaped = path.basename(target).replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n return statSync(target).isFile() && new RegExp(`[\"'](?:%dp0%|%~dp0)[\\\\\\\\/]node_modules[\\\\\\\\/]@opencode[\\\\\\\\/]cli[\\\\\\\\/]bin[\\\\\\\\/]${escaped}[\"']\\\\s+%\\\\*`, \"i\").test(script)\n }\n return realpathSync(command) === realpathSync(target)\n } catch { return false }\n}\n\nfunction npmCommand(prefix: string, platform: NodeJS.Platform): { command: string; binary: string } | undefined {\n const directory = npmCommandDirectory(prefix, platform)\n for (const name of [\"opencode2\", \"opencode\"]) {\n const command = path.join(directory, platform === \"win32\" ? `${name}.cmd` : name)\n const binary = npmLauncher(command, prefix, platform)\n if (binary) return { command, binary }\n }\n}\n\nexport function findPathOpenCode(host: InstallationHost = {}): string | undefined {\n const env = host.env ?? process.env\n const platform = host.platform ?? process.platform\n const key = Object.keys(env).find(key => key.toLowerCase() === \"path\")\n const extensions = platform === \"win32\" ? (env.PATHEXT || \".COM;.EXE;.BAT;.CMD\").toLowerCase().split(\";\") : [\"\"]\n for (const entry of (env[key ?? \"PATH\"] || \"\").split(platform === \"win32\" ? \";\" : \":\")) {\n const directory = entry.replace(/^\"|\"$/g, \"\")\n if (!directory || !path.isAbsolute(directory)) continue\n const prefix = platform === \"win32\" ? directory : path.dirname(directory)\n const inNpmBin = path.resolve(directory) === path.resolve(npmCommandDirectory(prefix, platform))\n for (const name of [\"opencode2\", \"opencode\"]) for (const extension of extensions) {\n const candidate = path.join(directory, `${name}${extension}`)\n try {\n if (!statSync(candidate).isFile()) continue\n if (isRetiredInstallation(candidate)) continue\n accessSync(candidate, platform === \"win32\" ? constants.F_OK : constants.X_OK)\n if (inNpmBin && npmPackage(prefix, platform)) {\n if (retiredNpmAlias(candidate, prefix, platform)) continue\n }\n return candidate\n } catch { /* Continue in PATH order. */ }\n }\n }\n}\n\nexport function resolveDefaultInstallation(host: InstallationHost = {}): { path: string; source?: \"path\" | \"user\" } {\n const platform = host.platform ?? process.platform\n const prefix = userNpmPrefix(host)\n const binary = npmCommand(prefix, platform)?.binary ?? npmExecutable(prefix, platform)\n const command = findPathOpenCode(host)\n if (command) {\n if (platform === \"win32\" && isIncompleteWindowsNpmShim(command, prefix, binary)) return { path: binary, source: \"user\" }\n return { path: command, source: \"path\" }\n }\n if (existsSync(binary) && !isRetiredInstallation(binary)) return { path: binary, source: \"user\" }\n return { path: \"opencode2\" }\n}\n\n/** Only a verified npm installation may be updated via npm. Homebrew/curl and\n * explicit custom executable choices retain their own installation authority. */\nexport function sharedInstallPrefix(host: InstallationHost = {}): string | undefined {\n const platform = host.platform ?? process.platform\n const userPrefix = userNpmPrefix(host)\n if (isRetiredInstallation(userPrefix)) return undefined\n const command = findPathOpenCode(host) ?? findPathOpenCode({ ...host,\n env: { PATH: npmCommandDirectory(userPrefix, platform), PATHEXT: \".EXE;.CMD;.BAT\" } })\n if (!command) return userPrefix\n const prefix = platform === \"win32\" ? path.dirname(command) : path.dirname(path.dirname(command))\n try {\n if (!npmLauncher(command, prefix, platform)\n && !(platform === \"win32\" && isIncompleteWindowsNpmShim(command, userPrefix, npmExecutable(userPrefix, platform)))) return undefined\n accessSync(prefix, constants.W_OK)\n return prefix\n } catch { return undefined }\n}\n\nexport async function installSharedOpenCode(version: string, options: InstallationHost & {\n node?: string; npm?: string\n execute?: typeof executeInstaller\n probe?: typeof probeBinaryVersionAsync\n registerPath?: (directory: string) => Promise<void>\n} = {}): Promise<string> {\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(version)) throw new Error(\"Installation requires an exact stable OpenCode version\")\n assertSupportedOpenCode(version)\n const platform = options.platform ?? process.platform\n const prefix = sharedInstallPrefix(options)\n if (!prefix) throw new Error(\"The PATH executable is not a writable npm installation\")\n const node = options.node ?? process.execPath\n const npm = options.npm ?? bundledNpm(node)\n if (!npm) throw new Error(\"npm is unavailable beside the CodeNomad Node runtime\")\n const binary = npmExecutable(prefix, platform)\n const probe = options.probe ?? probeBinaryVersionAsync\n return withInstallationLock(prefix, async () => {\n const oldBinary = npmCommand(prefix, platform)?.binary ?? binary\n const existing = await probe(oldBinary)\n if (existing.valid && (!existing.version || !/^(?:\\d+\\.\\d+\\.\\d+|0\\.0\\.0-beta-\\d+)$/.test(existing.version))) {\n throw new Error(\"Cannot replace an unverified OpenCode version automatically\")\n }\n const target = existing.valid && existing.version && /^\\d+\\.\\d+\\.\\d+$/.test(existing.version) && compareVersionStrings(existing.version, version) > 0 ? existing.version : version\n const directory = npmCommandDirectory(prefix, platform)\n const commandHost = { ...options, env: { PATH: directory, PATHEXT: \".EXE;.CMD;.BAT\" } }\n if (!existing.valid || existing.version !== target || !npmCommand(prefix, platform) || oldBinary !== binary) {\n const env = { ...(options.env ?? process.env) }\n // 2.0.15 -> 2.0.16 is qualified with a live Windows service. Older\n // installers and same-version launcher repair keep the npm preflight.\n if (existing.valid && existing.version && /^\\d+\\.\\d+\\.\\d+$/.test(existing.version)\n && compareVersionStrings(existing.version, \"2.0.15\") >= 0 && existing.version !== target\n && npmCommand(prefix, platform)) {\n await upgradeSharedOpenCode({ binary: oldBinary, version: target, prefix, node, npm, env, platform, execute: options.execute })\n } else {\n await assertExecutableWritable(oldBinary, platform)\n const key = Object.keys(env).find(key => key.toLowerCase() === \"path\") ?? \"PATH\"\n env[key] = `${path.dirname(node)}${platform === \"win32\" ? \";\" : \":\"}${env[key] || \"\"}`\n await (options.execute ?? executeInstaller)(node, [npm, \"install\", \"--global\", \"--prefix\", prefix,\n \"--no-audit\", \"--no-fund\", \"--registry=https://registry.npmjs.org\", `@opencode/cli@${target}`], env)\n }\n }\n const result = await probe(binary)\n if (!result.valid || !result.version || !/^\\d+\\.\\d+\\.\\d+$/.test(result.version) || compareVersionStrings(result.version, target) < 0) {\n throw new Error(\"Installed OpenCode version verification failed\")\n }\n if (!findPathOpenCode(commandHost) || sharedInstallPrefix(commandHost) !== prefix) {\n throw new Error(\"npm did not publish the verified OpenCode terminal command\")\n }\n await (options.registerPath ?? (directory => registerUserPath(directory, options)))(directory)\n return binary\n })\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuralnomads/codenomad-dev",
3
- "version": "0.20.0-dev-20260925-25e07c74",
3
+ "version": "0.20.0-dev-20260926-14b1eaa4",
4
4
  "description": "CodeNomad Server",
5
5
  "license": "MIT",
6
6
  "author": {