@venturewild/workspace 0.6.22 → 0.6.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venturewild/workspace",
3
- "version": "0.6.22",
3
+ "version": "0.6.23",
4
4
  "description": "Claude Code Web — Replit/Lovable-style chat-first browser UI that wraps the AI agent already installed on your machine.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -128,9 +128,11 @@ export async function fetchLatestVersion(channel, {
128
128
  /** Run `npm i -g <spec>`. Resolves {code, output, timedOut?, error?}; never rejects. */
129
129
  export function npmInstall(spec, {
130
130
  spawnImpl = spawn, timeoutMs = 180000, ensurePathImpl = ensureToolPath, env = process.env,
131
+ platform = process.platform,
131
132
  } = {}) {
132
133
  return new Promise((resolve) => {
133
- const cmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
134
+ const isWin = platform === 'win32';
135
+ const cmd = isWin ? 'npm.cmd' : 'npm';
134
136
  // The always-on supervisor (our caller in the field) runs under launchd/GUI,
135
137
  // which inherits a MINIMAL PATH omitting ~/.npm-global, /usr/local/bin,
136
138
  // Homebrew, nvm — so a bare `npm` spawn would ENOENT (the 0.1.8 `claude`
@@ -140,7 +142,15 @@ export function npmInstall(spec, {
140
142
  try { ensurePathImpl(childEnv); } catch { /* best-effort — fall back to inherited PATH */ }
141
143
  let child;
142
144
  try {
143
- child = spawnImpl(cmd, ['i', '-g', spec], { windowsHide: true, env: childEnv });
145
+ // On Windows npm is a `.cmd` shim, and Node >=18.20.2/20.12.2/22 REFUSES to
146
+ // spawn a .cmd/.bat directly (CVE-2024-27980 hardening) — it throws EINVAL
147
+ // synchronously. That is exactly how field auto-update failed instantly
148
+ // (code=-1 in ~16ms, never reaching npm). shell:true routes the shim
149
+ // through cmd.exe so it actually runs. The only interpolated arg is a
150
+ // package spec we build ourselves (a constant name + a semver from npm's
151
+ // own dist-tag), so there is no shell-injection surface. POSIX stays
152
+ // shell:false — npm is a real executable there.
153
+ child = spawnImpl(cmd, ['i', '-g', spec], { windowsHide: true, env: childEnv, shell: isWin });
144
154
  } catch (e) {
145
155
  return resolve({ code: -1, error: e?.message || String(e), output: '' });
146
156
  }
@@ -226,7 +236,7 @@ export class AutoUpdater {
226
236
  this.logImpl(`auto-update: installing ${this.packageName}@${target} (from ${from || 'unknown'})`);
227
237
  const install = await this.installImpl(`${this.packageName}@${target}`);
228
238
  if (install.code !== 0) {
229
- this.logImpl(`auto-update: install failed code=${install.code}${install.timedOut ? ' (timeout)' : ''}`);
239
+ this.logImpl(`auto-update: install failed code=${install.code}${install.timedOut ? ' (timeout)' : ''}${install.error ? ` error=${install.error}` : ''}`);
230
240
  const rec = recordUpdate(this.globalDir, { from, to: target, at: this.nowImpl(), status: 'install-failed' });
231
241
  this.onUpdate?.(rec);
232
242
  return { ok: false, stage: 'install', install, rec };