@vibecodr/cli 1.0.1 → 1.0.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  Pre-1.0.0 history for the `@vibecodr/cli@0.2.x` and `0.1.x` lines lives at [`docs/legacy/CHANGELOG-mcp-cli.md`](docs/legacy/CHANGELOG-mcp-cli.md). The `@vibecodr/vc-tools@0.1.x` line was the other half of the May 2026 merge; its source history is preserved in the archived [`BradenHartsell/vc-tools`](https://github.com/BradenHartsell/vc-tools) repository.
4
4
 
5
+ ## 1.0.3
6
+
7
+ Closes a Windows-CI flake in the hosted Browser Agent Workflow's idle-timeout closure path. No behavior change to the dispatcher or any CLI surface; the fix lives entirely inside `src/hosted/worker.ts` and matters only for the worker test suite plus the deployed Cloudflare Worker.
8
+
9
+ The worker's post-wait idle check measured `Date.now() - lastMeaningfulAt` after a `setTimeout` whose duration was capped at `idleTimeoutMs`. On Windows the OS system clock has ~15.6 ms tick resolution while Node's `setTimeout` uses a higher-resolution wakeup, so a `setTimeout(1_000)` could complete and a subsequent `Date.now()` read could still report 985-999 ms of elapsed wall clock — causing `>= idleTimeoutMs` to fall through and leave `closureReason` as `"completed"` when the workflow had clearly consumed its entire idle window.
10
+
11
+ The fix accounts for waits against the idle budget using the planned sleep duration (`performed.ms`) — the value the worker already requested from `setTimeout` — rather than measuring `Date.now()` across the sleep. This is the deterministic source of truth for "did this wait consume the idle window," matches the existing semantic that wait actions consume idle budget by their requested duration, and preserves all existing behavior for the happy `"completed"` path and for explicit sub-budget waits.
12
+
13
+ - `src/hosted/worker.ts`: switch post-wait idle accounting from wall-clock delta to planned-sleep duration.
14
+
15
+ ## 1.0.2
16
+
17
+ Hardens `preinstall-check.mjs` to also catch the **orphan-bin-shim** case that the 1.0.1 check missed.
18
+
19
+ When an earlier `npm install -g @vibecodr/cli` (or `@vibecodr/vc-tools`) was aborted mid-flight on Windows — typically because antivirus or an IDE held a file handle on the just-extracted tree during npm's cleanup — npm leaves the bin shim files (`vc-tools`, `vc-tools.cmd`, `vc-tools.ps1`, the `vibecodr` and `vibecodr-mcp` triples) at the global bin dir but the package itself isn't fully registered. The retry trips the same EEXIST because npm refuses to overwrite shim files it didn't write in the current install run.
20
+
21
+ The preinstall now:
22
+
23
+ - Inspects the global bin dir (`%APPDATA%\npm\` on Windows, `<prefix>/bin/` on POSIX) for any of the nine Windows shim names or three POSIX shim names.
24
+ - Cross-references against the global node_modules tree: if `<global-root>/@vibecodr/cli/package.json` doesn't exist or doesn't name `@vibecodr/cli`, the shim files are orphans.
25
+ - Prints an actionable cleanup recipe (`Remove-Item ... -Force` on Windows, `rm -f ...` on POSIX) listing the exact files to delete plus the half-installed package directory if present, then `npm install -g @vibecodr/cli`.
26
+
27
+ The check still bails for non-global installs, local dev installs from the source repo, and `VIBECDR_SKIP_PREINSTALL_CHECK=1` opt-outs, and false-positive-proofs itself: a clean re-install / upgrade of `@vibecodr/cli` (shims present + valid `@vibecodr/cli/package.json` present) passes through silently.
28
+
29
+ - `preinstall-check.mjs`: orphan-shim detection on top of the existing legacy-package check.
30
+ - `test/preinstall-check.test.ts`: 2 new cases (orphan-blocks, upgrade-passes-through) on top of the original 4.
31
+
5
32
  ## 1.0.1
6
33
 
7
34
  Fixes the global-install collision for users who already had `@vibecodr/vc-tools@0.1.x` installed globally. Both packages register a `vc-tools` bin under the same path; npm refuses to overwrite a bin owned by a different package, so the install fails with `EEXIST: file already exists`.
@@ -1,3 +1,3 @@
1
- export declare const CLI_VERSION = "1.0.1";
2
- export declare const VC_TOOLS_VERSION = "1.0.1";
1
+ export declare const CLI_VERSION = "1.0.3";
2
+ export declare const VC_TOOLS_VERSION = "1.0.3";
3
3
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
- export const CLI_VERSION = "1.0.1";
1
+ export const CLI_VERSION = "1.0.3";
2
2
  export const VC_TOOLS_VERSION = CLI_VERSION;
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecodr/cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "The official Vibecodr CLI: hosted browser, hosted computer, capsule uploads, Pulse operations, and agent-client MCP setup under one command.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -70,5 +70,8 @@
70
70
  "tsx": "^4.21.0",
71
71
  "typescript": "^5.9.3",
72
72
  "wrangler": "^4.90.0"
73
+ },
74
+ "overrides": {
75
+ "ws@<8.20.1": "^8.20.1"
73
76
  }
74
77
  }
@@ -1,20 +1,29 @@
1
- // Runs as the npm `preinstall` lifecycle script before file copy. Detects the
2
- // global-install collision where a pre-merger @vibecodr/vc-tools@0.1.x owns
3
- // the `vc-tools` bin shim and would block npm from writing the
4
- // @vibecodr/cli@1.x version of the same bin (npm refuses to overwrite a bin
5
- // owned by a different package; the user otherwise sees an unhelpful EEXIST
6
- // pointing at a file in the global bin dir). Exits non-zero with an
7
- // actionable message so npm aborts cleanly with our text instead of npm's.
1
+ // Runs as the npm `preinstall` lifecycle script before file copy. Catches two
2
+ // install-blocking scenarios for `npm install -g @vibecodr/cli` and exits
3
+ // with an actionable message instead of leaving the user staring at npm's
4
+ // bare EEXIST:
5
+ //
6
+ // 1. Legacy install: @vibecodr/vc-tools@0.1.x is globally installed. That
7
+ // package and @vibecodr/cli@1.x both register a `vc-tools` bin under
8
+ // the same path; npm refuses to overwrite a bin owned by a different
9
+ // package.
10
+ // 2. Orphan shims from a prior aborted install: an earlier `npm install
11
+ // -g @vibecodr/cli` (or @vibecodr/vc-tools) wrote some of the
12
+ // `vc-tools` / `vibecodr` / `vibecodr-mcp` shim files at the global
13
+ // bin dir then died on Windows-handle-locked file cleanup. The
14
+ // orphan files block the retry with EEXIST even though no package
15
+ // currently claims them.
8
16
  //
9
17
  // Bail-outs (the check is a no-op in any of these cases):
10
18
  // - VIBECDR_SKIP_PREINSTALL_CHECK=1 (operator opt-out)
11
19
  // - not a global install (the conflict is global-bin-only)
12
20
  // - running from inside this source repo (npm install / npm ci during
13
21
  // local dev shouldn't trip the check)
14
- // - npm ls fails for any other reason (we don't want a transient npm
15
- // error to block legitimate installs)
22
+ // - any inspection failure (`npm ls`, fs access) -- we never block a
23
+ // legitimate install on a transient diagnostic error
16
24
 
17
25
  import { execSync } from "node:child_process";
26
+ import { existsSync, readFileSync } from "node:fs";
18
27
  import path from "node:path";
19
28
  import { fileURLToPath } from "node:url";
20
29
 
@@ -23,9 +32,10 @@ const here = path.dirname(fileURLToPath(import.meta.url));
23
32
  const repoRoot = here;
24
33
 
25
34
  function isLocalDevInstall() {
26
- // The npm lifecycle sets npm_config_local_prefix (or PROJECT) to the
27
- // directory holding the package.json being installed FROM. For npm ci/install
28
- // run inside this repo, that prefix equals our repo root.
35
+ // The npm lifecycle sets npm_config_local_prefix (or INIT_CWD) to the
36
+ // directory holding the package.json being installed FROM. For
37
+ // npm ci / npm install run inside this repo, that prefix equals our
38
+ // repo root.
29
39
  const localPrefix = process.env["npm_config_local_prefix"] ?? process.env["INIT_CWD"];
30
40
  if (!localPrefix) return false;
31
41
  try {
@@ -49,6 +59,10 @@ if (isLocalDevInstall()) {
49
59
  process.exit(0);
50
60
  }
51
61
 
62
+ // ---------------------------------------------------------------------------
63
+ // Scenario 1: legacy @vibecodr/vc-tools@0.1.x globally installed
64
+ // ---------------------------------------------------------------------------
65
+
52
66
  let collidingVersion;
53
67
  try {
54
68
  const output = execSync("npm ls -g --depth 0 --json @vibecodr/vc-tools", {
@@ -70,38 +84,160 @@ try {
70
84
  // conflict.
71
85
  }
72
86
 
73
- if (!collidingVersion) {
74
- process.exit(0);
87
+ if (collidingVersion) {
88
+ printLegacyConflictMessage(collidingVersion);
89
+ process.exit(1);
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // Scenario 2: orphan vc-tools / vibecodr / vibecodr-mcp shims at the global
94
+ // bin dir with no @vibecodr/cli package currently installed to own them
95
+ // ---------------------------------------------------------------------------
96
+
97
+ function npmGlobalBinDir() {
98
+ // Windows: <prefix>/. POSIX: <prefix>/bin.
99
+ const prefix = process.env["npm_config_prefix"];
100
+ if (!prefix) return undefined;
101
+ return process.platform === "win32" ? prefix : path.join(prefix, "bin");
102
+ }
103
+
104
+ function npmGlobalRoot() {
105
+ const prefix = process.env["npm_config_prefix"];
106
+ if (!prefix) return undefined;
107
+ return process.platform === "win32"
108
+ ? path.join(prefix, "node_modules")
109
+ : path.join(prefix, "lib", "node_modules");
75
110
  }
76
111
 
77
- console.error("");
78
- console.error("==========================================================================");
79
- console.error(" @vibecodr/cli install blocked: legacy @vibecodr/vc-tools is in the way");
80
- console.error("==========================================================================");
81
- console.error("");
82
- console.error(` You have @vibecodr/vc-tools@${collidingVersion} installed globally. That`);
83
- console.error(" package and @vibecodr/cli@1.x both claim the `vc-tools` bin name on");
84
- console.error(" disk, and npm refuses to overwrite a bin owned by a different package");
85
- console.error(" (you would otherwise see a cryptic EEXIST pointing at the global bin");
86
- console.error(" dir).");
87
- console.error("");
88
- console.error(" The merged @vibecodr/cli replaces the legacy @vibecodr/vc-tools. The");
89
- console.error(" legacy line is preserved on npm under explicit version pins; nothing");
90
- console.error(" about your stored credentials, OS keychain entries, or config dirs is");
91
- console.error(" removed by the uninstall below.");
92
- console.error("");
93
- console.error(" To unblock, in this order:");
94
- console.error("");
95
- console.error(" npm uninstall -g @vibecodr/vc-tools");
96
- console.error(" npm install -g @vibecodr/cli");
97
- console.error("");
98
- console.error(" After install, all three bin names work and resolve to the same");
99
- console.error(" dispatcher: vibecodr, vibecodr-mcp, vc-tools.");
100
- console.error("");
101
- console.error(" Advanced opt-out (not recommended): set VIBECDR_SKIP_PREINSTALL_CHECK=1");
102
- console.error(" and retry. The collision still trips an EEXIST inside npm; the env var");
103
- console.error(" only silences this preflight check.");
104
- console.error("");
105
- console.error("==========================================================================");
106
- console.error("");
107
- process.exit(1);
112
+ function vibecodrCliInstalledAndValid() {
113
+ const root = npmGlobalRoot();
114
+ if (!root) return true; // can't determine -> don't block
115
+ const pkgPath = path.join(root, "@vibecodr", "cli", "package.json");
116
+ if (!existsSync(pkgPath)) return false;
117
+ try {
118
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
119
+ return pkg?.name === "@vibecodr/cli";
120
+ } catch {
121
+ return false;
122
+ }
123
+ }
124
+
125
+ const SHIM_NAMES_WIN = ["vc-tools", "vc-tools.cmd", "vc-tools.ps1", "vibecodr", "vibecodr.cmd", "vibecodr.ps1", "vibecodr-mcp", "vibecodr-mcp.cmd", "vibecodr-mcp.ps1"];
126
+ const SHIM_NAMES_POSIX = ["vc-tools", "vibecodr", "vibecodr-mcp"];
127
+
128
+ function detectOrphanShims() {
129
+ const dir = npmGlobalBinDir();
130
+ if (!dir) return [];
131
+ const candidates = process.platform === "win32" ? SHIM_NAMES_WIN : SHIM_NAMES_POSIX;
132
+ return candidates.filter((name) => existsSync(path.join(dir, name)));
133
+ }
134
+
135
+ const presentShims = detectOrphanShims();
136
+ if (presentShims.length > 0 && !vibecodrCliInstalledAndValid()) {
137
+ // Shim files exist at the global bin dir but no @vibecodr/cli package
138
+ // exists in global node_modules to own them. They're either from an
139
+ // earlier aborted install of @vibecodr/cli or a manually-deleted
140
+ // package that didn't clean its shims. Either way, the retry will
141
+ // EEXIST unless the user removes them first.
142
+ printOrphanShimMessage(npmGlobalBinDir(), npmGlobalRoot(), presentShims);
143
+ process.exit(1);
144
+ }
145
+
146
+ // No conflict detected; let npm proceed.
147
+ process.exit(0);
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Message helpers (separated so the test file can exercise the gate logic
151
+ // without diffing the multi-line console output)
152
+ // ---------------------------------------------------------------------------
153
+
154
+ function printLegacyConflictMessage(version) {
155
+ console.error("");
156
+ console.error("==========================================================================");
157
+ console.error(" @vibecodr/cli install blocked: legacy @vibecodr/vc-tools is in the way");
158
+ console.error("==========================================================================");
159
+ console.error("");
160
+ console.error(` You have @vibecodr/vc-tools@${version} installed globally. That`);
161
+ console.error(" package and @vibecodr/cli@1.x both claim the `vc-tools` bin name on");
162
+ console.error(" disk, and npm refuses to overwrite a bin owned by a different package");
163
+ console.error(" (you would otherwise see a cryptic EEXIST pointing at the global bin");
164
+ console.error(" dir).");
165
+ console.error("");
166
+ console.error(" The merged @vibecodr/cli replaces the legacy @vibecodr/vc-tools. The");
167
+ console.error(" legacy line is preserved on npm under explicit version pins; nothing");
168
+ console.error(" about your stored credentials, OS keychain entries, or config dirs is");
169
+ console.error(" removed by the uninstall below.");
170
+ console.error("");
171
+ console.error(" To unblock, in this order:");
172
+ console.error("");
173
+ console.error(" npm uninstall -g @vibecodr/vc-tools");
174
+ console.error(" npm install -g @vibecodr/cli");
175
+ console.error("");
176
+ console.error(" After install, all three bin names work and resolve to the same");
177
+ console.error(" dispatcher: vibecodr, vibecodr-mcp, vc-tools.");
178
+ console.error("");
179
+ console.error(" Advanced opt-out (not recommended): set VIBECDR_SKIP_PREINSTALL_CHECK=1");
180
+ console.error(" and retry. The collision still trips an EEXIST inside npm; the env var");
181
+ console.error(" only silences this preflight check.");
182
+ console.error("");
183
+ console.error("==========================================================================");
184
+ console.error("");
185
+ }
186
+
187
+ function printOrphanShimMessage(binDir, root, shims) {
188
+ console.error("");
189
+ console.error("==========================================================================");
190
+ console.error(" @vibecodr/cli install blocked: orphan bin shims from a prior install");
191
+ console.error("==========================================================================");
192
+ console.error("");
193
+ console.error(" These bin shim files exist in your global npm bin directory but no");
194
+ console.error(" @vibecodr/cli package is currently installed to own them:");
195
+ console.error("");
196
+ for (const name of shims) {
197
+ console.error(` ${path.join(binDir, name)}`);
198
+ }
199
+ console.error("");
200
+ console.error(" They are typically left behind when an earlier global install of");
201
+ console.error(" @vibecodr/cli or @vibecodr/vc-tools was aborted mid-flight (Windows");
202
+ console.error(" file-handle locks during npm cleanup are the common cause). npm");
203
+ console.error(" refuses to overwrite shim files it didn't create in the current");
204
+ console.error(" install run, so the retry trips the same EEXIST every time.");
205
+ console.error("");
206
+ console.error(" To unblock on Windows (PowerShell):");
207
+ console.error("");
208
+ for (const name of shims) {
209
+ console.error(` Remove-Item "${path.join(binDir, name)}" -Force`);
210
+ }
211
+ if (root) {
212
+ const halfInstall = path.join(root, "@vibecodr", "cli");
213
+ if (existsSync(halfInstall)) {
214
+ console.error(` Remove-Item "${halfInstall}" -Recurse -Force`);
215
+ }
216
+ }
217
+ console.error(" npm install -g @vibecodr/cli");
218
+ console.error("");
219
+ console.error(" On macOS / Linux (bash):");
220
+ console.error("");
221
+ for (const name of shims) {
222
+ console.error(` rm -f "${path.join(binDir, name)}"`);
223
+ }
224
+ if (root) {
225
+ const halfInstall = path.join(root, "@vibecodr", "cli");
226
+ if (existsSync(halfInstall)) {
227
+ console.error(` rm -rf "${halfInstall}"`);
228
+ }
229
+ }
230
+ console.error(" npm install -g @vibecodr/cli");
231
+ console.error("");
232
+ console.error(" If the rm or Remove-Item fails with EPERM/locked-file errors, close");
233
+ console.error(" any IDE / terminal that has run vibecodr, vibecodr-mcp, or vc-tools");
234
+ console.error(" recently (the OS may still hold handles on the shim files), then");
235
+ console.error(" retry.");
236
+ console.error("");
237
+ console.error(" Advanced opt-out (not recommended): set VIBECDR_SKIP_PREINSTALL_CHECK=1");
238
+ console.error(" and retry. The shim collision still trips an EEXIST inside npm; the");
239
+ console.error(" env var only silences this preflight check.");
240
+ console.error("");
241
+ console.error("==========================================================================");
242
+ console.error("");
243
+ }