@popoverinstall/cli 0.7.1 → 0.8.0

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 (47) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +90 -19
  3. package/dist/changelog.d.ts.map +1 -1
  4. package/dist/changelog.js +8 -1
  5. package/dist/changelog.js.map +1 -1
  6. package/dist/daemon-lock.d.ts +102 -0
  7. package/dist/daemon-lock.d.ts.map +1 -0
  8. package/dist/daemon-lock.js +321 -0
  9. package/dist/daemon-lock.js.map +1 -0
  10. package/dist/doctor.d.ts.map +1 -1
  11. package/dist/doctor.js +13 -5
  12. package/dist/doctor.js.map +1 -1
  13. package/dist/exec.d.ts +49 -0
  14. package/dist/exec.d.ts.map +1 -0
  15. package/dist/exec.js +58 -0
  16. package/dist/exec.js.map +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +154 -35
  19. package/dist/index.js.map +1 -1
  20. package/dist/login.d.ts.map +1 -1
  21. package/dist/login.js +38 -5
  22. package/dist/login.js.map +1 -1
  23. package/dist/rename.d.ts +18 -0
  24. package/dist/rename.d.ts.map +1 -0
  25. package/dist/rename.js +252 -0
  26. package/dist/rename.js.map +1 -0
  27. package/dist/setup.d.ts.map +1 -1
  28. package/dist/setup.js +4 -8
  29. package/dist/setup.js.map +1 -1
  30. package/dist/snapshot.js +24 -4
  31. package/dist/snapshot.js.map +1 -1
  32. package/dist/theme.d.ts +5 -3
  33. package/dist/theme.d.ts.map +1 -1
  34. package/dist/theme.js +7 -5
  35. package/dist/theme.js.map +1 -1
  36. package/package.json +3 -3
  37. package/plugin/.claude-plugin/plugin.json +1 -1
  38. package/plugin/README.md +10 -2
  39. package/plugin/commands/ask.md +65 -0
  40. package/plugin/commands/fork.md +1 -1
  41. package/plugin/commands/team.md +37 -64
  42. package/plugin/commands/tell.md +66 -0
  43. package/plugin/scripts/announce-roster.mjs +5 -1
  44. package/plugin/scripts/emit-event.mjs +17 -0
  45. package/plugin/scripts/ensure-daemon.mjs +41 -1
  46. package/plugin/scripts/roster.mjs +5 -0
  47. package/plugin/skills/popover/SKILL.md +23 -8
