@neuralnomads/codenomad-dev 0.11.3-dev-20260219-9800afb7 → 0.11.3-dev-20260220-34d3f803

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,18 +5,21 @@
5
5
  ## Features & Capabilities
6
6
 
7
7
  ### 🌍 Deployment Freedom
8
+
8
9
  - **Remote Access**: Host CodeNomad on a powerful workstation and access it from your lightweight laptop.
9
10
  - **Code Anywhere**: Tunnel in via VPN or SSH to code securely from coffee shops or while traveling.
10
11
  - **Multi-Device**: The responsive web client works on tablets and iPads, turning any screen into a dev terminal.
11
12
  - **Always-On**: Run as a background service so your sessions are always ready when you connect.
12
13
 
13
14
  ### ⚡️ Workspace Power
15
+
14
16
  - **Multi-Instance**: Juggle multiple OpenCode sessions side-by-side with per-instance tabs.
15
17
  - **Long-Context Native**: Scroll through massive transcripts without hitches.
16
18
  - **Deep Task Awareness**: Monitor background tasks and child sessions without losing your flow.
17
19
  - **Command Palette**: A single, global palette to jump tabs, launch tools, and fire shortcuts.
18
20
 
19
21
  ## Prerequisites
22
+
20
23
  - **OpenCode**: `opencode` must be installed and configured on your system.
21
24
  - Node.js 18+ and npm (for running or building from source).
22
25
  - A workspace folder on disk you want to serve.
@@ -25,6 +28,7 @@
25
28
  ## Usage
26
29
 
27
30
  ### Run via npx (Recommended)
31
+
28
32
  You can run CodeNomad directly without installing it:
29
33
 
30
34
  ```sh
@@ -43,6 +47,7 @@ On startup, CodeNomad prints two URLs:
43
47
  - `Remote Connection URL : ...` (used by browsers/other machines when remote access is enabled)
44
48
 
45
49
  ### Install Globally
50
+
46
51
  Or install it globally to use the `codenomad` command:
47
52
 
48
53
  ```sh
@@ -51,6 +56,7 @@ codenomad --launch
51
56
  ```
52
57
 
53
58
  ### Install Locally (per-project)
59
+
54
60
  If you prefer to install CodeNomad into a project and run the local binary:
55
61
 
56
62
  ```sh
@@ -61,6 +67,7 @@ npx codenomad --launch
61
67
  (`npx codenomad ...` will use `./node_modules/.bin/codenomad` when present.)
62
68
 
63
69
  ### Common Flags
70
+
64
71
  You can configure the server using flags or environment variables:
65
72
 
66
73
  | Flag | Env Variable | Description |
@@ -74,7 +81,7 @@ You can configure the server using flags or environment variables:
74
81
  | `--tls-ca <path>` | `CLI_TLS_CA` | Optional CA chain/bundle (PEM) |
75
82
  | `--tlsSANs <list>` | `CLI_TLS_SANS` | Additional TLS SANs (comma-separated) |
76
83
  | `--host <addr>` | `CLI_HOST` | Interface to bind (default 127.0.0.1) |
77
- | `--workspace-root <path>` | `CLI_WORKSPACE_ROOT` | Default root for new workspaces |
84
+ | `--workspace-root <path>` | `CLI_WORKSPACE_ROOT` | Restricts the root path where new workspaces can be opened. Git worktrees are created in `.codenomad/worktrees` inside the project folder. |
78
85
  | `--unrestricted-root` | `CLI_UNRESTRICTED_ROOT` | Allow full-filesystem browsing |
79
86
  | `--config <path>` | `CLI_CONFIG` | Config file location |
80
87
  | `--launch` | `CLI_LAUNCH` | Open the UI in a Chromium-based browser |
@@ -87,10 +94,11 @@ You can configure the server using flags or environment variables:
87
94
  | `--ui-dir <path>` | `CLI_UI_DIR` | Directory containing the built UI bundle |
88
95
  | `--ui-dev-server <url>` | `CLI_UI_DEV_SERVER` | Proxy UI requests to a running dev server (requires `--https=false --http=true`) |
89
96
  | `--ui-no-update` | `CLI_UI_NO_UPDATE` | Disable remote UI updates |
