@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.
- package/CHANGELOG.md +101 -0
- package/README.md +90 -19
- package/dist/changelog.d.ts.map +1 -1
- package/dist/changelog.js +8 -1
- package/dist/changelog.js.map +1 -1
- package/dist/daemon-lock.d.ts +102 -0
- package/dist/daemon-lock.d.ts.map +1 -0
- package/dist/daemon-lock.js +321 -0
- package/dist/daemon-lock.js.map +1 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +13 -5
- package/dist/doctor.js.map +1 -1
- package/dist/exec.d.ts +49 -0
- package/dist/exec.d.ts.map +1 -0
- package/dist/exec.js +58 -0
- package/dist/exec.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +154 -35
- package/dist/index.js.map +1 -1
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +38 -5
- package/dist/login.js.map +1 -1
- package/dist/rename.d.ts +18 -0
- package/dist/rename.d.ts.map +1 -0
- package/dist/rename.js +252 -0
- package/dist/rename.js.map +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +4 -8
- package/dist/setup.js.map +1 -1
- package/dist/snapshot.js +24 -4
- package/dist/snapshot.js.map +1 -1
- package/dist/theme.d.ts +5 -3
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +7 -5
- package/dist/theme.js.map +1 -1
- package/package.json +3 -3
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/README.md +10 -2
- package/plugin/commands/ask.md +65 -0
- package/plugin/commands/fork.md +1 -1
- package/plugin/commands/team.md +37 -64
- package/plugin/commands/tell.md +66 -0
- package/plugin/scripts/announce-roster.mjs +5 -1
- package/plugin/scripts/emit-event.mjs +17 -0
- package/plugin/scripts/ensure-daemon.mjs +41 -1
- package/plugin/scripts/roster.mjs +5 -0
- 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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"}
|
package/dist/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"
|
|
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
|
|
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 {
|
package/dist/doctor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAe,MAAM,wBAAwB,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE7C,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,6HAA6H;AAC7H,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAU,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM;IAC1B,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,IAAa,EAAE,EAAE;QAC1C,GAAG,CAAC,GAAG,CAAC,CAAC;QACT,IAAI,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,QAAQ,IAAI,CAAC,CAAC;IAChB,CAAC,CAAC;IAEF,iBAAiB;IACjB,MAAM,OAAO,GAAG,MAAM,aAAa,EAAE,CAAC;IACtC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,IAAI,CAAC,8BAA8B,EAAE,iDAAiD,CAAC,CAAC;IAC1F,CAAC;SAAM,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,kBAAkB,CAAC,EAAE,CAAC;QACxD,IAAI,CACF,UAAU,OAAO,qBAAqB,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EACtE,oFAAoF,CACrF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;IAC1B,CAAC;IAED,kBAAkB;IAClB,MAAM,cAAc,GAAG,oBAAoB,EAAE,CAAC;IAC9C,IAAI,cAAc;QAAE,EAAE,CAAC,sBAAsB,CAAC,CAAC;SAC1C,CAAC;QACJ,IAAI,CAAC,0CAA0C,CAAC,CAAC;QACjD,GAAG,CAAC,wDAAwD,CAAC,CAAC;QAC9D,GAAG,CAAC,yDAAyD,CAAC,CAAC;IACjE,CAAC;IAED,iBAAiB;IACjB,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,CAAC,+BAA+B,EAAE,sBAAsB,CAAC,CAAC;IAChE,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,sBAAsB,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC;IAED,YAAY;IACZ,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,CAAC,CAAC;IAChE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QAC/B,IAAI,CAAC,uBAAuB,EAAE,mDAAmD,aAAa,EAAE,EAAE,CAAC,CAAC;IACtG,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,oBAAoB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,IAAI,CACF,yCAAyC,EACzC,8EAA8E,CAC/E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,KAAK,CAAC,OAAO,qBAAqB,EAAE;gBAC7D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,+EAA+E;gBAC/E,8EAA8E;gBAC9E,4EAA4E;gBAC5E,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;aAC3D,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,EAAE,CAAC,sBAAsB,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;YAC7C,CAAC;iBAAM,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC9B,IAAI,CAAC,mCAAmC,EAAE,mCAAmC,CAAC,CAAC;YACjF,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,oBAAoB,GAAG,CAAC,MAAM,EAAE,EAAE,0CAA0C,CAAC,CAAC;YACrF,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CACF,gBAAgB,KAAK,CAAC,OAAO,EAAE,EAC/B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,qEAAqE;IACrE,IAAI,MAAM,UAAU,EAAE,EAAE,CAAC;QACvB,EAAE,CAAC,WAAW,CAAC,CAAC;IAClB,CAAC;SAAM,CAAC;QACN,IAAI,CACF,uBAAuB,EACvB,qFAAqF;YACnF,0DAA0D,CAC7D,CAAC;IACJ,CAAC;IAED,qEAAqE;IACrE,IAAI,IAAI,EAAE,CAAC,KAAK,MAAM,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,SAAS,CAC5B,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,EAChE,KAAK,CACN,CAAC;QACF,IAAI,MAAM,EAAE,CAAC,KAAK,WAAW,EAAE,CAAC;YAC9B,kFAAkF;YAClF,0DAA0D;YAC1D,IAAI,MAAM,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM;gBAAE,EAAE,CAAC,qBAAqB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;iBAC3E,IAAI,MAAM,CAAC,KAAK,EAAE,IAAI,KAAK,KAAK,EAAE,CAAC;gBACtC,IAAI,CAAC,wCAAwC,CAAC,CAAC;gBAC/C,GAAG,CAAC,2EAA2E,CAAC,CAAC;YACnF,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;YAC9D,EAAE,CAAC,WAAW,MAAM,CAAC,OAAO,CAAC,MAAM,cAAc,MAAM,iBAAiB,CAAC,CAAC;YAC1E,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAChC,GAAG,CAAC,iEAAiE,CAAC,CAAC;YACzE,CAAC;YACD,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC9B,IAAI,CAAC,GAAG,MAAM,CAAC,kBAAkB,8CAA8C,CAAC,CAAC;gBACjF,GAAG,CAAC,mDAAmD,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,EAAE,CAAC,KAAK,OAAO,EAAE,CAAC;YACjC,IAAI,CAAC,kBAAkB,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,QAAQ,KAAK,CAAC;QAAE,EAAE,CAAC,2BAA2B,CAAC,CAAC;;QAC/C,GAAG,CAAC,GAAG,QAAQ,oBAAoB,CAAC,CAAC;IAC1C,OAAO,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,QAAQ,CAAC;IACvD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,
|
|
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
|
package/dist/exec.js.map
ADDED
|
@@ -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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAsCA;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,MAEb,CAAC"}
|