@@ -0,0 +1,321 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { daemonLockPath, popoverHome } from "@popoverinstall/shared";
5
+ /**
6
+ * Reading the daemon's lock file, and refusing to act on it when it is stale.
7
+ *
8
+ * The lock is advisory. A daemon is running if and only if it answers on the pipe; the file
9
+ * exists so that a daemon which has stopped answering can still be signalled. That makes
10
+ * every use of it an act of trust in a number written by a process that may be long gone,
11
+ * and this module exists to make that trust conditional.
12
+ *
13
+ * Two ways it used to be misplaced, both reproduced:
14
+ *
15
+ * 1. **A recycled pid.** A daemon killed without its graceful path — `taskkill /F`, Task
16
+ * Manager, a power cut — leaves the file behind, and Windows reissues pids aggressively.
17
+ * `popover daemon stop` then sent SIGTERM to whatever now holds that number, and
18
+ * `popover update` runs the same path, so this was not a rare route into someone else's
19
+ * process.
20
+ * 2. **A zero-byte lock.** A crash mid-write, or a full disk, leaves an empty file.
21
+ * `Number("")` is `0` and `Number.isFinite(0)` is `true`, so the pid check passed and
22
+ * `process.kill(0, "SIGTERM")` went out — which on macOS and Linux signals the *caller's
23
+ * entire process group*, killing the user's shell.
24
+ *
25
+ * So: the pid must be a positive integer, and the process wearing it must still look like the
26
+ * one that wrote the file — same start time, running the same script. When that cannot be
27
+ * established, nothing is signalled. Refusing to act is always recoverable; signalling the
28
+ * wrong process is not.
29
+ */
30
+ /** How far the OS-reported start time may differ from the recorded one. */
31
+ const START_TIME_TOLERANCE_MS = 5_000;
32
+ /**
33
+ * Parse the lock file, tolerating the format that predates it.
34
+ *
35
+ * A daemon installed before this change is still a daemon somebody may need to stop, and its
36
+ * lock holds a bare pid. It is accepted, flagged `legacy`, and verified against a weaker
37
+ * signal — see `verifyDaemonLock`.
38
+ */
39
+ export function readDaemonLock(file = daemonLockPath()) {
40
+ let raw;
41
+ try {
42
+ raw = readFileSync(file, "utf8").trim();
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ // Includes the zero-byte case, which is the whole reason this returns null rather than
48
+ // letting `Number("")` become a pid of 0.
49
+ if (!raw)
50
+ return null;
51
+ if (raw.startsWith("{")) {
52
+ try {
53
+ const parsed = JSON.parse(raw);
54
+ const pid = Number(parsed.pid);
55
+ if (!Number.isInteger(pid))
56
+ return null;
57
+ return {
58
+ pid,
59
+ ...(Number.isFinite(Number(parsed.startedAt)) && Number(parsed.startedAt) > 0
60
+ ? { startedAt: Number(parsed.startedAt) }
61
+ : {}),
62
+ ...(parsed.entry ? { entry: String(parsed.entry) } : {}),
63
+ legacy: false,
64
+ };
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ const pid = Number(raw);
71
+ if (!Number.isInteger(pid))
72
+ return null;
73
+ return { pid, legacy: true };
74
+ }
75
+ /**
76
+ * Whether the process named by the lock is still the daemon that wrote it.
77
+ *
78
+ * `expectedEntry` is the daemon build this CLI would start, used as the identity test for a
79
+ * legacy lock that records no entry of its own. It is a weaker test — it says "this pid is
80
+ * running the daemon" rather than "this pid is the one that wrote the file" — but combined
81
+ * with the pid still existing it is enough to rule out an unrelated process, which is the
82
+ * failure that matters.
83
+ */
84
+ export function verifyDaemonLock(lock, opts = {}) {
85
+ // `pid > 0` rather than `Number.isFinite`: 0 is a real, finite number and a catastrophic
86
+ // signal target, and a negative pid signals a process group on POSIX.
87
+ if (!Number.isInteger(lock.pid) || lock.pid <= 0) {
88
+ return { ok: false, reason: "bad-pid", detail: `the lock names pid ${lock.pid}` };
89
+ }
90
+ const probe = opts.probe ?? inspectProcess;
91
+ const info = probe(lock.pid);
92
+ if (info.state === "gone") {
93
+ return { ok: false, reason: "gone", detail: `no process is running as pid ${lock.pid}` };
94
+ }
95
+ if (info.state === "unknown") {
96
+ return {
97
+ ok: false,
98
+ reason: "unknown",
99
+ detail: `this machine would not say what pid ${lock.pid} is`,
100
+ };
101
+ }
102
+ // The start time is the strongest available evidence against a recycled pid: a reissued
103
+ // number belongs to a process that started later, by definition.
104
+ if (lock.startedAt !== undefined) {
105
+ if (info.startedAt === undefined) {
106
+ return {
107
+ ok: false,
108
+ reason: "unknown",
109
+ detail: `could not read the start time of pid ${lock.pid}`,
110
+ };
111
+ }
112
+ if (Math.abs(info.startedAt - lock.startedAt) > START_TIME_TOLERANCE_MS) {
113
+ return {
114
+ ok: false,
115
+ reason: "not-ours",
116
+ detail: `pid ${lock.pid} started at a different time than the daemon that wrote the lock`,
117
+ };
118
+ }
119
+ }
120
+ const wanted = lock.entry ?? opts.expectedEntry ?? null;
121
+ if (wanted) {
122
+ if (info.commandLine === undefined) {
123
+ return {
124
+ ok: false,
125
+ reason: "unknown",
126
+ detail: `could not read the command line of pid ${lock.pid}`,
127
+ };
128
+ }
129
+ if (!commandLineNames(info.commandLine, wanted)) {
130
+ return {
131
+ ok: false,
132
+ reason: "not-ours",
133
+ detail: `pid ${lock.pid} is not running the popover daemon`,
134
+ };
135
+ }
136
+ }
137
+ else if (lock.legacy) {
138
+ // Nothing recorded and nothing to compare against. A legacy lock plus no known daemon
139
+ // build is exactly the situation this module exists to refuse.
140
+ return {
141
+ ok: false,
142
+ reason: "unknown",
143
+ detail: "the lock predates this version and there is no daemon build to check it against",
144
+ };
145
+ }
146
+ return { ok: true, pid: lock.pid };
147
+ }
148
+ /**
149
+ * Whether a command line is running a given script.
150
+ *
151
+ * Compared on the path as text rather than by resolving it: the daemon recorded
152
+ * `process.argv[1]` and the OS reports the same string back, so an exact substring match is
153
+ * both sufficient and free of filesystem calls that could themselves fail. Windows gets a
154
+ * case-insensitive comparison with separators normalised, because a path can come back with
155
+ * either slash and either case and still be the same file.
156
+ */
157
+ function commandLineNames(commandLine, entry) {
158
+ if (process.platform !== "win32")
159
+ return commandLine.includes(entry);
160
+ const norm = (s) => s.replace(/\\/g, "/").toLowerCase();
161
+ return norm(commandLine).includes(norm(entry));
162
+ }
163
+ /**
164
+ * Ask the OS what a pid is, without ever throwing.
165
+ *
166
+ * Deliberately not `process.kill(pid, 0)`, which answers only "does something hold this
167
+ * number" — the exact question that led here. What is needed is identity, and identity means
168
+ * the start time and the command line, neither of which Node exposes for another process.
169
+ *
170
+ * Costs one subprocess. That is fine: this runs on the fallback path of `popover daemon
171
+ * stop`, after the pipe has already failed, and never on any hot path.
172
+ */
173
+ export function inspectProcess(pid) {
174
+ try {
175
+ return process.platform === "win32" ? inspectWindows(pid) : inspectPosix(pid);
176
+ }
177
+ catch {
178
+ // A probe that could not run tells us nothing, and "nothing" must not read as "gone" —
179
+ // that would send us on to clean the lock of a daemon that is alive and well.
180
+ return { state: "unknown" };
181
+ }
182
+ }
183
+ function inspectPosix(pid) {
184
+ let out;
185
+ try {
186
+ out = execFileSync("ps", ["-o", "lstart=,args=", "-p", String(pid)], {
187
+ encoding: "utf8",
188
+ timeout: 8_000,
189
+ stdio: ["ignore", "pipe", "ignore"],
190
+ });
191
+ }
192
+ catch (err) {
193
+ // `ps` exits non-zero with no output when the pid does not exist, which is an answer.
194
+ // Anything else — no `ps`, a timeout — is not.
195
+ const status = err.status;
196
+ if (status === 1)
197
+ return { state: "gone" };
198
+ return { state: "unknown" };
199
+ }
200
+ const line = out.split("\n").find((l) => l.trim().length > 0);
201
+ if (!line)
202
+ return { state: "gone" };
203
+ // `lstart` is `%a %b %e %H:%M:%S %Y`, space-padded to a fixed width, so the date and the
204
+ // command are separable without knowing anything about the command.
205
+ const m = /^\s*(\w{3}\s+\w{3}\s+[\d ]?\d\s+\d{2}:\d{2}:\d{2}\s+\d{4})\s+(.*)$/.exec(line);
206
+ if (!m)
207
+ return { state: "running", commandLine: line.trim() };
208
+ const at = Date.parse(m[1].replace(/\s+/g, " "));
209
+ return {
210
+ state: "running",
211
+ ...(Number.isFinite(at) ? { startedAt: at } : {}),
212
+ commandLine: m[2].trim(),
213
+ };
214
+ }
215
+ function inspectWindows(pid) {
216
+ // One CIM query, because `Get-Process` reports a start time but not a command line and the
217
+ // command line is what identifies the script. Emitted as JSON so nothing has to be parsed
218
+ // out of a localised table.
219
+ const script = `$ErrorActionPreference='Stop';` +
220
+ `$p = Get-CimInstance Win32_Process -Filter "ProcessId=${pid}";` +
221
+ `if ($null -eq $p) { exit 3 };` +
222
+ `$o = [ordered]@{ ` +
223
+ `start = [int64]([DateTimeOffset]$p.CreationDate).ToUnixTimeMilliseconds(); ` +
224
+ `cmd = [string]$p.CommandLine };` +
225
+ `[Console]::Out.Write(($o | ConvertTo-Json -Compress))`;
226
+ let out;
227
+ try {
228
+ out = execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script], { encoding: "utf8", timeout: 8_000, stdio: ["ignore", "pipe", "ignore"], windowsHide: true });
229
+ }
230
+ catch (err) {
231
+ // Exit 3 is our own "no such process"; every other failure is the probe, not the answer.
232
+ if (err.status === 3)
233
+ return { state: "gone" };
234
+ return { state: "unknown" };
235
+ }
236
+ try {
237
+ const parsed = JSON.parse(out.trim());
238
+ const at = Number(parsed.start);
239
+ return {
240
+ state: "running",
241
+ ...(Number.isFinite(at) && at > 0 ? { startedAt: at } : {}),
242
+ ...(parsed.cmd ? { commandLine: String(parsed.cmd) } : {}),
243
+ };
244
+ }
245
+ catch {
246
+ return { state: "unknown" };
247
+ }
248
+ }
249
+ /** Remove a lock that has been shown to name nothing of ours. Never throws. */
250
+ export function clearDaemonLock(file = daemonLockPath()) {
251
+ try {
252
+ rmSync(file, { force: true });
253
+ }
254
+ catch {
255
+ /* the next start overwrites it anyway */
256
+ }
257
+ }
258
+ // ---------------------------------------------------------------------------
259
+ // The update interlock
260
+ // ---------------------------------------------------------------------------
261
+ /**
262
+ * Marker saying "an upgrade is replacing the install tree; do not start a daemon".
263
+ *
264
+ * `popover update` stops the daemon and then hands npm up to five minutes to replace the
265
+ * global package. The SessionStart hook runs in *every* Claude Code session, pings, finds
266
+ * nothing, and starts `node <entry>` pointing into the tree npm is halfway through replacing.
267
+ * On Windows the daemon it starts then holds those files open, npm's rename fails EPERM or
268
+ * EBUSY, and the update aborts with a half-replaced global install — a machine with no
269
+ * working `popover` at all.
270
+ *
271
+ * The deadline is what keeps this from being worse than the problem. A marker with no expiry
272
+ * that survives a crashed update would suppress the daemon forever, on a machine whose owner
273
+ * has no idea this file exists.
274
+ */
275
+ export function updateLockPath() {
276
+ return path.join(popoverHome(), "update.lock");
277
+ }
278
+ export function beginUpdateLock(ttlMs) {
279
+ try {
280
+ mkdirSync(popoverHome(), { recursive: true });
281
+ writeFileSync(updateLockPath(), `${JSON.stringify({ v: 1, pid: process.pid, deadline: Date.now() + ttlMs })}\n`, "utf8");
282
+ }
283
+ catch {
284
+ // Best effort. Failing to write it makes the race possible again, which is where we
285
+ // started; failing the update over it would be worse.
286
+ }
287
+ }
288
+ export function clearUpdateLock() {
289
+ try {
290
+ rmSync(updateLockPath(), { force: true });
291
+ }
292
+ catch {
293
+ /* a stale marker expires on its own */
294
+ }
295
+ }
296
+ /**
297
+ * Whether an upgrade is in flight right now.
298
+ *
299
+ * Every branch fails towards starting the daemon. Only a marker that is present, readable and
300
+ * unexpired holds anything back; a torn or truncated one means the process that wrote it was
301
+ * interrupted, which is a dead update rather than a running one. Reading it the other way
302
+ * round would leave a machine permanently without a daemon over a file its owner has never
303
+ * heard of — worse than the race this guards against, and far harder to diagnose.
304
+ *
305
+ * `packages/plugin/scripts/ensure-daemon.mjs` duplicates this check, down to that choice. It
306
+ * must stay dependency-free — a hook that fails to resolve an import breaks every session on
307
+ * the machine — so it cannot import this. Change both together.
308
+ */
309
+ export function updateInProgress() {
310
+ try {
311
+ const file = updateLockPath();
312
+ if (!existsSync(file))
313
+ return false;
314
+ const deadline = Number(JSON.parse(readFileSync(file, "utf8")).deadline);
315
+ return Number.isFinite(deadline) && Date.now() < deadline;
316
+ }
317
+ catch {
318
+ return false;
319
+ }
320
+ }
321
+ //# sourceMappingURL=daemon-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-lock.js","sourceRoot":"","sources":["../src/daemon-lock.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAUA,6HAA6H;AAC7H,eAAO,MAAM,kBAAkB,sBAAuB,CAAC;AAEvD;;;;;GAKG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CA0H9C;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAY5D;AAcD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAS/F"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAWA,6HAA6H;AAC7H,eAAO,MAAM,kBAAkB,sBAAuB,CAAC;AAEvD;;;;;GAKG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAuI9C;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ5D;AAcD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAS/F"}
package/dist/doctor.js CHANGED
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  import { claudeHome, daemonAddress, loadCredentials } from "@popoverinstall/shared";
6
+ import { execTool } from "./exec.js";
6
7
  import { askDaemon } from "./ipc-client.js";
7
8
  import { bad, dim, ok, warn } from "./ui.js";
8
9
  const execFileAsync = promisify(execFile);
@@ -60,6 +61,17 @@ export async function doctor() {
60
61
  if (!pong.authenticated) {
61
62
  fail("daemon is running but not authenticated", "Its token exchange failed. Check the backend, then `popover daemon restart`.");
62
63
  }
64
+ // A blocked client is a failure, not a suggestion: the backend is refusing its writes,
65
+ // so this machine's agents are invisible to the team no matter how healthy everything
66
+ // else here looks. Reported before the roster check below, which would otherwise show
67
+ // an empty team and read as "nobody is around".
68
+ if (pong.update?.blocked) {
69
+ fail("this build is too old to publish to your team", `The backend refuses its writes, so your agents are invisible to teammates. Run \`${pong.update.command}\`.`);
70
+ }
71
+ else if (pong.update) {
72
+ warn(`a newer release is available (${pong.update.current} → ${pong.update.latest})`);
73
+ dim(`Run \`${pong.update.command}\`.`);
74
+ }
63
75
  }
64
76
  // 5. Backend round trip — the check that actually exercises the JWT path.
65
77
  if (creds) {
@@ -130,11 +142,7 @@ export async function doctor() {
130
142
  export async function claudeVersion() {
131
143
  const bin = process.env.POPOVER_CLAUDE_BIN ?? "claude";
132
144
  try {
133
- const { stdout } = await execFileAsync(bin, ["--version"], {
134
- timeout: 10_000,
135
- windowsHide: true,
136
- shell: process.platform === "win32",
137
- });
145
+ const { stdout } = await execTool(bin, ["--version"], { timeout: 10_000 });
138
146
  return stdout.trim().split(/\s+/)[0] ?? null;
139
147
  }
140
148
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
package/dist/exec.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Running another CLI, on a platform where that is not a one-liner.
3
+ *
4
+ * Every tool this package shells out to — `npm`, `claude` — ships on Windows as a `.cmd`
5
+ * shim rather than an executable, and that costs two things:
6
+ *
7
+ * 1. **Node will not spawn a `.cmd` directly.** Bare `npm` fails `ENOENT` (CreateProcess
8
+ * appends `.exe`, never `.cmd`), and a full path to one fails `EINVAL` — Node has
9
+ * refused to run batch files without an explicit shell since CVE-2024-27980. So the
10
+ * call has to go through `cmd.exe`.
11
+ * 2. **`cmd.exe` re-parses the arguments.** `child_process` hands the shell one flat
12
+ * string, so an argument containing a space arrives as two. That is not theoretical:
13
+ * `popover setup` registers the plugin by path, and on Windows the path it passes runs
14
+ * through `C:\Users\<name>\AppData\...` — one space in a username and Claude Code is
15
+ * told to add a marketplace that does not exist.
16
+ *
17
+ * Hence quoting, which only happens on the path where a shell is involved. POSIX keeps the
18
+ * direct spawn and needs none of this.
19
+ */
20
+ type ExecOptions = {
21
+ cwd?: string;
22
+ timeout?: number;
23
+ maxBuffer?: number;
24
+ windowsHide?: boolean;
25
+ };
26
+ /**
27
+ * Spawn a CLI that may be a Windows shim, quoting arguments if a shell is in the way.
28
+ *
29
+ * Rejects the way `execFile` does — with the error carrying `stdout`/`stderr` — so callers
30
+ * can keep reading npm's own diagnosis out of a failure.
31
+ */
32
+ export declare function execTool(file: string, args: string[], options?: ExecOptions): Promise<{
33
+ stdout: string;
34
+ stderr: string;
35
+ }>;
36
+ /**
37
+ * One argument, safe to hand to `cmd.exe`.
38
+ *
39
+ * Quoted only when it has to be, because quoting everything makes the command echoed in an
40
+ * error message harder to read than the command the user would have typed.
41
+ *
42
+ * `%` is the one character this cannot defend against: `cmd.exe` expands `%FOO%` inside
43
+ * double quotes and offers no escape for it on a command line. A path containing a `%` that
44
+ * also names a real environment variable would still break — which beats the previous
45
+ * behaviour of any space breaking, and is as far as a shell-based spawn can go.
46
+ */
47
+ export declare function quoteForCmd(arg: string): string;
48
+ export {};
49
+ //# sourceMappingURL=exec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAOH,KAAK,WAAW,GAAG;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAS7C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK/C"}
package/dist/exec.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Running another CLI, on a platform where that is not a one-liner.
3
+ *
4
+ * Every tool this package shells out to — `npm`, `claude` — ships on Windows as a `.cmd`
5
+ * shim rather than an executable, and that costs two things:
6
+ *
7
+ * 1. **Node will not spawn a `.cmd` directly.** Bare `npm` fails `ENOENT` (CreateProcess
8
+ * appends `.exe`, never `.cmd`), and a full path to one fails `EINVAL` — Node has
9
+ * refused to run batch files without an explicit shell since CVE-2024-27980. So the
10
+ * call has to go through `cmd.exe`.
11
+ * 2. **`cmd.exe` re-parses the arguments.** `child_process` hands the shell one flat
12
+ * string, so an argument containing a space arrives as two. That is not theoretical:
13
+ * `popover setup` registers the plugin by path, and on Windows the path it passes runs
14
+ * through `C:\Users\<name>\AppData\...` — one space in a username and Claude Code is
15
+ * told to add a marketplace that does not exist.
16
+ *
17
+ * Hence quoting, which only happens on the path where a shell is involved. POSIX keeps the
18
+ * direct spawn and needs none of this.
19
+ */
20
+ import { execFile } from "node:child_process";
21
+ import { promisify } from "node:util";
22
+ const execFileAsync = promisify(execFile);
23
+ /**
24
+ * Spawn a CLI that may be a Windows shim, quoting arguments if a shell is in the way.
25
+ *
26
+ * Rejects the way `execFile` does — with the error carrying `stdout`/`stderr` — so callers
27
+ * can keep reading npm's own diagnosis out of a failure.
28
+ */
29
+ export function execTool(file, args, options = {}) {
30
+ if (process.platform !== "win32") {
31
+ return execFileAsync(file, args, { windowsHide: true, ...options });
32
+ }
33
+ return execFileAsync(quoteForCmd(file), args.map(quoteForCmd), {
34
+ windowsHide: true,
35
+ ...options,
36
+ shell: true,
37
+ });
38
+ }
39
+ /**
40
+ * One argument, safe to hand to `cmd.exe`.
41
+ *
42
+ * Quoted only when it has to be, because quoting everything makes the command echoed in an
43
+ * error message harder to read than the command the user would have typed.
44
+ *
45
+ * `%` is the one character this cannot defend against: `cmd.exe` expands `%FOO%` inside
46
+ * double quotes and offers no escape for it on a command line. A path containing a `%` that
47
+ * also names a real environment variable would still break — which beats the previous
48
+ * behaviour of any space breaking, and is as far as a shell-based spawn can go.
49
+ */
50
+ export function quoteForCmd(arg) {
51
+ if (arg === "")
52
+ return '""';
53
+ if (!/[\s"&|<>^()!]/.test(arg))
54
+ return arg;
55
+ // cmd.exe strips the outer pair, so an embedded quote has to survive being seen twice.
56
+ return `"${arg.replace(/"/g, '\\"')}"`;
57
+ }
58
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAS1C;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CACtB,IAAY,EACZ,IAAc,EACd,UAAuB,EAAE;IAEzB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE;QAC7D,WAAW,EAAE,IAAI;QACjB,GAAG,OAAO;QACV,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC5B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC3C,uFAAuF;IACvF,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;AACzC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA+BA;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,MAEb,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAsCA;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,MAEb,CAAC"}