ideawave 0.0.106 → 0.0.108

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
@@ -16,4 +16,5 @@ bunx ideawave@latest
16
16
  Keep the TUI running, open [ideawave.app](https://ideawave.app), and start
17
17
  working with your agent directly on the board.
18
18
 
19
- Requires Node.js 22 or newer. Available for macOS, Linux, and Windows.
19
+ Requires Node.js 22 or newer, or Bun 1.2.16 or newer (no Node needed).
20
+ Available for macOS, Linux, and Windows.
package/bin/ideawave.mjs CHANGED
@@ -4,7 +4,7 @@ import { accessSync, constants, readFileSync } from "node:fs";
4
4
  import { createRequire } from "node:module";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
- import { isPathInside, launcherRuntimeError } from "./launcher-core.mjs";
7
+ import { corruptedHostLockEvidence, detectLauncherRuntime, isPathInside } from "./launcher-core.mjs";
8
8
  import { detectLinuxLibc, resolveHostTarget } from "./platform.mjs";
9
9
 
10
10
  const require = createRequire(import.meta.url);
@@ -25,6 +25,40 @@ function readManifest(filename, label) {
25
25
  }
26
26
  }
27
27
 
28
+ function missingHostError(target, version) {
29
+ const base = `IdeaWave's ${target.key} host package is missing (expected ${target.packageName}@${version}).`;
30
+ const reinstall =
31
+ `Reinstall with optional dependencies enabled, for example \`npm install ideawave@${version} --include=optional\`.`;
32
+ if (path.basename(path.resolve(packageRoot, "..")) !== "node_modules") {
33
+ return `${base} ${reinstall}`;
34
+ }
35
+ const treeRoot = path.resolve(packageRoot, "..", "..");
36
+ let lockRaw;
37
+ try {
38
+ lockRaw = readFileSync(path.join(treeRoot, "package-lock.json"), "utf8");
39
+ } catch {
40
+ return `${base} ${reinstall}`;
41
+ }
42
+ if (!corruptedHostLockEvidence(lockRaw, target.packageName)) {
43
+ return `${base} ${reinstall}`;
44
+ }
45
+ if (treeRoot.split(path.sep).includes("_npx")) {
46
+ const removeExample = process.platform === "win32"
47
+ ? `\`rmdir /s /q "${treeRoot}"\``
48
+ : `\`rm -rf "${treeRoot}"\``;
49
+ return (
50
+ `${base} A known npm bug (https://github.com/npm/cli/issues/4828) corrupted this package-runner cache entry ` +
51
+ `while updating IdeaWave: its lockfile resolved IdeaWave's host for a different platform. ` +
52
+ `Delete the cache entry with ${removeExample}, then run \`npx ideawave\` again.`
53
+ );
54
+ }
55
+ return (
56
+ `${base} A known npm bug (https://github.com/npm/cli/issues/4828) left this installation's package-lock.json ` +
57
+ `resolved for a different platform. Delete node_modules and package-lock.json in ${treeRoot}, ` +
58
+ `then reinstall with \`npm install ideawave@${version} --include=optional\`.`
59
+ );
60
+ }
61
+
28
62
  function resolveDistribution() {
29
63
  const manifest = readManifest(path.join(packageRoot, "package.json"), "IdeaWave launcher");
30
64
  if (manifest.name !== "ideawave" || typeof manifest.version !== "string" || !manifest.version) {
@@ -51,10 +85,7 @@ function resolveDistribution() {
51
85
  try {
52
86
  hostManifestPath = require.resolve(`${target.packageName}/package.json`);
53
87
  } catch {
54
- throw new Error(
55
- `IdeaWave's ${target.key} host package is missing (expected ${target.packageName}@${manifest.version}). ` +
56
- `Reinstall with optional dependencies enabled, for example \`npm install ideawave@${manifest.version} --include=optional\`.`,
57
- );
88
+ throw new Error(missingHostError(target, manifest.version));
58
89
  }
59
90
  const hostManifest = readManifest(hostManifestPath, `IdeaWave ${target.key} host`);
60
91
  if (hostManifest.name !== target.packageName) {
@@ -103,23 +134,31 @@ function resolveDistribution() {
103
134
  };
104
135
  }
105
136
 
106
- const runtimeError = launcherRuntimeError();
107
- if (runtimeError) {
108
- fail(runtimeError);
137
+ const runtime = detectLauncherRuntime();
138
+ if (runtime.error) {
139
+ fail(runtime.error);
109
140
  } else {
110
141
  try {
111
142
  const distribution = resolveDistribution();
143
+ // Start from a copy so stale launcher-contract values and a leaked
144
+ // BUN_BE_BUN (which would make the compiled host act as the bun CLI
145
+ // instead of IdeaWave) never reach the host or its children.
146
+ const env = { ...process.env };
147
+ delete env.BUN_BE_BUN;
148
+ delete env.IDEAWAVE_LAUNCHER_NODE_PATH;
149
+ delete env.IDEAWAVE_LAUNCHER_NODE_VERSION;
150
+ env.IDEAWAVE_DISTRIBUTION_MODE = "standalone";
151
+ env.IDEAWAVE_LAUNCHER_RUNTIME = runtime.kind;
152
+ if (runtime.kind === "node") {
153
+ env.IDEAWAVE_LAUNCHER_NODE_PATH = process.execPath;
154
+ env.IDEAWAVE_LAUNCHER_NODE_VERSION = process.version;
155
+ }
156
+ env.IDEAWAVE_PLAYWRIGHT_CORE_PATH = distribution.playwrightRoot;
157
+ env.IDEAWAVE_PRODUCT_VERSION = distribution.manifest.version;
112
158
  const child = spawn(distribution.executable, process.argv.slice(2), {
113
159
  cwd: process.cwd(),
114
160
  stdio: "inherit",
115
- env: {
116
- ...process.env,
117
- IDEAWAVE_DISTRIBUTION_MODE: "standalone",
118
- IDEAWAVE_LAUNCHER_NODE_PATH: process.execPath,
119
- IDEAWAVE_LAUNCHER_NODE_VERSION: process.version,
120
- IDEAWAVE_PLAYWRIGHT_CORE_PATH: distribution.playwrightRoot,
121
- IDEAWAVE_PRODUCT_VERSION: distribution.manifest.version,
122
- },
161
+ env,
123
162
  });
124
163
 
125
164
  const signalHandlers = new Map();
@@ -1,32 +1,99 @@
1
1
  import path from "node:path";
2
2
 
3
3
  export const MINIMUM_NODE_MAJOR = 22;
4
+ // BUN_BE_BUN self-execution of the compiled host exists since Bun 1.2.16;
5
+ // older Bun launchers predate the runtime contract the host relies on.
6
+ export const MINIMUM_BUN_VERSION = "1.2.16";
4
7
 
5
8
  export function parseNodeMajor(version) {
6
9
  const match = /^v?(\d+)(?:\.|$)/.exec(String(version));
7
10
  return match ? Number(match[1]) : null;
8
11
  }
9
12
 
13
+ export function parseBunVersion(version) {
14
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(String(version));
15
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
16
+ }
17
+
18
+ function bunVersionAtLeast(version, minimum) {
19
+ const actual = parseBunVersion(version);
20
+ const required = parseBunVersion(minimum);
21
+ if (!actual || !required) return false;
22
+ for (let index = 0; index < 3; index += 1) {
23
+ if (actual[index] !== required[index]) return actual[index] > required[index];
24
+ }
25
+ return true;
26
+ }
27
+
10
28
  /**
29
+ * Identify the JavaScript runtime executing the launcher. Supported launch
30
+ * paths are Node.js 22+ (`npx ideawave`, or `bunx ideawave` with Node
31
+ * installed) and Bun 1.2.16+ (`bunx ideawave` on a Bun-only machine, or
32
+ * `bunx --bun ideawave`). Anything else yields a `kind: null` error result.
33
+ *
11
34
  * @param {{ nodeVersion?: string, bunVersion?: string | null, runtimeName?: string } | undefined} options
35
+ * @returns {{ kind: "node" | "bun" | null, version: string, error: string | null }}
12
36
  */
13
- export function launcherRuntimeError(options = {}) {
37
+ export function detectLauncherRuntime(options = {}) {
14
38
  const {
15
39
  nodeVersion = process.version,
16
40
  bunVersion = process.versions?.bun,
17
41
  runtimeName = process.release?.name,
18
42
  } = options;
19
43
  if (bunVersion) {
20
- return "IdeaWave's launcher must run on Node.js, not Bun. Run `bunx ideawave` without `--bun`, or use `npx ideawave`.";
44
+ if (!bunVersionAtLeast(bunVersion, MINIMUM_BUN_VERSION)) {
45
+ return {
46
+ kind: null,
47
+ version: String(bunVersion),
48
+ error:
49
+ `IdeaWave requires Bun ${MINIMUM_BUN_VERSION} or newer when launched with Bun; this launcher is using ${bunVersion}. ` +
50
+ "Run `bun upgrade`, or use `npx ideawave` with Node.js 22 or newer.",
51
+ };
52
+ }
53
+ return { kind: "bun", version: String(bunVersion), error: null };
21
54
  }
22
55
  if (runtimeName !== "node") {
23
- return `IdeaWave's launcher requires Node.js 22 or newer; detected ${runtimeName || "an unknown runtime"}.`;
56
+ return {
57
+ kind: null,
58
+ version: "",
59
+ error:
60
+ `IdeaWave's launcher requires Node.js ${MINIMUM_NODE_MAJOR} or newer or Bun ${MINIMUM_BUN_VERSION} or newer; ` +
61
+ `detected ${runtimeName || "an unknown runtime"}.`,
62
+ };
24
63
  }
25
64
  const major = parseNodeMajor(nodeVersion);
26
65
  if (major === null || major < MINIMUM_NODE_MAJOR) {
27
- return `IdeaWave requires Node.js 22 or newer; this launcher is using ${nodeVersion || "an unknown version"}.`;
66
+ return {
67
+ kind: null,
68
+ version: String(nodeVersion ?? ""),
69
+ error:
70
+ `IdeaWave requires Node.js ${MINIMUM_NODE_MAJOR} or newer; this launcher is using ${nodeVersion || "an unknown version"}. ` +
71
+ "Upgrade Node.js, or launch with Bun 1.2.16 or newer via `bunx ideawave`.",
72
+ };
73
+ }
74
+ return { kind: "node", version: String(nodeVersion), error: null };
75
+ }
76
+
77
+ /**
78
+ * npm's arborist can update an existing install (notably an `_npx` cache
79
+ * entry) while keeping a package-lock that resolved IdeaWave's platform host
80
+ * for a different machine, so the matching host is silently never installed
81
+ * (https://github.com/npm/cli/issues/4828). A lockfile that lists host
82
+ * packages but not the required one is direct evidence of that corruption.
83
+ *
84
+ * @param {string} lockRaw
85
+ * @param {string} packageName
86
+ */
87
+ export function corruptedHostLockEvidence(lockRaw, packageName) {
88
+ try {
89
+ const lock = JSON.parse(lockRaw);
90
+ const packages = lock && typeof lock === "object" ? lock.packages : null;
91
+ if (!packages || typeof packages !== "object") return false;
92
+ const hosts = Object.keys(packages).filter((key) => key.startsWith("node_modules/ideawave-host-"));
93
+ return hosts.length > 0 && !hosts.includes(`node_modules/${packageName}`);
94
+ } catch {
95
+ return false;
28
96
  }
29
- return null;
30
97
  }
31
98
 
32
99
  export function isPathInside(root, candidate) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ideawave",
3
- "version": "0.0.106",
3
+ "version": "0.0.108",
4
4
  "description": "IdeaWave local agent host and terminal UI",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -38,12 +38,12 @@
38
38
  "playwright-core": "1.62.1"
39
39
  },
40
40
  "optionalDependencies": {
41
- "ideawave-host-darwin-arm64": "0.0.106",
42
- "ideawave-host-darwin-x64": "0.0.106",
43
- "ideawave-host-linux-arm64": "0.0.106",
44
- "ideawave-host-linux-arm64-musl": "0.0.106",
45
- "ideawave-host-linux-x64": "0.0.106",
46
- "ideawave-host-linux-x64-musl": "0.0.106",
47
- "ideawave-host-windows": "0.0.106"
41
+ "ideawave-host-darwin-arm64": "0.0.108",
42
+ "ideawave-host-darwin-x64": "0.0.108",
43
+ "ideawave-host-linux-arm64": "0.0.108",
44
+ "ideawave-host-linux-arm64-musl": "0.0.108",
45
+ "ideawave-host-linux-x64": "0.0.108",
46
+ "ideawave-host-linux-x64-musl": "0.0.108",
47
+ "ideawave-host-windows": "0.0.108"
48
48
  }
49
49
  }