@vibecontrols/agent 2026.601.30
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/LICENSE +22 -0
- package/README.md +558 -0
- package/dist/agent-config-85pskv43.js +2 -0
- package/dist/agent-ready-tracker-zp6p8e6f.js +2 -0
- package/dist/app-yvjadjmh.js +2 -0
- package/dist/bootstrap-workspace-zpm20zez.js +2 -0
- package/dist/bootstrap.service-pjmnpxha.js +2 -0
- package/dist/bridge-client-341r9rry.js +2 -0
- package/dist/cli.js +5 -0
- package/dist/daemon-profile-vas1vf2t.js +2 -0
- package/dist/esm-9fpye77x.js +2 -0
- package/dist/finalize-retry-handle-registry-vv241fsq.js +2 -0
- package/dist/finalize-retry-worker-xp1nhv3c.js +2 -0
- package/dist/gateway-client-43gzvj5s.js +2 -0
- package/dist/getMachineId-bsd-a56s0v8c.js +2 -0
- package/dist/getMachineId-darwin-w9k0yw9r.js +3 -0
- package/dist/getMachineId-linux-anh31jbf.js +2 -0
- package/dist/getMachineId-unsupported-5hv3pwca.js +2 -0
- package/dist/getMachineId-win-njb8tery.js +2 -0
- package/dist/highlights-8d9mgr01.js +2 -0
- package/dist/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-jwvdxm9x.js +2 -0
- package/dist/highlights-qbx2vnme.js +2 -0
- package/dist/highlights-r3m83kn9.js +2 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-s7mqapt6.js +2 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/index-01qzsnwd.js +16 -0
- package/dist/index-0248afsn.js +3 -0
- package/dist/index-04n4qgvd.js +407 -0
- package/dist/index-0ckffygp.js +5 -0
- package/dist/index-0cn9bv8z.js +4 -0
- package/dist/index-1hnw0rhc.js +178 -0
- package/dist/index-1zw3kea7.js +10 -0
- package/dist/index-2gsarrbn.js +4 -0
- package/dist/index-2xs9cvjn.js +28 -0
- package/dist/index-3ys16efc.js +231 -0
- package/dist/index-4wgjx8bf.js +3 -0
- package/dist/index-52cp759f.js +3 -0
- package/dist/index-5mw3eshk.js +4 -0
- package/dist/index-678rwfc0.js +5 -0
- package/dist/index-6jq17k9s.js +7 -0
- package/dist/index-6jzsthh9.js +3 -0
- package/dist/index-6mprnf7p.js +9 -0
- package/dist/index-8kvc8ttn.js +15 -0
- package/dist/index-8sm0nkh8.js +3 -0
- package/dist/index-9bqd8veb.js +21 -0
- package/dist/index-b5dhmybd.js +4 -0
- package/dist/index-c58g96mb.js +26 -0
- package/dist/index-cs78wq6y.js +3 -0
- package/dist/index-d5ysy1yn.js +3 -0
- package/dist/index-dm6yjmgq.js +3 -0
- package/dist/index-e1bw1bwr.js +4 -0
- package/dist/index-ef95xr4z.js +9 -0
- package/dist/index-g2raeeh4.js +11 -0
- package/dist/index-g3ap3xpr.js +5 -0
- package/dist/index-g8zv1gta.js +17 -0
- package/dist/index-h8a8s8sn.js +3 -0
- package/dist/index-hrdamx5j.js +2 -0
- package/dist/index-jw1k4vbk.js +3 -0
- package/dist/index-mtm8cfyt.js +158 -0
- package/dist/index-mxc61yr1.js +3 -0
- package/dist/index-n7qyrdr1.js +3 -0
- package/dist/index-qfz9fy56.js +3 -0
- package/dist/index-rc79x8fw.js +3 -0
- package/dist/index-rdp5xq4r.js +15 -0
- package/dist/index-scsjyj4m.js +171 -0
- package/dist/index-ssjmzqcz.js +13 -0
- package/dist/index-tmrbs96r.js +11 -0
- package/dist/index-tp4y9jde.js +83 -0
- package/dist/index-v9fx5wab.js +83 -0
- package/dist/index-vdahdt49.js +2 -0
- package/dist/index-x1h8r7pr.js +3 -0
- package/dist/index-xjzmb1pn.js +3 -0
- package/dist/index-yrgm89r8.js +3 -0
- package/dist/index-yy1mm8zs.js +3 -0
- package/dist/index-z5a4yxzz.js +8 -0
- package/dist/index.js +5 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/injections-srewsjcz.js +2 -0
- package/dist/interactive-22ta89hc.js +2 -0
- package/dist/key.cmd-wgcq6kt8.js +2 -0
- package/dist/log-shipper-k24m8yw5.js +2 -0
- package/dist/path-utils-35re7qf9.js +2 -0
- package/dist/plugin-system-c916v9an.js +2 -0
- package/dist/postinstall-shim-fix.cjs +382 -0
- package/dist/prereqs-runner-ca4kt803.js +2 -0
- package/dist/preuninstall.cjs +254 -0
- package/dist/profile-mount-npcknw6v.js +2 -0
- package/dist/prune-stale-shims-nkx9vq5m.js +2 -0
- package/dist/register-core-qrawzyym.js +2 -0
- package/dist/secondary-profile-attach-db5cr3e1.js +2 -0
- package/dist/subprocess-g9sk1ep9.js +2 -0
- package/dist/telemetry-tnq47dcs.js +2 -0
- package/dist/tree-sitter-javascript-3h25c6bs.js +2 -0
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-3nemcjhe.js +2 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-16ftwa53.js +2 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-f6mq6ze6.js +2 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/dist/tree-sitter-zig-s2trkm2d.js +2 -0
- package/dist/tunnel-bootstrap-2kg79ng8.js +2 -0
- package/package.json +122 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* postinstall self-heal for @vibecontrols/agent.
|
|
4
|
+
*
|
|
5
|
+
* The `curl | bash` install path uses `bun add -g`, which symlinks
|
|
6
|
+
* ~/.bun/bin/vibe into bun's global node_modules. If that target is
|
|
7
|
+
* later wiped (manual rm, vibe nuke fallback, partial bun remove), the
|
|
8
|
+
* symlink dangles. Because ~/.bun/bin precedes nvm/npm bin in $PATH,
|
|
9
|
+
* `vibe` invocations hit ENOENT instead of falling through to a parallel
|
|
10
|
+
* `npm install -g` install.
|
|
11
|
+
*
|
|
12
|
+
* Worse: bash caches resolved binaries in its in-process hash table on
|
|
13
|
+
* first successful exec. Once cached, bash skips PATH lookup entirely
|
|
14
|
+
* for that name. So even after the dangling symlink is removed, every
|
|
15
|
+
* existing shell that previously ran `vibe` continues to try the dead
|
|
16
|
+
* path until the user runs `hash -r` or opens a new terminal.
|
|
17
|
+
*
|
|
18
|
+
* Strategy:
|
|
19
|
+
* - Walk ~/.bun/bin/ for symlinks whose readlink target points into
|
|
20
|
+
* ~/.bun/install/global/node_modules/@vibecontrols/.
|
|
21
|
+
* - If target is alive, leave it (a working bun-global install).
|
|
22
|
+
* - If target is dead AND the shim name matches one we ship (`vibe`),
|
|
23
|
+
* REBIND it to this package's dist/cli.js — that keeps cached bash
|
|
24
|
+
* hash entries pointed at the same path while making them work.
|
|
25
|
+
* - Other dead vibecontrols-scoped shims get unlinked.
|
|
26
|
+
*
|
|
27
|
+
* Scoped strictly to @vibecontrols/* targets — never touches unrelated
|
|
28
|
+
* tools' shims. Best-effort throughout: any error is swallowed so a
|
|
29
|
+
* cleanup hiccup never breaks `npm install -g`.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
"use strict";
|
|
33
|
+
|
|
34
|
+
const fs = require("fs");
|
|
35
|
+
const os = require("os");
|
|
36
|
+
const path = require("path");
|
|
37
|
+
const { spawnSync } = require("child_process");
|
|
38
|
+
|
|
39
|
+
const OWNED_BIN_NAMES = new Set(["vibe"]);
|
|
40
|
+
|
|
41
|
+
// Cross-package-manager hygiene: a host can have prior `vibe` shims sitting
|
|
42
|
+
// in a node-version-manager bin (nvm/fnm/volta), in the npm global bin, or
|
|
43
|
+
// in /usr/local/bin from an older install. When the user reinstalls via a
|
|
44
|
+
// different package manager (e.g. curl|bash → bun), those older shims may
|
|
45
|
+
// outrank the new one in PATH and/or be cached in the shell's hash table,
|
|
46
|
+
// causing `vibe` to ENOENT against a stale path. We scan a fixed set of
|
|
47
|
+
// well-known bin directories and prune any *dangling* `vibe` shim. Live
|
|
48
|
+
// shims (real installs of the package) are never touched.
|
|
49
|
+
function candidateExternalBins() {
|
|
50
|
+
const home = os.homedir();
|
|
51
|
+
const bins = new Set();
|
|
52
|
+
const isWin = process.platform === "win32";
|
|
53
|
+
if (isWin) {
|
|
54
|
+
if (process.env.APPDATA) bins.add(path.join(process.env.APPDATA, "npm"));
|
|
55
|
+
if (process.env.USERPROFILE) {
|
|
56
|
+
bins.add(path.join(process.env.USERPROFILE, "AppData", "Roaming", "npm"));
|
|
57
|
+
bins.add(
|
|
58
|
+
path.join(process.env.USERPROFILE, "AppData", "Local", "Volta", "bin"),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
if (process.env.ProgramFiles)
|
|
62
|
+
bins.add(path.join(process.env.ProgramFiles, "nodejs"));
|
|
63
|
+
} else {
|
|
64
|
+
bins.add(path.join(home, ".local", "bin"));
|
|
65
|
+
bins.add("/usr/local/bin");
|
|
66
|
+
bins.add("/opt/homebrew/bin");
|
|
67
|
+
bins.add(path.join(home, ".volta", "bin"));
|
|
68
|
+
// nvm + fnm — enumerate installed node versions.
|
|
69
|
+
const nvmRoot = path.join(home, ".nvm", "versions", "node");
|
|
70
|
+
try {
|
|
71
|
+
for (const v of fs.readdirSync(nvmRoot)) {
|
|
72
|
+
bins.add(path.join(nvmRoot, v, "bin"));
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
/* no nvm */
|
|
76
|
+
}
|
|
77
|
+
const fnmRoot = path.join(home, ".fnm", "node-versions");
|
|
78
|
+
try {
|
|
79
|
+
for (const v of fs.readdirSync(fnmRoot)) {
|
|
80
|
+
bins.add(path.join(fnmRoot, v, "installation", "bin"));
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
/* no fnm */
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return [...bins];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function pruneStaleExternalShims() {
|
|
90
|
+
const names =
|
|
91
|
+
process.platform === "win32" ? ["vibe", "vibe.cmd", "vibe.ps1"] : ["vibe"];
|
|
92
|
+
for (const dir of candidateExternalBins()) {
|
|
93
|
+
if (!fs.existsSync(dir)) continue;
|
|
94
|
+
for (const name of names) {
|
|
95
|
+
const p = path.join(dir, name);
|
|
96
|
+
let st;
|
|
97
|
+
try {
|
|
98
|
+
st = fs.lstatSync(p);
|
|
99
|
+
} catch {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// Unix: only prune symlinks (real files might be valid wrappers from
|
|
103
|
+
// another live install). Dangling symlinks are unambiguous garbage.
|
|
104
|
+
if (st.isSymbolicLink()) {
|
|
105
|
+
let target;
|
|
106
|
+
try {
|
|
107
|
+
target = fs.readlinkSync(p);
|
|
108
|
+
} catch {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const abs = path.isAbsolute(target)
|
|
112
|
+
? target
|
|
113
|
+
: path.resolve(dir, target);
|
|
114
|
+
try {
|
|
115
|
+
fs.statSync(abs);
|
|
116
|
+
continue; // live — leave it
|
|
117
|
+
} catch {
|
|
118
|
+
/* dangling */
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
fs.unlinkSync(p);
|
|
122
|
+
console.log(`[@vibecontrols/agent] pruned stale shim: ${p}`);
|
|
123
|
+
} catch {
|
|
124
|
+
/* best-effort */
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Peer deps every vibe-plugin-* package marks `--external` at build time and
|
|
132
|
+
// expects to resolve from the host. Without these symlinks at the npm-global
|
|
133
|
+
// root, plugins installed at `<npm-global>/@vibecontrols/<plugin>/` fail to
|
|
134
|
+
// resolve `import 'elysia'` (etc.) because Node's walk-up-the-tree search
|
|
135
|
+
// can't see the agent's nested copy at
|
|
136
|
+
// `<npm-global>/@vibecontrols/agent/node_modules/elysia/`.
|
|
137
|
+
//
|
|
138
|
+
// Ones we link MUST be:
|
|
139
|
+
// - present in the agent's own node_modules after a successful npm-global
|
|
140
|
+
// install (we don't fabricate them; if missing, we skip),
|
|
141
|
+
// - widely-shared peer deps that change rarely (so a single shared copy
|
|
142
|
+
// doesn't trigger version-skew issues across plugins),
|
|
143
|
+
// - listed as peerDependencies in actual plugin packages.
|
|
144
|
+
//
|
|
145
|
+
// Restricting to this allowlist (rather than mirroring every dep of the
|
|
146
|
+
// agent) keeps the symlink set small and predictable. Add to it only when
|
|
147
|
+
// a new plugin's --external flag exposes a new gap.
|
|
148
|
+
const SHARED_PEER_DEPS = ["elysia", "commander"];
|
|
149
|
+
|
|
150
|
+
function main() {
|
|
151
|
+
const home = os.homedir();
|
|
152
|
+
const bunBin = path.join(home, ".bun", "bin");
|
|
153
|
+
const vibeGlobalRoot = path.join(
|
|
154
|
+
home,
|
|
155
|
+
".bun",
|
|
156
|
+
"install",
|
|
157
|
+
"global",
|
|
158
|
+
"node_modules",
|
|
159
|
+
"@vibecontrols",
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
if (!fs.existsSync(bunBin)) return;
|
|
163
|
+
|
|
164
|
+
// process.cwd() during npm postinstall is the installed package root.
|
|
165
|
+
const agentRoot = process.cwd();
|
|
166
|
+
const ourCli = path.join(agentRoot, "dist", "cli.js");
|
|
167
|
+
const ourCliExists = fs.existsSync(ourCli);
|
|
168
|
+
|
|
169
|
+
// Step 0: expose the agent's nested peer deps at the npm-global root so
|
|
170
|
+
// sibling plugin packages can resolve their `import 'elysia'` etc.
|
|
171
|
+
// Walks two levels up from the agent's package dir to the root that
|
|
172
|
+
// contains all installed packages (npm-global) and symlinks the agent's
|
|
173
|
+
// own dependency copy. No-op if either source missing or target already
|
|
174
|
+
// present.
|
|
175
|
+
try {
|
|
176
|
+
const agentNodeModules = path.join(agentRoot, "node_modules");
|
|
177
|
+
const npmGlobalRoot = path.dirname(path.dirname(agentRoot));
|
|
178
|
+
if (
|
|
179
|
+
fs.existsSync(agentNodeModules) &&
|
|
180
|
+
fs.existsSync(npmGlobalRoot) &&
|
|
181
|
+
path.basename(path.dirname(agentRoot)) === "@vibecontrols"
|
|
182
|
+
) {
|
|
183
|
+
for (const dep of SHARED_PEER_DEPS) {
|
|
184
|
+
const src = path.join(agentNodeModules, dep);
|
|
185
|
+
const dst = path.join(npmGlobalRoot, dep);
|
|
186
|
+
if (!fs.existsSync(src)) continue;
|
|
187
|
+
let dstStat = null;
|
|
188
|
+
try {
|
|
189
|
+
dstStat = fs.lstatSync(dst);
|
|
190
|
+
} catch {
|
|
191
|
+
/* missing — will create */
|
|
192
|
+
}
|
|
193
|
+
if (dstStat) {
|
|
194
|
+
// Already present (real install or our prior symlink). If it's our
|
|
195
|
+
// own stale symlink to a now-missing src, replace; otherwise leave.
|
|
196
|
+
if (dstStat.isSymbolicLink()) {
|
|
197
|
+
try {
|
|
198
|
+
const link = fs.readlinkSync(dst);
|
|
199
|
+
const linkAbs = path.isAbsolute(link)
|
|
200
|
+
? link
|
|
201
|
+
: path.resolve(npmGlobalRoot, link);
|
|
202
|
+
if (linkAbs === src) continue; // already pointing at us
|
|
203
|
+
try {
|
|
204
|
+
fs.statSync(linkAbs);
|
|
205
|
+
continue; // points elsewhere but valid — don't clobber
|
|
206
|
+
} catch {
|
|
207
|
+
// dangling symlink — replace
|
|
208
|
+
fs.unlinkSync(dst);
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
// Real install (or directory) — never clobber.
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
fs.symlinkSync(src, dst);
|
|
220
|
+
console.log(
|
|
221
|
+
`[@vibecontrols/agent] linked shared peer dep ${dep} at ${dst}`,
|
|
222
|
+
);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
// best-effort; symlink can fail on Windows without admin/dev mode,
|
|
225
|
+
// or on filesystems that disallow symlinks. Plugins that depend on
|
|
226
|
+
// these peer deps will still fail to load — surface the reason.
|
|
227
|
+
console.log(
|
|
228
|
+
`[@vibecontrols/agent] could not link ${dep}: ${err.message}`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
} catch {
|
|
234
|
+
/* never fail the install on a peer-dep linking hiccup */
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let entries;
|
|
238
|
+
try {
|
|
239
|
+
entries = fs.readdirSync(bunBin);
|
|
240
|
+
} catch {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
for (const name of entries) {
|
|
245
|
+
const full = path.join(bunBin, name);
|
|
246
|
+
let st;
|
|
247
|
+
try {
|
|
248
|
+
st = fs.lstatSync(full);
|
|
249
|
+
} catch {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (!st.isSymbolicLink()) continue;
|
|
253
|
+
|
|
254
|
+
let link;
|
|
255
|
+
try {
|
|
256
|
+
link = fs.readlinkSync(full);
|
|
257
|
+
} catch {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const target = path.isAbsolute(link) ? link : path.resolve(bunBin, link);
|
|
261
|
+
if (!target.startsWith(vibeGlobalRoot + path.sep)) continue;
|
|
262
|
+
|
|
263
|
+
let targetAlive = true;
|
|
264
|
+
try {
|
|
265
|
+
fs.statSync(target);
|
|
266
|
+
} catch {
|
|
267
|
+
targetAlive = false;
|
|
268
|
+
}
|
|
269
|
+
if (targetAlive) continue;
|
|
270
|
+
|
|
271
|
+
if (OWNED_BIN_NAMES.has(name) && ourCliExists) {
|
|
272
|
+
try {
|
|
273
|
+
fs.unlinkSync(full);
|
|
274
|
+
fs.symlinkSync(ourCli, full);
|
|
275
|
+
try {
|
|
276
|
+
fs.chmodSync(ourCli, 0o755);
|
|
277
|
+
} catch {
|
|
278
|
+
/* non-fatal */
|
|
279
|
+
}
|
|
280
|
+
console.log(
|
|
281
|
+
`[@vibecontrols/agent] rebound stale bun-shim ${full} -> ${ourCli}`,
|
|
282
|
+
);
|
|
283
|
+
continue;
|
|
284
|
+
} catch {
|
|
285
|
+
/* fall through to unlink */
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
fs.unlinkSync(full);
|
|
291
|
+
console.log(`[@vibecontrols/agent] pruned dangling bun-shim: ${full}`);
|
|
292
|
+
} catch {
|
|
293
|
+
/* best-effort */
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Windows-only: when the agent is installed via `npm install -g`, npm derives
|
|
299
|
+
// the `vibe.ps1` / `vibe.cmd` shim's interpreter from the bin's
|
|
300
|
+
// `#!/usr/bin/env bun` shebang — emitting a bare `bun`. If Bun isn't on PATH at
|
|
301
|
+
// invocation time the shim dies with
|
|
302
|
+
// "The term 'bun.exe' is not recognized as a name of a cmdlet...". Bun is the
|
|
303
|
+
// agent's runtime and the installer guarantees it under ~/.bun, so rewrite the
|
|
304
|
+
// bare `bun` token in those shims to bun's absolute path. Best-effort and
|
|
305
|
+
// idempotent: only acts when Bun is NOT already resolvable on PATH (a working
|
|
306
|
+
// setup is left untouched) and a concrete bun.exe exists to point at.
|
|
307
|
+
function patchWindowsNpmBunShims() {
|
|
308
|
+
if (process.platform !== "win32") return;
|
|
309
|
+
const home = os.homedir();
|
|
310
|
+
const bunAbs = path.join(home, ".bun", "bin", "bun.exe");
|
|
311
|
+
if (!fs.existsSync(bunAbs)) return;
|
|
312
|
+
|
|
313
|
+
// If bun already resolves on PATH the npm shim works as-is — don't touch it.
|
|
314
|
+
try {
|
|
315
|
+
const w = spawnSync("where.exe", ["bun"], { stdio: "ignore" });
|
|
316
|
+
if (w && w.status === 0) return;
|
|
317
|
+
} catch {
|
|
318
|
+
/* where.exe missing/blocked — proceed to patch defensively */
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const dirs = new Set();
|
|
322
|
+
if (process.env.APPDATA) dirs.add(path.join(process.env.APPDATA, "npm"));
|
|
323
|
+
if (process.env.USERPROFILE) {
|
|
324
|
+
dirs.add(path.join(process.env.USERPROFILE, "AppData", "Roaming", "npm"));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
for (const dir of dirs) {
|
|
328
|
+
// PowerShell shim: `& "bun$exe" "$basedir/.../cli.js" $args`
|
|
329
|
+
const ps1 = path.join(dir, "vibe.ps1");
|
|
330
|
+
try {
|
|
331
|
+
if (fs.existsSync(ps1)) {
|
|
332
|
+
const c = fs.readFileSync(ps1, "utf8");
|
|
333
|
+
if (c.includes('"bun$exe"')) {
|
|
334
|
+
// String split/join avoids regex-escaping the backslashes in bunAbs.
|
|
335
|
+
fs.writeFileSync(ps1, c.split('"bun$exe"').join(`"${bunAbs}"`));
|
|
336
|
+
console.log(
|
|
337
|
+
`[@vibecontrols/agent] patched ${ps1} -> ${bunAbs} (bun was not on PATH)`,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} catch {
|
|
342
|
+
/* best-effort */
|
|
343
|
+
}
|
|
344
|
+
// cmd.exe shim: npm emits a bare (often quoted) `bun` as the program name.
|
|
345
|
+
const cmd = path.join(dir, "vibe.cmd");
|
|
346
|
+
try {
|
|
347
|
+
if (
|
|
348
|
+
fs.existsSync(cmd) &&
|
|
349
|
+
!fs.readFileSync(cmd, "utf8").includes(bunAbs)
|
|
350
|
+
) {
|
|
351
|
+
const c = fs.readFileSync(cmd, "utf8");
|
|
352
|
+
const patched = c.replace(/"bun"/g, `"${bunAbs}"`);
|
|
353
|
+
if (patched !== c) {
|
|
354
|
+
fs.writeFileSync(cmd, patched);
|
|
355
|
+
console.log(
|
|
356
|
+
`[@vibecontrols/agent] patched ${cmd} -> ${bunAbs} (bun was not on PATH)`,
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
} catch {
|
|
361
|
+
/* best-effort */
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
pruneStaleExternalShims();
|
|
368
|
+
} catch {
|
|
369
|
+
/* never fail the install on a cross-bin cleanup hiccup */
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
patchWindowsNpmBunShims();
|
|
374
|
+
} catch {
|
|
375
|
+
/* never fail the install on a shim-patch hiccup */
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
main();
|
|
380
|
+
} catch {
|
|
381
|
+
/* never fail the install on a cleanup hiccup */
|
|
382
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* preuninstall hook for @vibecontrols/agent.
|
|
4
|
+
*
|
|
5
|
+
* Runs before npm physically removes the package on `npm uninstall -g
|
|
6
|
+
* @vibecontrols/agent`. Cleans up everything the agent left on the host
|
|
7
|
+
* so the user gets a true uninstall — not a half-state where files,
|
|
8
|
+
* tunnels, and autostart daemons keep running with no agent to manage
|
|
9
|
+
* them.
|
|
10
|
+
*
|
|
11
|
+
* What we remove:
|
|
12
|
+
* 1. Running agent daemons — best-effort SIGTERM by PID from the
|
|
13
|
+
* product-level agents.json registry.
|
|
14
|
+
* 2. Detached tunnel children — every PID recorded in any profile's
|
|
15
|
+
* tunnel.state.json (cloudflared/frpc
|
|
16
|
+
* survive `vibe stop` by design, so
|
|
17
|
+
* they survive uninstall too unless
|
|
18
|
+
* we kill them explicitly).
|
|
19
|
+
* 3. Autostart daemons — systemd user units, launchd plists,
|
|
20
|
+
* Windows Task Scheduler tasks. Best-
|
|
21
|
+
* effort across all profiles found in
|
|
22
|
+
* the registry.
|
|
23
|
+
* 4. Data directory — `~/.boff/vibecontrols/` recursive
|
|
24
|
+
* rm. SKIPPED when:
|
|
25
|
+
* - VIBECONTROLS_PRESERVE_DATA=true
|
|
26
|
+
* - npm_config_preserve_data=true
|
|
27
|
+
* (set via `npm uninstall -g
|
|
28
|
+
* @vibecontrols/agent --preserve-data`)
|
|
29
|
+
*
|
|
30
|
+
* Idempotent + safe: every step is wrapped in try/catch and logs to
|
|
31
|
+
* stderr. A cleanup hiccup never throws — we'd rather let the npm
|
|
32
|
+
* uninstall complete than leave the user stuck on a failed hook.
|
|
33
|
+
*
|
|
34
|
+
* CommonJS because npm invokes preuninstall hooks as plain Node scripts
|
|
35
|
+
* via `node`, not Bun. Mirror style of postinstall-shim-fix.cjs.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
"use strict";
|
|
39
|
+
|
|
40
|
+
const fs = require("fs");
|
|
41
|
+
const os = require("os");
|
|
42
|
+
const path = require("path");
|
|
43
|
+
const { spawnSync } = require("child_process");
|
|
44
|
+
|
|
45
|
+
function log(msg) {
|
|
46
|
+
try {
|
|
47
|
+
process.stderr.write(`[@vibecontrols/agent preuninstall] ${msg}\n`);
|
|
48
|
+
} catch {
|
|
49
|
+
/* ignore */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function shouldPreserveData() {
|
|
54
|
+
if (process.env.VIBECONTROLS_PRESERVE_DATA === "true") return true;
|
|
55
|
+
// npm forwards --preserve-data as npm_config_preserve_data="true"
|
|
56
|
+
if (process.env.npm_config_preserve_data === "true") return true;
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getProductDir() {
|
|
61
|
+
if (process.env.VIBECONTROLS_HOME) return process.env.VIBECONTROLS_HOME;
|
|
62
|
+
return path.join(os.homedir(), ".boff", "vibecontrols");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readJsonSafe(file) {
|
|
66
|
+
try {
|
|
67
|
+
if (!fs.existsSync(file)) return null;
|
|
68
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function killPid(pid, signal) {
|
|
75
|
+
if (!pid || typeof pid !== "number") return;
|
|
76
|
+
try {
|
|
77
|
+
process.kill(pid, signal || "SIGTERM");
|
|
78
|
+
} catch {
|
|
79
|
+
/* already dead — ignore */
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Step 1: stop running agent daemons (best-effort SIGTERM from registry).
|
|
84
|
+
function stopAgents(productDir) {
|
|
85
|
+
try {
|
|
86
|
+
const registry = readJsonSafe(path.join(productDir, "agents.json"));
|
|
87
|
+
if (!registry) return [];
|
|
88
|
+
const list = Array.isArray(registry)
|
|
89
|
+
? registry
|
|
90
|
+
: Array.isArray(registry.instances)
|
|
91
|
+
? registry.instances
|
|
92
|
+
: [];
|
|
93
|
+
const names = [];
|
|
94
|
+
for (const entry of list) {
|
|
95
|
+
if (entry && typeof entry.pid === "number") {
|
|
96
|
+
killPid(entry.pid, "SIGTERM");
|
|
97
|
+
log(`SIGTERM agent ${entry.name || "?"} pid=${entry.pid}`);
|
|
98
|
+
}
|
|
99
|
+
if (entry && typeof entry.name === "string") names.push(entry.name);
|
|
100
|
+
}
|
|
101
|
+
return names;
|
|
102
|
+
} catch (err) {
|
|
103
|
+
log(`stopAgents: ${err && err.message ? err.message : err}`);
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Step 2: kill detached tunnel children recorded in any profile's
|
|
109
|
+
// tunnel.state.json under <productDir>/agents/<profile>/tunnel.state.json.
|
|
110
|
+
function stopTunnels(productDir) {
|
|
111
|
+
try {
|
|
112
|
+
const agentsDir = path.join(productDir, "agents");
|
|
113
|
+
if (!fs.existsSync(agentsDir)) return;
|
|
114
|
+
const profiles = fs.readdirSync(agentsDir);
|
|
115
|
+
for (const profile of profiles) {
|
|
116
|
+
const stateFile = path.join(agentsDir, profile, "tunnel.state.json");
|
|
117
|
+
const state = readJsonSafe(stateFile);
|
|
118
|
+
if (state && typeof state.pid === "number") {
|
|
119
|
+
killPid(state.pid, "SIGTERM");
|
|
120
|
+
log(`SIGTERM tunnel pid=${state.pid} (profile=${profile})`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
log(`stopTunnels: ${err && err.message ? err.message : err}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Step 3: remove autostart daemons across all known profiles.
|
|
129
|
+
//
|
|
130
|
+
// The registry's profile names tell us which autostart entries the agent
|
|
131
|
+
// has likely created. We attempt removal for each — if a name has no
|
|
132
|
+
// installed daemon, the underlying tools no-op silently.
|
|
133
|
+
function removeAutostart(productDir, profileNames) {
|
|
134
|
+
// Always include the canonical default so a clean uninstall on a host
|
|
135
|
+
// with no agents.json still strips a manually-installed default daemon.
|
|
136
|
+
const candidates = new Set(["default", ...profileNames]);
|
|
137
|
+
const platform = os.platform();
|
|
138
|
+
for (const name of candidates) {
|
|
139
|
+
try {
|
|
140
|
+
if (platform === "linux") {
|
|
141
|
+
const unit = `vibecontrols-agent-${name}.service`;
|
|
142
|
+
const unitPath = path.join(
|
|
143
|
+
os.homedir(),
|
|
144
|
+
".config",
|
|
145
|
+
"systemd",
|
|
146
|
+
"user",
|
|
147
|
+
unit,
|
|
148
|
+
);
|
|
149
|
+
spawnSync("systemctl", ["--user", "stop", unit], { stdio: "ignore" });
|
|
150
|
+
spawnSync("systemctl", ["--user", "disable", unit], {
|
|
151
|
+
stdio: "ignore",
|
|
152
|
+
});
|
|
153
|
+
if (fs.existsSync(unitPath)) {
|
|
154
|
+
fs.unlinkSync(unitPath);
|
|
155
|
+
log(`removed systemd unit: ${unitPath}`);
|
|
156
|
+
}
|
|
157
|
+
spawnSync("systemctl", ["--user", "daemon-reload"], {
|
|
158
|
+
stdio: "ignore",
|
|
159
|
+
});
|
|
160
|
+
} else if (platform === "darwin") {
|
|
161
|
+
const label = `com.boff.vibecontrols.agent.${name}`;
|
|
162
|
+
const plistPath = path.join(
|
|
163
|
+
os.homedir(),
|
|
164
|
+
"Library",
|
|
165
|
+
"LaunchAgents",
|
|
166
|
+
`${label}.plist`,
|
|
167
|
+
);
|
|
168
|
+
spawnSync("launchctl", ["unload", plistPath], { stdio: "ignore" });
|
|
169
|
+
const uidProc = spawnSync("id", ["-u"], { encoding: "utf8" });
|
|
170
|
+
const uid = (uidProc.stdout || "").trim();
|
|
171
|
+
if (uid) {
|
|
172
|
+
spawnSync("launchctl", ["bootout", `gui/${uid}/${label}`], {
|
|
173
|
+
stdio: "ignore",
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (fs.existsSync(plistPath)) {
|
|
177
|
+
fs.unlinkSync(plistPath);
|
|
178
|
+
log(`removed launchd plist: ${plistPath}`);
|
|
179
|
+
}
|
|
180
|
+
} else if (platform === "win32") {
|
|
181
|
+
const taskName = `VibeControlsAgent_${name}`;
|
|
182
|
+
spawnSync("schtasks.exe", ["/End", "/TN", taskName], {
|
|
183
|
+
stdio: "ignore",
|
|
184
|
+
});
|
|
185
|
+
spawnSync("schtasks.exe", ["/Delete", "/TN", taskName, "/F"], {
|
|
186
|
+
stdio: "ignore",
|
|
187
|
+
});
|
|
188
|
+
// Best-effort cleanup of the generated XML registration file.
|
|
189
|
+
const xmlPath = path.join(productDir, `autostart-${name}.xml`);
|
|
190
|
+
if (fs.existsSync(xmlPath)) {
|
|
191
|
+
try {
|
|
192
|
+
fs.unlinkSync(xmlPath);
|
|
193
|
+
} catch {
|
|
194
|
+
/* ignore */
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
log(
|
|
200
|
+
`removeAutostart(${name}): ${err && err.message ? err.message : err}`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Step 4: remove the data directory (gated by --preserve-data / env var).
|
|
207
|
+
function removeDataDir(productDir) {
|
|
208
|
+
if (shouldPreserveData()) {
|
|
209
|
+
log(
|
|
210
|
+
`VIBECONTROLS_PRESERVE_DATA / --preserve-data set; keeping ${productDir}`,
|
|
211
|
+
);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
if (fs.existsSync(productDir)) {
|
|
216
|
+
fs.rmSync(productDir, { recursive: true, force: true });
|
|
217
|
+
log(`removed data dir: ${productDir}`);
|
|
218
|
+
}
|
|
219
|
+
} catch (err) {
|
|
220
|
+
log(`removeDataDir: ${err && err.message ? err.message : err}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function main() {
|
|
225
|
+
const productDir = getProductDir();
|
|
226
|
+
// Order matters: stop agents first so they don't auto-respawn tunnels;
|
|
227
|
+
// kill tunnels before tearing down autostart units (which on Linux/mac
|
|
228
|
+
// would re-launch the agent + tunnel chain on the next reboot if left
|
|
229
|
+
// enabled); finally rm the data dir.
|
|
230
|
+
const names = stopAgents(productDir);
|
|
231
|
+
// Give agents a brief moment to flush their shutdown handlers before
|
|
232
|
+
// we kill detached tunnel children. 250 ms is enough for SIGTERM
|
|
233
|
+
// delivery without dragging out the npm uninstall noticeably.
|
|
234
|
+
try {
|
|
235
|
+
const end = Date.now() + 250;
|
|
236
|
+
while (Date.now() < end) {
|
|
237
|
+
// Busy-wait is fine here; this script runs once per uninstall and
|
|
238
|
+
// the alternative (setTimeout + Promise) would force the script
|
|
239
|
+
// into async land for very little benefit.
|
|
240
|
+
}
|
|
241
|
+
} catch {
|
|
242
|
+
/* ignore */
|
|
243
|
+
}
|
|
244
|
+
stopTunnels(productDir);
|
|
245
|
+
removeAutostart(productDir, names);
|
|
246
|
+
removeDataDir(productDir);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
main();
|
|
251
|
+
} catch (err) {
|
|
252
|
+
// Never fail the uninstall on a cleanup hiccup.
|
|
253
|
+
log(`unexpected: ${err && err.message ? err.message : err}`);
|
|
254
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{nb as a,ob as b,pb as c,qb as d,rb as e,sb as f}from"./index-v9fx5wab.js";import"./index-01qzsnwd.js";import"./index-d5ysy1yn.js";import"./index-04n4qgvd.js";import"./index-g2raeeh4.js";import"./index-9bqd8veb.js";import"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";export{b as unmountProfileRoutes,a as mountProfileRoutes,c as dispatchProfileRequest,f as __setMountForTests,e as __resetProfileMountsForTests,d as __listMountedProfilesForTests};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{J as a}from"./index-3ys16efc.js";import"./index-2gsarrbn.js";import"./index-b5dhmybd.js";import"./index-e1bw1bwr.js";import"./index-g8zv1gta.js";import"./index-qfz9fy56.js";import"./index-ssjmzqcz.js";import"./index-4wgjx8bf.js";import"./index-g3ap3xpr.js";import"./index-0ckffygp.js";import"./index-rc79x8fw.js";import"./index-yrgm89r8.js";import"./index-52cp759f.js";import"./index-01qzsnwd.js";import"./index-d5ysy1yn.js";import"./index-04n4qgvd.js";import"./index-5mw3eshk.js";import"./index-z5a4yxzz.js";import"./index-ef95xr4z.js";import"./index-xjzmb1pn.js";import"./index-g2raeeh4.js";import"./index-9bqd8veb.js";import"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";export{a as registerCoreCommands};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{x as a}from"./index-1hnw0rhc.js";import"./index-tp4y9jde.js";import"./index-4wgjx8bf.js";import"./index-g3ap3xpr.js";import"./index-0ckffygp.js";import"./index-v9fx5wab.js";import"./index-01qzsnwd.js";import"./index-d5ysy1yn.js";import"./index-04n4qgvd.js";import"./index-g2raeeh4.js";import"./index-9bqd8veb.js";import"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";export{a as attachSecondaryProfile};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{o as a,p as b,q as c,r as d,s as e}from"./index-n7qyrdr1.js";import"./index-0ckffygp.js";import"./index-9bqd8veb.js";import"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";export{a as spawnTracked,c as listTrackedByOwner,b as listTracked,d as killTracked,e as killAllTracked};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|