90
- | `--ui-auto-update <enabled>` | `CLI_UI_AUTO_UPDATE` | Enable remote UI updates (true|false) |
97
+ | `--ui-auto-update <enabled>` | `CLI_UI_AUTO_UPDATE` | Enable remote UI updates (`true` |
91
98
  | `--ui-manifest-url <url>` | `CLI_UI_MANIFEST_URL` | Remote UI manifest URL |
92
99
 
93
100
  ### Dev Releases (Advanced)
101
+
94
102
  If you want the latest bleeding-edge builds (published as GitHub pre-releases), use the dev package:
95
103
 
96
104
  ```sh
@@ -141,12 +149,14 @@ codenomad --tlsSANs "localhost,127.0.0.1,my-hostname,192.168.1.10"
141
149
  ```
142
150
 
143
151
  ### Authentication
152
+
144
153
  - Default behavior: CodeNomad requires a login (username/password) and stores a session cookie in the browser.
145
154
  - `--dangerously-skip-auth` / `CODENOMAD_SKIP_AUTH=true` disables the login prompt and treats all requests as authenticated.
146
155
  Use this only when access is already protected by another layer (SSO proxy, VPN, Coder workspace auth, etc.).
147
156
  If you bind to `0.0.0.0` while skipping auth, anyone who can reach the port can access the API.
148
157
 
149
158
  ### Progressive Web App (PWA)
159
+
150
160
  When running as a server CodeNomad can also be installed as a PWA from any supported browser, giving you a native app experience just like the Electron installation but executing on the remote server instead.
151
161
 
152
162
  1. Open the CodeNomad UI in a Chromium-based browser (Chrome, Edge, Brave, etc.).
@@ -158,5 +168,6 @@ When running as a server CodeNomad can also be installed as a PWA from any suppo
158
168
  > If you host CodeNomad on a remote machine, use HTTPS. Self-signed certificates generally won't work unless they are explicitly trusted by the device/browser (e.g., via a custom CA).
159
169
 
160
170
  ### Data Storage
171
+
161
172
  - **Config**: `~/.config/codenomad/config.json`
162
173
  - **Instance Data**: `~/.config/codenomad/instances` (chat history, etc.)
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ function parseCliOptions(argv) {
45
45
  .addOption(new Option("--tls-cert <path>", "TLS certificate (PEM)").env("CLI_TLS_CERT"))
46
46
  .addOption(new Option("--tls-ca <path>", "TLS CA chain (PEM)").env("CLI_TLS_CA"))
47
47
  .addOption(new Option("--tlsSANs <list>", "Additional TLS SANs (comma-separated)").env("CLI_TLS_SANS"))
48
- .addOption(new Option("--workspace-root <path>", "Workspace root directory").env("CLI_WORKSPACE_ROOT").default(process.cwd()))
48
+ .addOption(new Option("--workspace-root <path>", "Restricts root path where workspaces can be opened").env("CLI_WORKSPACE_ROOT").default(process.cwd()))
49
49
  .addOption(new Option("--root <path>").env("CLI_ROOT").hideHelp(true))
50
50
  .addOption(new Option("--unrestricted-root", "Allow browsing the full filesystem").env("CLI_UNRESTRICTED_ROOT").default(false))
51
51
  .addOption(new Option("--config <path>", "Path to the config file").env("CLI_CONFIG").default(DEFAULT_CONFIG_PATH))
@@ -4,7 +4,7 @@ import { connect } from "net";
4
4
  import { FileSystemBrowser } from "../filesystem/browser";
5
5
  import { searchWorkspaceFiles } from "../filesystem/search";
6
6
  import { clearWorkspaceSearchCache } from "../filesystem/search-cache";
7
- import { WorkspaceRuntime, probeBinaryVersion } from "./runtime";
7
+ import { WorkspaceRuntime } from "./runtime";
8
8
  import { getOpencodeConfigDir } from "../opencode-config.js";
9
9
  import { buildOpencodeBasicAuthHeader, DEFAULT_OPENCODE_USERNAME, generateOpencodeServerPassword, OPENCODE_SERVER_PASSWORD_ENV, OPENCODE_SERVER_USERNAME_ENV, } from "./opencode-auth";
10
10
  const STARTUP_STABILITY_DELAY_MS = 1500;
@@ -67,9 +67,6 @@ export class WorkspaceManager {
67
67
  createdAt: new Date().toISOString(),
68
68
  updatedAt: new Date().toISOString(),
69
69
  };
70
- if (!descriptor.binaryVersion) {
71
- descriptor.binaryVersion = this.detectBinaryVersion(resolvedBinaryPath);
72
- }
73
70
  this.workspaces.set(id, descriptor);
74
71
  this.options.eventBus.publish({ type: "workspace.created", workspace: descriptor });
75
72
  const serverConfig = this.options.settings.getOwner("config", "server");
@@ -99,7 +96,10 @@ export class WorkspaceManager {
99
96
  environment,
100
97
  onExit: (info) => this.handleProcessExit(info.workspaceId, info),
101
98
  });
102
- await this.waitForWorkspaceReadiness({ workspaceId: id, port, exitPromise, getLastOutput });
99
+ const runtimeVersion = await this.waitForWorkspaceReadiness({ workspaceId: id, port, exitPromise, getLastOutput });
100
+ if (runtimeVersion) {
101
+ descriptor.binaryVersion = runtimeVersion;
102
+ }
103
103
  descriptor.pid = pid;
104
104
  descriptor.port = port;
105
105
  descriptor.status = "ready";
@@ -208,27 +208,6 @@ export class WorkspaceManager {
208
208
  }
209
209
  return candidates[0] ?? "";
210
210
  }
211
- detectBinaryVersion(resolvedPath) {
212
- if (!resolvedPath) {
213
- return undefined;
214
- }
215
- const result = probeBinaryVersion(resolvedPath);
216
- if (result.valid) {
217
- if (result.version) {
218
- this.options.logger.debug({ binary: resolvedPath, version: result.version }, "Detected binary version");
219
- return result.version;
220
- }
221
- if (result.reported) {
222
- this.options.logger.debug({ binary: resolvedPath, reported: result.reported }, "Binary reported version string");
223
- return result.reported;
224
- }
225
- return undefined;
226
- }
227
- if (result.error) {
228
- this.options.logger.warn({ binary: resolvedPath, err: result.error }, "Failed to detect binary version");
229
- }
230
- return undefined;
231
- }
232
211
  async waitForWorkspaceReadiness(params) {
233
212
  await Promise.race([
234
213
  this.waitForPortAvailability(params.port),
@@ -236,13 +215,14 @@ export class WorkspaceManager {
236
215
  throw this.buildStartupError(params.workspaceId, "exited before becoming ready", info, params.getLastOutput());
237
216
  }),
238
217
  ]);
239
- await this.waitForInstanceHealth(params);
218
+ const version = await this.waitForInstanceHealth(params);
240
219
  await Promise.race([
241
220
  this.delay(STARTUP_STABILITY_DELAY_MS),
242
221
  params.exitPromise.then((info) => {
243
222
  throw this.buildStartupError(params.workspaceId, "exited shortly after start", info, params.getLastOutput());
244
223
  }),
245
224
  ]);
225
+ return version;
246
226
  }
247
227
  async waitForInstanceHealth(params) {
248
228
  const probeResult = await Promise.race([
@@ -252,7 +232,7 @@ export class WorkspaceManager {
252
232
  }),
253
233
  ]);
254
234
  if (probeResult.ok) {
255
- return;
235
+ return probeResult.version;
256
236
  }
257
237
  const latestOutput = params.getLastOutput().trim();
258
238
  if (latestOutput) {
@@ -262,7 +242,7 @@ export class WorkspaceManager {
262
242
  throw new Error(`Workspace ${params.workspaceId} failed health check: ${reason}.`);
263
243
  }
264
244
  async probeInstance(workspaceId, port) {
265
- const url = `http://127.0.0.1:${port}/project/current`;
245
+ const url = `http://127.0.0.1:${port}/global/health`;
266
246
  try {
267
247
  const headers = {};
268
248
  const authHeader = this.opencodeAuth.get(workspaceId)?.authorization;
@@ -271,11 +251,19 @@ export class WorkspaceManager {
271
251
  }
272
252
  const response = await fetch(url, { headers });
273
253
  if (!response.ok) {
274
- const reason = `health probe returned HTTP ${response.status}`;
254
+ const reason = `/global/health returned HTTP ${response.status}`;
275
255
  this.options.logger.debug({ workspaceId, status: response.status }, "Health probe returned server error");
276
256
  return { ok: false, reason };
277
257
  }
278
- return { ok: true };
258
+ const payload = (await response.json().catch(() => null));
259
+ const healthy = payload?.healthy === true;
260
+ const version = typeof payload?.version === "string" ? payload.version.trim() : undefined;
261
+ if (!healthy) {
262
+ const reason = "Instance reported unhealthy";
263
+ this.options.logger.debug({ workspaceId, payload }, "Health probe returned unhealthy response");
264
+ return { ok: false, reason };
265
+ }
266
+ return { ok: true, version: version || undefined };
279
267
  }
280
268
  catch (error) {
281
269
  const reason = error instanceof Error ? error.message : String(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuralnomads/codenomad-dev",
3
- "version": "0.11.3-dev-20260219-9800afb7",
3
+ "version": "0.11.3-dev-20260220-34d3f803",
4
4
  "description": "CodeNomad Server",
5
5
  "license": "MIT",
6
6
  "author": {