@agentvault/grok-bridge 0.1.0 → 0.2.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/README.md CHANGED
@@ -6,6 +6,60 @@ Bridges a Grok agent into E2E-encrypted AgentVault direct messages and rooms.
6
6
  no runtime code. The only shared dependency is `@agentvault/agentvault`, the
7
7
  protocol and crypto library.
8
8
 
9
+ ## Usage
10
+
11
+ **Prerequisite:** install Grok and run `grok login`. The bridge copies that
12
+ credential into its sandbox once, at first run.
13
+
14
+ ### One paste (recommended) — `--install`
15
+
16
+ ```sh
17
+ npm i -g @agentvault/grok-bridge
18
+ AV_AGENT_NAME=<agent> AV_INVITE_TOKEN=<token> agentvault-grok-bridge --install
19
+ ```
20
+
21
+ Enrols the agent, waits for you to click **Approve** in the dashboard, installs a
22
+ supervised OS service (launchd on macOS, systemd on Linux), and **exits** — so
23
+ your terminal comes back and the agent survives closing it and rebooting.
24
+
25
+ Three things worth knowing:
26
+
27
+ - **`npm i -g` is not optional.** The service bakes the running entrypoint into
28
+ its launcher. Under `npx` that is a package cache the package manager later
29
+ prunes, so the service would work today and die silently weeks later.
30
+ `--install` refuses an ephemeral entrypoint at startup, before your single-use
31
+ invite token is spent.
32
+ - **`XAI_API_KEY` is NOT inherited by the service.** A supervisor does not read
33
+ your shell environment. Use `grok login`; the bridge refuses to install a
34
+ service that would have no credential and run mute.
35
+ - **Re-running is safe.** With credentials already saved you can omit the token.
36
+ If the installed service is current it is restarted, not re-installed, so
37
+ settings baked at install time are preserved.
38
+
39
+ ### Foreground (no service)
40
+
41
+ ```sh
42
+ AV_AGENT_NAME=<agent> AV_INVITE_TOKEN=<token> npx @agentvault/grok-bridge
43
+ ```
44
+
45
+ Runs in the terminal and stops when you close it. Unchanged by `--install`.
46
+
47
+ ### Managing the service
48
+
49
+ ```sh
50
+ agentvault-grok-bridge status # is it loaded?
51
+ agentvault-grok-bridge restart # pick up new credentials / recover
52
+ agentvault-grok-bridge uninstall # remove it
53
+ agentvault-grok-bridge logs # where the logs are
54
+ ```
55
+
56
+ Use `restart`, **never `install`**, to restart. `install` rebuilds the service
57
+ from your current shell and re-bakes `PATH`, silently discarding environment
58
+ baked at the original install.
59
+
60
+ Logs go to `<data-dir>/logs/grok-bridge.error.log` — the bridge writes to stderr,
61
+ so the `.error.log` file is the one with content in it.
62
+
9
63
  ## Copied-from ledger — REVIEW PERIODICALLY
10
64
 
11
65
  These files were copied once from the Claude bridge and are now owned here. A fix
package/dist/config.d.ts CHANGED
@@ -1,3 +1,29 @@
1
+ /**
2
+ * The one-paste install flag: enrol, install the supervised service, then EXIT so
3
+ * the customer's terminal comes back.
4
+ *
5
+ * WIRE LITERAL. `packages/web/lib/framework-install.ts` emits this exact string in
6
+ * the connect command. The two packages cannot import each other, so the only
7
+ * tripwire is a test on each side that names the other — see config.test.ts
8
+ * "freezes the wire literal".
9
+ *
10
+ * Deliberately "-" prefixed: loadConfig's token slot rejects "-" args, so adding a
11
+ * flag can never be mistaken for an invite token.
12
+ */
13
+ export declare const INSTALL_FLAG = "--install";
14
+ /**
15
+ * Package-manager scratch caches. `service/subcommand.ts` bakes `process.argv[1]`
16
+ * into the launcher script, so a service installed from one of these points at a
17
+ * directory the package manager later prunes — it works today and dies weeks later
18
+ * with nothing naming the cause.
19
+ *
20
+ * A DENYLIST, not proof of durability: it covers npm's npx cache, pnpm/yarn `dlx`,
21
+ * `bunx`, and anything under the OS temp dir. Other ephemeral runners are knowingly
22
+ * unhandled.
23
+ */
24
+ export declare function isEphemeralEntrypoint(entrypoint: string): boolean;
25
+ /** Throw unless `entrypoint` is durable enough to bake into a supervised service. */
26
+ export declare function assertInstallableEntrypoint(entrypoint: string): void;
1
27
  export declare function hasPersistedCreds(dataDir: string): boolean;
2
28
  export declare function slugify(name: string): string;
3
29
  export type DataDirSource = "explicit" | "per-agent";
@@ -38,6 +64,10 @@ export interface BridgeConfig {
38
64
  * instructs the agent. It does not: given a bare message the model answered
39
65
  * in prose and never called room__say, so the reply was discarded. */
40
66
  systemPrompt?: string;
67
+ /** Set by --install: enrol in the foreground, install the supervised service,
68
+ * then EXIT so the customer's terminal returns. False leaves a bare run
69
+ * byte-for-byte as it behaves today. */
70
+ installAfterEnroll: boolean;
41
71
  }
42
72
  export declare function loadConfig(env: NodeJS.ProcessEnv, argv?: string[]): BridgeConfig;
43
73
  //# sourceMappingURL=config.d.ts.map
package/dist/index.js CHANGED
@@ -12,6 +12,18 @@ var __export = (target, all) => {
12
12
  // src/config.ts
13
13
  import { existsSync, readFileSync as readFileSync2, mkdirSync, chmodSync } from "node:fs";
14
14
  import { join as join5, resolve as resolve2, sep, dirname } from "node:path";
15
+ import { tmpdir } from "node:os";
16
+ function isEphemeralEntrypoint(entrypoint) {
17
+ return entrypoint.includes("/_npx/") || entrypoint.includes("/dlx-") || entrypoint.includes("/.bun/install/cache/") || entrypoint.startsWith(tmpdir() + sep);
18
+ }
19
+ function assertInstallableEntrypoint(entrypoint) {
20
+ if (isEphemeralEntrypoint(entrypoint)) {
21
+ throw new Error(
22
+ `cannot install a background service from an ephemeral package cache (${entrypoint}) \u2014 it is deleted on cleanup and the service would silently die later. Install globally first:
23
+ npm i -g @agentvault/grok-bridge && agentvault-grok-bridge ` + INSTALL_FLAG
24
+ );
25
+ }
26
+ }
15
27
  function hasRecoverableBackup(dataDir) {
16
28
  try {
17
29
  const parsed = JSON.parse(readFileSync2(join5(dataDir, BACKUP_FILE), "utf-8"));
@@ -35,6 +47,12 @@ function resolveDataDir(env) {
35
47
  function loadConfig(env, argv = []) {
36
48
  const { dataDir, source: dataDirSource } = resolveDataDir(env);
37
49
  const inviteToken = (argv[0] && !argv[0].startsWith("-") ? argv[0] : "") || env.AV_INVITE_TOKEN || "";
50
+ const unknownFlags = argv.filter((a2) => a2.startsWith("-") && !KNOWN_FLAGS.has(a2));
51
+ if (unknownFlags.length) {
52
+ throw new Error(
53
+ `unknown option(s): ${unknownFlags.join(", ")} \u2014 the only supported flag is ${INSTALL_FLAG}`
54
+ );
55
+ }
38
56
  const workspaceDir = env.AV_WORKSPACE_DIR || join5(dirname(dataDir), "workspaces", slugify2(env.AV_AGENT_NAME ?? "grok"));
39
57
  const wsReal = resolve2(workspaceDir);
40
58
  const ddReal = resolve2(dataDir);
@@ -60,6 +78,9 @@ function loadConfig(env, argv = []) {
60
78
  inviteToken,
61
79
  dataDir,
62
80
  dataDirSource,
81
+ // One-paste install (--install): enrol, hand off to the supervisor, exit.
82
+ // Default false keeps a bare `npx @agentvault/grok-bridge` run identical.
83
+ installAfterEnroll: argv.includes(INSTALL_FLAG),
63
84
  apiUrl: env.AV_API_URL ?? "https://api.agentvault.chat",
64
85
  // Identity shown to the agent's own session. Set this to the agent's
65
86
  // AgentVault name via AV_AGENT_NAME (the native connect command passes it).
@@ -76,10 +97,12 @@ function loadConfig(env, argv = []) {
76
97
  systemPrompt: env.AV_SYSTEM_PROMPT || void 0
77
98
  };
78
99
  }
79
- var CRED_FILES, BACKUP_FILE;
100
+ var INSTALL_FLAG, KNOWN_FLAGS, CRED_FILES, BACKUP_FILE;
80
101
  var init_config = __esm({
81
102
  "src/config.ts"() {
82
103
  "use strict";
104
+ INSTALL_FLAG = "--install";
105
+ KNOWN_FLAGS = /* @__PURE__ */ new Set([INSTALL_FLAG]);
83
106
  CRED_FILES = ["agentvault.json", "secure-channel.json"];
84
107
  BACKUP_FILE = "agentvault.json.bak";
85
108
  }
@@ -130,6 +153,16 @@ function writeLauncher(deps, spec) {
130
153
  deps.writeFile(spec.launcherPath, spec.launcherScript);
131
154
  deps.chmod(spec.launcherPath, 493);
132
155
  }
156
+ function parseLauncherEntrypoint(script) {
157
+ if (!script) return void 0;
158
+ const m2 = /^if n=\$\(command -v .+\); then exec "\$n" (.+) "\$@"; fi$/m.exec(script);
159
+ if (!m2) return void 0;
160
+ return unSq(m2[1]);
161
+ }
162
+ function unSq(quoted) {
163
+ if (quoted.length < 2 || !quoted.startsWith("'") || !quoted.endsWith("'")) return void 0;
164
+ return quoted.slice(1, -1).replace(/'\\''/g, "'");
165
+ }
133
166
  var FALLBACK_NODES;
134
167
  var init_launcher = __esm({
135
168
  "src/service/launcher.ts"() {
@@ -232,7 +265,6 @@ var init_launchd = __esm({
232
265
  constructor(deps) {
233
266
  this.deps = deps;
234
267
  }
235
- deps;
236
268
  plistPath(label) {
237
269
  return join10(this.deps.home, "Library", "LaunchAgents", `${label}.plist`);
238
270
  }
@@ -317,7 +349,6 @@ var init_systemd = __esm({
317
349
  constructor(deps) {
318
350
  this.deps = deps;
319
351
  }
320
- deps;
321
352
  unitPath(label) {
322
353
  return join11(this.deps.home, ".config", "systemd", "user", `${label}.service`);
323
354
  }
@@ -353,6 +384,11 @@ var init_systemd = __esm({
353
384
  });
354
385
 
355
386
  // src/service/backend.ts
387
+ var backend_exports = {};
388
+ __export(backend_exports, {
389
+ defaultDeps: () => defaultDeps,
390
+ selectBackend: () => selectBackend
391
+ });
356
392
  import { spawnSync } from "node:child_process";
357
393
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2, rmSync, existsSync as existsSync3, chmodSync as chmodSync4 } from "node:fs";
358
394
  import { userInfo } from "node:os";
@@ -456,12 +492,105 @@ var init_subcommand = __esm({
456
492
  }
457
493
  });
458
494
 
495
+ // src/service/install-on-ready.ts
496
+ var install_on_ready_exports = {};
497
+ __export(install_on_ready_exports, {
498
+ installOnReady: () => installOnReady
499
+ });
500
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
501
+ import { join as join12 } from "node:path";
502
+ function installOnReady(channel, cfg, deps) {
503
+ const hasCreds = deps.hasCreds ?? hasPersistedCreds;
504
+ const readLauncher = deps.readLauncher ?? ((p2) => existsSync4(p2) ? readFileSync3(p2, "utf8") : void 0);
505
+ const fenceAuthExists = deps.fenceAuthExists ?? ((home) => existsSync4(join12(home, ".grok", "auth.json")));
506
+ let fired = false;
507
+ channel.on("ready", () => {
508
+ if (fired) return;
509
+ fired = true;
510
+ void (async () => {
511
+ try {
512
+ if (!hasCreds(cfg.dataDir)) {
513
+ throw new Error(
514
+ `enrolment did not persist credentials to ${cfg.dataDir} \u2014 refusing to install a service that would crash-loop`
515
+ );
516
+ }
517
+ if (!fenceAuthExists(cfg.fenceHome)) {
518
+ throw new Error(
519
+ "no grok credential in the fence \u2014 run `grok login`, then paste the same command again (your enrolment is already saved, so the token is not needed). Note that XAI_API_KEY from your shell is NOT inherited by the background service."
520
+ );
521
+ }
522
+ if (deps.waitForIdle) {
523
+ const timeoutMs = deps.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
524
+ let timer;
525
+ const timedOut = await Promise.race([
526
+ deps.waitForIdle().then(() => false),
527
+ new Promise((r2) => {
528
+ timer = setTimeout(() => r2(true), timeoutMs);
529
+ })
530
+ ]);
531
+ if (timer) clearTimeout(timer);
532
+ if (timedOut) {
533
+ deps.log(
534
+ `agent still busy after ${timeoutMs}ms \u2014 handing off anyway; that reply may be lost`
535
+ );
536
+ }
537
+ }
538
+ const reaped = await deps.reap();
539
+ if (!reaped.ok) {
540
+ throw new Error(
541
+ `teardown did not complete (${reaped.failures.join("; ")}) \u2014 refusing to install over a session that may still be open`
542
+ );
543
+ }
544
+ const backend = deps.backend ?? selectBackend(process.platform);
545
+ const spec = buildServiceSpec(cfg, {
546
+ entrypoint: deps.entrypoint,
547
+ home: deps.env.HOME ?? "",
548
+ path: deps.env.PATH ?? ""
549
+ });
550
+ const installedEntrypoint = parseLauncherEntrypoint(readLauncher(spec.launcherPath));
551
+ const entrypointIsCurrent = installedEntrypoint !== void 0 && !isEphemeralEntrypoint(installedEntrypoint) && installedEntrypoint === deps.entrypoint;
552
+ if (backend.status(spec.label).loaded && entrypointIsCurrent) {
553
+ backend.restart(spec.label);
554
+ deps.log(`service ${spec.label} already installed and current \u2014 restarted`);
555
+ } else {
556
+ backend.install(spec);
557
+ deps.log(
558
+ installedEntrypoint && !entrypointIsCurrent ? `service ${spec.label} pointed at a stale entrypoint (${installedEntrypoint}) \u2014 re-installed` : `installed service ${spec.label}`
559
+ );
560
+ }
561
+ const after = backend.status(spec.label);
562
+ if (!after.loaded) {
563
+ throw new Error(`service did not load: ${after.detail}`);
564
+ }
565
+ deps.log(`it is running now. logs: ${spec.logErr}`);
566
+ deps.log(`check it: agentvault-grok-bridge status`);
567
+ deps.log(`restart it: agentvault-grok-bridge restart (never 'install')`);
568
+ deps.exit(0);
569
+ } catch (e) {
570
+ deps.log(`--install failed: ${e instanceof Error ? e.message : String(e)}`);
571
+ deps.exit(1);
572
+ }
573
+ })();
574
+ });
575
+ }
576
+ var DEFAULT_IDLE_TIMEOUT_MS;
577
+ var init_install_on_ready = __esm({
578
+ "src/service/install-on-ready.ts"() {
579
+ "use strict";
580
+ init_config();
581
+ init_spec();
582
+ init_launcher();
583
+ init_backend();
584
+ DEFAULT_IDLE_TIMEOUT_MS = 3e4;
585
+ }
586
+ });
587
+
459
588
  // src/index.ts
460
589
  import { spawn } from "node:child_process";
461
590
  import { mkdirSync as mkdirSync5 } from "node:fs";
462
- import { join as join12 } from "node:path";
591
+ import { join as join13 } from "node:path";
463
592
 
464
- // ../plugin/dist/index.js
593
+ // ../../../../packages/plugin/dist/index.js
465
594
  import * as nc from "node:crypto";
466
595
  import { chmod, mkdir, writeFile } from "node:fs/promises";
467
596
  import { readdir, readFile, rename, rm } from "node:fs/promises";
@@ -732,7 +861,7 @@ var init_libsodium_sumo = __esm2({
732
861
  }, Module: function(binary) {
733
862
  }, Instance: function(module2, info) {
734
863
  this.exports = // EMSCRIPTEN_START_ASM
735
- (function instantiate(ea2) {
864
+ function instantiate(ea2) {
736
865
  var a2;
737
866
  var b2 = new Uint8Array(123);
738
867
  for (var c2 = 25; c2 >= 0; --c2) {
@@ -40527,7 +40656,7 @@ var init_libsodium_sumo = __esm2({
40527
40656
  } } }), f: yi, g: ma2, h: Ba2, i: ma2, j: ka2, k: Le2, l: Je2, m: zg, n: yg, o: xg, p: wg, q: ka2, r: Ba2, s: ka2, t: ka2, u: Le2, v: ra2, w: vg, x: ug, y: tg, z: sg, A: Ba2, B: rg, C: qg, D: pg, E: ng, F: mg, G: lg, H: kg, I: jg, J: ka2, K: Ce2, L: Ba2, M: ma2, N: Ta2, O: ra2, P: ka2, Q: Oa2, R: Ba2, S: ma2, T: Ta2, U: ra2, V: ig, W: hg, X: gg, Y: fg, Z: ka2, _: Ma2, $: Ba2, aa: ma2, ba: Ta2, ca: ra2, da: ka2, ea: ka2, fa: Kg, ga: wd, ha: vd, ia: ra2, ja: ka2, ka: ka2, la: Cb, ma: ra2, na: Xa2, oa: eg, pa: qb, qa: dg, ra: cg, sa: ua2, ta: ka2, ua: Dc, va: ra2, wa: Va2, xa: ud, ya: Ya2, za: bg, Aa: ag, Ba: ka2, Ca: ka2, Da: Dc, Ea: ra2, Fa: ze2, Ga: ud, Ha: Ng, Ia: wd, Ja: vd, Ka: ka2, La: ka2, Ma: ka2, Na: ka2, Oa: Ma2, Pa: ka2, Qa: ma2, Ra: ma2, Sa: Ta2, Ta: dh2, Ua: ch, Va: bh, Wa: ah, Xa: Lb, Ya: Kb, Za: td, _a: sd, $a: rd, ab: $f, bb: _f, cb: Zf, db: qd, eb: Yf, fb: pd, gb: Xf, hb: Wf, ib: Uf, jb: Md, kb: tc, lb: nb, mb, nb: Lb, ob: Kb, pb: td, qb: sd, rb: ka2, sb: ka2, tb: ka2, ub: ka2, vb: Ma2, wb: ka2, xb: ma2, yb: ma2, zb: Ta2, Ab: Aa2, Bb: ka2, Cb: ma2, Db: ka2, Eb: ma2, Fb: Sa2, Gb: ka2, Hb: ma2, Ib: ka2, Jb: ma2, Kb: vb, Lb: ua2, Mb: ma2, Nb: ka2, Ob: ma2, Pb: ub, Qb: ua2, Rb: ma2, Sb: ka2, Tb: ma2, Ub: tb, Vb: ua2, Wb: ma2, Xb: ka2, Yb: ma2, Zb: ma2, _b: ua2, $b: ka2, ac: ma2, bc: ua2, cc: ka2, dc: Nd, ec: pe2, fc: od, gc: fh, hc: nd, ic: eh, jc: ra2, kc: ma2, lc: ua2, mc: ka2, nc: ma2, oc: ua2, pc: ka2, qc: ma2, rc: ma2, sc: pe2, tc: ra2, uc: od, vc: Tf, wc: Ja2, xc: xh, yc: nd, zc: Ia2, Ac: ua2, Bc: md, Cc: Lg, Dc: ka2, Ec: Ag, Fc: Ub, Gc: Sf, Hc: Ua2, Ic: Rf, Jc: ua2, Kc: Cb, Lc: Za2, Mc: ic, Nc: Ca2, Oc: md, Pc: ma2, Qc: ua2, Rc: Oa2, Sc: ka2, Tc: ld, Uc: Nd, Vc: ma2, Wc: ua2, Xc: Oa2, Yc: ka2, Zc: ld, _c: ra2, $c: Gh, ad: Fh, bd: Eh, cd: Dh, dd: ra2, ed: Ch, fd: ka2, gd: Ba2, hd: Bh, id: Cb, jd: ze2, kd: hi, ld: gi, md: fi, nd: ei, od: di, pd: ua2, qd: Ba2, rd: ci, sd: Dc, td: Gg, ud: nb, vd: Fg, wd: Eg, xd: ka2, yd: ka2, zd: ka2, Ad: ka2, Bd: Dg, Cd: Rc, Dd: ma2, Ed: ka2, Fd: kd, Gd: jd, Hd: Sg, Id: id, Jd: Rg, Kd: Qg, Ld: ra2, Md: kd, Nd: jd, Od: Ga2, Pd: id, Qd: Fa2, Rd: ma2, Sd: ka2, Td: Rc, Ud: ra2, Vd: Db, Wd: ma2, Xd: wa2, Yd: Ba2, Zd: wa2, _d: ma2, $d: yc, ae: wh, be: Vb, ce: wa2, de: xc, ee: wc, fe: vc, ge: me2, he: vh, ie: uh, je: Oa2, ke: th, le: Pf, me: Of, ne: Nf, oe: Mf, pe: Lf, qe: fb, re: ma2, se: wa2, te: Ba2, ue: wa2, ve: ma2, we: yc, xe: fe2, ye: Db, ze: wa2, Ae: xc, Be: wc, Ce: fb, De: ee2, Ee: Vb, Fe: de2, Ge: vc, He: uc, Ie: Kf, Je: hd, Ke: Jf, Le: Db, Me: fb, Ne: fb, Oe: ma2, Pe: wa2, Qe: Ba2, Re: wa2, Se: ma2, Te: yc, Ue: fe2, Ve: Db, We: wa2, Xe: xc, Ye: wc, Ze: fb, _e: ee2, $e: Vb, af: de2, bf: vc, cf: uc, df: Hf, ef: hd, ff: Gf, gf: Ff, hf: Ef, jf: sh, kf: Jg, lf: Ig, mf: Hg, nf: ka2, of: ka2, pf: Ea2, qf: wb, rf: ka2, sf: ka2, tf: ka2, uf: Ma2, vf: ka2, wf: ma2, xf: ma2, yf: Ta2, zf: Mg, Af: Lb, Bf: Kb, Cf: ra2, Df: rd, Ef: Df, Ff: qd, Gf: pd, Hf: Lb, If: Kb, Jf: ka2, Kf: Ma2, Lf: ka2, Mf: ma2, Nf: ma2, Of: Ta2, Pf: ra2, Qf: ra2, Rf: Mh, Sf: Lh, Tf: Kh, Uf: Cf, Vf: Bf, Wf: Jh, Xf: Ih, Yf: Ma2, Zf: ka2, _f: Hh, $f: Ba2, ag: Db, bg: fb, cg: Vb, dg: Oa2, eg: ma2, fg: Tg, gg: gd, hg: Je2, ig: Oa2, jg: ma2, kg: gd, lg: Cb, mg: ua2, ng: ka2, og: ka2, pg: ua2, qg: Wd, rg: lh, sg: kh, tg: jh, ug: fd, vg: ed, wg: dd, xg: cd, yg: ih, zg: ic, Ag: hh, Bg: gh, Cg: Cb, Dg: ua2, Eg: ka2, Fg: ka2, Gg: ua2, Hg: Wd, Ig: nh, Jg: mh, Kg: Vd, Lg: ic, Mg: Ud, Ng: Td, Og: Yd, Pg: Xd, Qg: ph, Rg: oh, Sg: cd, Tg: ed, Ug: dd, Vg: fd, Wg: ka2, Xg: Oa2, Yg: wa2, Zg: ka2, _g: Ce2, $g: wa2, ah: zf, bh: yf, ch: xf, dh: wf, eh: vf, fh: uf, gh: ra2, hh: ra2, ih: ka2, jh: Ma2, kh: wa2, lh: qh, mh: bd, nh: ad, oh: ra2, ph: ka2, qh: Oa2, rh: wa2, sh: tf, th: sf, uh: rf, vh: ra2, wh: bd, xh: qf, yh: ad, zh: ka2, Ah: Ma2, Bh: wa2, Ch: ra2, Dh: ma2, Eh: ka2, Fh: ua2, Gh: db, Hh: kb, Ih: xd, Jh: oi, Kh: ni, Lh: Ae2, Mh: mi, Nh: za2, Oh: li, Ph: ka2, Qh: ki, Rh: pf, Sh: Ah, Th: zh, Uh: yh, Vh: Bc, Wh: Ac, Xh: ti, Yh: ji, Zh: ii, _h: Cg, $h: Bg, ai: fb, bi: Ba2, ci: tc, di: nb, ei: rh, fi: $c, gi: of, hi: nf, ii: mf, ji: _c, ki: lf, li: Yc, mi: kf, ni: ka2, oi: ka2, pi: ka2, qi: ka2, ri: Ma2, si: ma2, ti: Ta2, ui: jf, vi: hf, wi: Md, xi: bi, yi: ai, zi: $h, Ai: _h, Bi: Zh, Ci: ye2, Di: xe2, Ei: we2, Fi: ve2, Gi: ue2, Hi: te2, Ii: se2, Ji: re2, Ki: ka2, Li: ua2, Mi: ka2, Ni: ua2, Oi: ka2, Pi: Yh, Qi: Xh, Ri: Wh, Si: Vh, Ti: Uh, Ui: Th, Vi: Sh, Wi: Rh, Xi: Qh, Yi: Ph, Zi: Oh, _i: re2, $i: Nh, aj: ka2, bj: ua2, cj: ua2, dj: ka2, ej: gf, fj: ma2, gj: wa2, hj: Ba2, ij: wa2, jj: ka2, kj: $g, lj: _g, mj: Zg, nj: wa2, oj: Ed, pj: wa2, qj: Yg, rj: Ed, sj: me2, tj: uc, uj: ff, vj: ef, wj: df, xj: cf, yj: Xg, zj: Wg, Aj: Vg, Bj: Ug, Cj: ka2, Dj: ka2, Ej: Pg, Fj: Og, Gj: ka2, Hj: ka2, Ij: $c, Jj: bf, Kj: _c, Lj: Yc, Mj: ka2, Nj: Ma2, Oj: ma2, Pj: Ta2, Qj: ma2, Rj: ma2, Sj: af, Tj: $e2, Uj: _e2, Vj: ka2, Wj: Oa2, Xj: wa2, Yj: ra2, Zj: Ze2, _j: Ye2, $j: ka2, ak: Oa2, bk: wa2, ck: ra2, dk: ka2, ek: Ma2, fk: wa2, gk: Xe2, hk: We2, ik: Ve2, jk: ra2, kk: Da2, lk: ta2 };
40528
40657
  }
40529
40658
  return da2(ea2);
40530
- })(info);
40659
+ }(info);
40531
40660
  }, instantiate: function(binary, info) {
40532
40661
  return { then: function(ok) {
40533
40662
  var module2 = new WebAssembly2.Module(binary);
@@ -81102,7 +81231,7 @@ function _stringbool(Classes, _params) {
81102
81231
  type: "pipe",
81103
81232
  in: stringSchema,
81104
81233
  out: booleanSchema,
81105
- transform: ((input, payload) => {
81234
+ transform: (input, payload) => {
81106
81235
  let data = input;
81107
81236
  if (params.case !== "sensitive")
81108
81237
  data = data.toLowerCase();
@@ -81121,14 +81250,14 @@ function _stringbool(Classes, _params) {
81121
81250
  });
81122
81251
  return {};
81123
81252
  }
81124
- }),
81125
- reverseTransform: ((input, _payload) => {
81253
+ },
81254
+ reverseTransform: (input, _payload) => {
81126
81255
  if (input === true) {
81127
81256
  return truthyArray[0] || "true";
81128
81257
  } else {
81129
81258
  return falsyArray[0] || "false";
81130
81259
  }
81131
- }),
81260
+ },
81132
81261
  error: params.error
81133
81262
  });
81134
81263
  return codec2;
@@ -82184,10 +82313,10 @@ var init_schemas2 = __esm2({
82184
82313
  inst.with = inst.check;
82185
82314
  inst.clone = (_def, params) => clone(inst, _def, params);
82186
82315
  inst.brand = () => inst;
82187
- inst.register = ((reg, meta3) => {
82316
+ inst.register = (reg, meta3) => {
82188
82317
  reg.add(inst, meta3);
82189
82318
  return inst;
82190
- });
82319
+ };
82191
82320
  inst.apply = (fn) => fn(inst);
82192
82321
  });
82193
82322
  ZodMiniObject = /* @__PURE__ */ $constructor("ZodMiniObject", (inst, def) => {
@@ -83273,10 +83402,10 @@ var init_schemas3 = __esm2({
83273
83402
  inst.with = inst.check;
83274
83403
  inst.clone = (def2, params) => clone(inst, def2, params);
83275
83404
  inst.brand = () => inst;
83276
- inst.register = ((reg, meta3) => {
83405
+ inst.register = (reg, meta3) => {
83277
83406
  reg.add(inst, meta3);
83278
83407
  return inst;
83279
- });
83408
+ };
83280
83409
  inst.parse = (data, params) => parse2(inst, data, params, { callee: inst.parse });
83281
83410
  inst.safeParse = (data, params) => safeParse3(inst, data, params);
83282
83411
  inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
@@ -83938,13 +84067,11 @@ function assertCompleteRequestPrompt(request) {
83938
84067
  if (request.params.ref.type !== "ref/prompt") {
83939
84068
  throw new TypeError(`Expected CompleteRequestPrompt, but got ${request.params.ref.type}`);
83940
84069
  }
83941
- void request;
83942
84070
  }
83943
84071
  function assertCompleteRequestResourceTemplate(request) {
83944
84072
  if (request.params.ref.type !== "ref/resource") {
83945
84073
  throw new TypeError(`Expected CompleteRequestResourceTemplate, but got ${request.params.ref.type}`);
83946
84074
  }
83947
- void request;
83948
84075
  }
83949
84076
  var LATEST_PROTOCOL_VERSION;
83950
84077
  var DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
@@ -98107,8 +98234,6 @@ var GrokSession = class {
98107
98234
  this.driver = driver2;
98108
98235
  this.opts = opts;
98109
98236
  }
98110
- driver;
98111
- opts;
98112
98237
  queue = [];
98113
98238
  running = false;
98114
98239
  /** The message whose turn is in flight, or null between turns. Null is a
@@ -98221,9 +98346,6 @@ var AcpChildDied = class extends Error {
98221
98346
  this.signal = signal;
98222
98347
  this.name = "AcpChildDied";
98223
98348
  }
98224
- method;
98225
- code;
98226
- signal;
98227
98349
  };
98228
98350
  var AcpTimeout = class extends Error {
98229
98351
  constructor(method, ms) {
@@ -98232,8 +98354,6 @@ var AcpTimeout = class extends Error {
98232
98354
  this.ms = ms;
98233
98355
  this.name = "AcpTimeout";
98234
98356
  }
98235
- method;
98236
- ms;
98237
98357
  };
98238
98358
  var AcpConnection = class {
98239
98359
  constructor(child, opts) {
@@ -98251,7 +98371,6 @@ var AcpConnection = class {
98251
98371
  this.stdinErrors++;
98252
98372
  });
98253
98373
  }
98254
- child;
98255
98374
  nextId = 1;
98256
98375
  pending = /* @__PURE__ */ new Map();
98257
98376
  /** Undecoded leftover. Decode UTF-8 only on complete lines so a 4-byte
@@ -98434,8 +98553,6 @@ var GrokDriver = class {
98434
98553
  this.acp.onNotification((method, params) => this.onNotification(method, params));
98435
98554
  this.acp.onRequest((method, params) => this.onAgentRequest(method, params));
98436
98555
  }
98437
- child;
98438
- opts;
98439
98556
  acp;
98440
98557
  session = null;
98441
98558
  /** State for the turn currently in flight. Null between turns, so a late
@@ -98846,16 +98963,76 @@ function buildClientVersion2(version2) {
98846
98963
  return `@agentvault/grok-bridge@${version2 ?? "dev"}`;
98847
98964
  }
98848
98965
 
98966
+ // src/reap.ts
98967
+ var DEFAULT_KILL_TIMEOUT_MS = 5e3;
98968
+ var SIGKILL_GRACE_MS = 250;
98969
+ function childHasExited(child) {
98970
+ return child.exitCode !== null && child.exitCode !== void 0 || child.signalCode !== null && child.signalCode !== void 0;
98971
+ }
98972
+ function waitForChildExit(child, killTimeoutMs, log) {
98973
+ if (childHasExited(child)) return Promise.resolve();
98974
+ return new Promise((resolve3) => {
98975
+ let settled = false;
98976
+ const timers = [];
98977
+ const done = () => {
98978
+ if (settled) return;
98979
+ settled = true;
98980
+ for (const t2 of timers) clearTimeout(t2);
98981
+ resolve3();
98982
+ };
98983
+ child.on("exit", done);
98984
+ timers.push(
98985
+ setTimeout(() => {
98986
+ log(`grok child ignored SIGTERM after ${killTimeoutMs}ms \u2014 escalating to SIGKILL`);
98987
+ try {
98988
+ child.kill?.("SIGKILL");
98989
+ } catch {
98990
+ }
98991
+ timers.push(setTimeout(done, SIGKILL_GRACE_MS));
98992
+ }, killTimeoutMs)
98993
+ );
98994
+ });
98995
+ }
98996
+ async function reap(r2, opts = {}) {
98997
+ const log = opts.log ?? (() => {
98998
+ });
98999
+ const killTimeoutMs = opts.killTimeoutMs ?? DEFAULT_KILL_TIMEOUT_MS;
99000
+ const failures = [];
99001
+ const attempt = async (label, fn) => {
99002
+ try {
99003
+ await fn();
99004
+ } catch (e) {
99005
+ const msg = `${label}: ${e instanceof Error ? e.message : String(e)}`;
99006
+ failures.push(msg);
99007
+ log(`reap: ${msg}`);
99008
+ }
99009
+ };
99010
+ if (r2.channel) await attempt("channel.stop", () => r2.channel.stop());
99011
+ if (r2.driver) await attempt("driver.stop", () => r2.driver.stop());
99012
+ if (r2.child) await waitForChildExit(r2.child, killTimeoutMs, log);
99013
+ if (r2.mcp) await attempt("mcp.close", () => r2.mcp.close());
99014
+ return { ok: failures.length === 0, failures };
99015
+ }
99016
+
98849
99017
  // src/index.ts
98850
99018
  var shuttingDown = false;
99019
+ var exiting = false;
98851
99020
  var driver;
98852
99021
  var mcp;
98853
- var shutdown = async (code = 0) => {
98854
- if (shuttingDown) return;
99022
+ var channelRef;
99023
+ var childRef;
99024
+ var reapAll = async () => {
98855
99025
  shuttingDown = true;
99026
+ return reap(
99027
+ { channel: channelRef, driver, mcp, child: childRef },
99028
+ { log: (m2) => logLine("[grok-bridge] " + m2) }
99029
+ );
99030
+ };
99031
+ var shutdown = async (code = 0) => {
99032
+ if (exiting) return;
99033
+ exiting = true;
98856
99034
  try {
98857
- await driver?.stop();
98858
- await mcp?.close();
99035
+ await reapAll();
98859
99036
  } finally {
98860
99037
  process.exit(code);
98861
99038
  }
@@ -98868,6 +99045,11 @@ async function main() {
98868
99045
  process.exit(process.exitCode ?? 0);
98869
99046
  }
98870
99047
  const cfg = loadConfig(process.env, process.argv.slice(2));
99048
+ if (cfg.installAfterEnroll) {
99049
+ assertInstallableEntrypoint(process.argv[1]);
99050
+ const { selectBackend: selectBackend2 } = await Promise.resolve().then(() => (init_backend(), backend_exports));
99051
+ selectBackend2(process.platform);
99052
+ }
98871
99053
  mkdirSync5(cfg.workspaceDir, { recursive: true });
98872
99054
  mkdirSync5(cfg.fenceHome, { recursive: true });
98873
99055
  seedFenceCredential({
@@ -98882,9 +99064,10 @@ async function main() {
98882
99064
  agentName: cfg.agentName,
98883
99065
  platform: "node",
98884
99066
  clientVersion: buildClientVersion2(
98885
- true ? "0.1.0" : void 0
99067
+ true ? "0.2.0" : void 0
98886
99068
  )
98887
99069
  });
99070
+ channelRef = channel;
98888
99071
  let session;
98889
99072
  mcp = await startRoomMcpServer({
98890
99073
  // `session` is assigned further down. The current ordering is safe — grok
@@ -98902,7 +99085,7 @@ async function main() {
98902
99085
  },
98903
99086
  log: (m2) => logLine(m2)
98904
99087
  });
98905
- const pluginDir = join12(cfg.fenceHome, "av-room-plugin");
99088
+ const pluginDir = join13(cfg.fenceHome, "av-room-plugin");
98906
99089
  writeRoomPluginHttp(pluginDir, mcp.url);
98907
99090
  const spawnSpec = grokSpawnArgs({
98908
99091
  grokPath: cfg.grokPath,
@@ -98917,6 +99100,7 @@ async function main() {
98917
99100
  env: spawnSpec.env,
98918
99101
  stdio: ["pipe", "pipe", "pipe"]
98919
99102
  });
99103
+ childRef = child;
98920
99104
  child.on("error", (err) => {
98921
99105
  logLine(`[grok-bridge] fatal: ${describeSpawnFailure(err, spawnSpec)}`);
98922
99106
  process.exit(1);
@@ -98966,6 +99150,23 @@ async function main() {
98966
99150
  log: (m2) => logLine("[grok-bridge] " + m2),
98967
99151
  onTerminal: (_reason, o2) => void shutdown(o2.restart ? 1 : 0)
98968
99152
  });
99153
+ if (cfg.installAfterEnroll) {
99154
+ const { installOnReady: installOnReady2 } = await Promise.resolve().then(() => (init_install_on_ready(), install_on_ready_exports));
99155
+ installOnReady2(channel, cfg, {
99156
+ entrypoint: process.argv[1],
99157
+ env: process.env,
99158
+ // FOUND LIVE 2026-08-16: the owner's activation greeting is usually already
99159
+ // being answered when `ready` fires, and reaping mid-turn killed the reply
99160
+ // for good — the message was acked, so the service never saw it again.
99161
+ // GrokSession.idle() resolves when the queue drains.
99162
+ waitForIdle: () => session.idle(),
99163
+ // The one shared teardown. `exiting` is deliberately NOT set here: Ctrl-C
99164
+ // must keep working for the whole multi-second launchctl window.
99165
+ reap: reapAll,
99166
+ exit: (code) => process.exit(code),
99167
+ log: (m2) => logLine("[grok-bridge] " + m2)
99168
+ });
99169
+ }
98969
99170
  await channel.start();
98970
99171
  }
98971
99172
  main().catch(async (err) => {
package/dist/reap.d.ts ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The ONE teardown path. Both `shutdown()` and the `--install` hand-off call this;
3
+ * nothing copies its body.
4
+ *
5
+ * WHY IT IS SHARED. An earlier design inlined a second copy inside the install
6
+ * flow. That copy stopped only the driver and the MCP listener — it never closed
7
+ * the AgentVault WebSocket, and it did not wait for the grok child to die. Both
8
+ * omissions are invisible in a unit test that substitutes the teardown closure,
9
+ * and both matter: `LaunchdBackend.install` runs bootout -> poll-until-gone ->
10
+ * bootstrap -> kickstart synchronously, so the supervised service is alive the
11
+ * instant install returns. A half-reaped foreground process means two ACP sessions
12
+ * and two WebSockets for one device.
13
+ */
14
+ export interface Reapable {
15
+ /** SecureChannel. `stop()` closes the WS, flushes pending ACKs, and sets the
16
+ * stopped flag so it will not reconnect. */
17
+ channel?: {
18
+ stop(): Promise<void>;
19
+ };
20
+ driver?: {
21
+ stop(): Promise<void>;
22
+ };
23
+ mcp?: {
24
+ close(): Promise<void>;
25
+ };
26
+ /** The grok child. `exitCode`/`signalCode` are read to detect an ALREADY-dead
27
+ * child; both are non-null once it has exited.
28
+ *
29
+ * ⚠️ The signal parameter is `NodeJS.Signals`, NOT `string`. A real
30
+ * `ChildProcess.kill` is `(signal?: NodeJS.Signals | number) => boolean`, and
31
+ * parameter types are contravariant — declaring `string` here makes a real
32
+ * ChildProcess UNASSIGNABLE to this shape and fails the strict build at the
33
+ * index.ts call site. `signalCode` is `NodeJS.Signals | null` for the same
34
+ * reason. Verified: widening it to `string` reintroduces TS2322. */
35
+ child?: {
36
+ kill?: (signal: NodeJS.Signals) => unknown;
37
+ on(event: "exit", listener: () => void): unknown;
38
+ exitCode?: number | null;
39
+ signalCode?: NodeJS.Signals | null;
40
+ };
41
+ }
42
+ export interface ReapResult {
43
+ /** False if any stage failed. Callers that treat a complete teardown as a
44
+ * PRECONDITION (the --install hand-off does) must refuse to continue on false
45
+ * rather than install over a socket that may still be open. */
46
+ ok: boolean;
47
+ failures: string[];
48
+ }
49
+ /**
50
+ * Tear everything down, in the one order that is safe.
51
+ *
52
+ * CHANNEL FIRST. Closing it stops new inbound before anything else is dismantled.
53
+ * With the channel last, the WebSocket keeps delivering room and DM traffic into a
54
+ * session whose driver is already dying, for as long as the child takes to exit —
55
+ * turn failures and dropped messages inside the very window this function exists
56
+ * to make empty. `stop()` also flushes pending ACKs, which it can only do over a
57
+ * socket that is still up.
58
+ *
59
+ * Every stage is guarded separately: one failure must not strand the others. A
60
+ * driver that throws must never leave the WebSocket open.
61
+ */
62
+ export declare function reap(r: Reapable, opts?: {
63
+ killTimeoutMs?: number;
64
+ log?: (m: string) => void;
65
+ }): Promise<ReapResult>;
66
+ //# sourceMappingURL=reap.d.ts.map
@@ -0,0 +1,57 @@
1
+ import { type BridgeConfig } from "../config.js";
2
+ import type { ReapResult } from "../reap.js";
3
+ import { type ServiceBackend } from "./backend.js";
4
+ export interface InstallOnReadyDeps {
5
+ entrypoint: string;
6
+ env: NodeJS.ProcessEnv;
7
+ backend?: ServiceBackend;
8
+ hasCreds?: (dataDir: string) => boolean;
9
+ /** Contents of the installed launcher, or undefined when none is installed. */
10
+ readLauncher?: (path: string) => string | undefined;
11
+ fenceAuthExists?: (fenceHome: string) => boolean;
12
+ /** Resolves when no turn is in flight (GrokSession.idle). See rule 2. */
13
+ waitForIdle?: () => Promise<void>;
14
+ /** Bound on that wait. The terminal coming back is the product promise, so a
15
+ * wedged turn must never hold the customer's shell open. */
16
+ idleTimeoutMs?: number;
17
+ /** The ONE teardown (src/reap.ts). Must complete before launchctl runs. */
18
+ reap: () => Promise<ReapResult>;
19
+ exit: (code: number) => void;
20
+ log: (msg: string) => void;
21
+ }
22
+ interface ReadyChannel {
23
+ on(ev: "ready", cb: () => void): unknown;
24
+ }
25
+ /**
26
+ * One-paste install: enrol in the foreground, hand off to the supervisor, exit —
27
+ * so the customer's terminal comes back and the agent survives a reboot.
28
+ *
29
+ * Four rules this module exists to enforce.
30
+ *
31
+ * 1. REFUSE BEFORE REAPING. Everything that can say no does so before anything is
32
+ * torn down. Refusing afterwards leaves the customer with an approved device,
33
+ * a spent invite token, and no running process — strictly worse than never
34
+ * having guarded at all.
35
+ *
36
+ * 2. REAP COMPLETELY, THEN BOOTSTRAP. `LaunchdBackend.install` runs bootout →
37
+ * poll-until-gone → bootstrap → kickstart synchronously, so the service is
38
+ * live the instant install returns. A partial teardown means two ACP sessions
39
+ * and two WebSockets on one device. `reap()` reports failure rather than
40
+ * swallowing it, and a failed reap is a refusal.
41
+ *
42
+ * 3. COMPARE, DON'T DETECT. `status().loaded` means only that a plist with this
43
+ * label exists. The old `install` subcommand never guarded its entrypoint
44
+ * (subcommand.ts), so a customer can hold a LOADED plist baked against a
45
+ * pruned npx cache; restarting that repairs nothing and reports success.
46
+ * Restart only when a service is loaded AND its baked entrypoint is the one we
47
+ * would install. Compare the ENTRYPOINT, never the whole launcher script —
48
+ * that embeds the install-time PATH and is volatile across shells.
49
+ *
50
+ * 4. VERIFY BEFORE CLAIMING. `backend.install` returns void, and launchd's
51
+ * implementation runs `kickstart` regardless of every `bootstrap` exit code.
52
+ * This log line is the customer's only feedback, so it comes after a
53
+ * `status()` that returned loaded:true.
54
+ */
55
+ export declare function installOnReady(channel: ReadyChannel, cfg: BridgeConfig, deps: InstallOnReadyDeps): void;
56
+ export {};
57
+ //# sourceMappingURL=install-on-ready.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-on-ready.d.ts","sourceRoot":"","sources":["../../src/service/install-on-ready.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4C,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAC3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,+EAA+E;IAC/E,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACpD,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC;iEAC6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IAChC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B;AAMD,UAAU,YAAY;IACpB,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EACrB,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,kBAAkB,GACvB,IAAI,CAgHN"}
@@ -13,4 +13,23 @@ export declare function renderLauncher(opts: {
13
13
  /** Write the launcher executable (0755) into the data-dir before the backend
14
14
  * points the service at it. Shared by both platform backends. */
15
15
  export declare function writeLauncher(deps: BackendDeps, spec: ServiceSpec): void;
16
+ /**
17
+ * Read back the entrypoint `renderLauncher` baked into an installed launcher.
18
+ *
19
+ * THE READER LIVES BESIDE THE WRITER ON PURPOSE. This pair decides
20
+ * restart-versus-reinstall, and a parser that drifts from the renderer fails
21
+ * SILENTLY — it just answers `undefined` forever, which the caller reads as
22
+ * "stale", which means every run re-installs and silently re-bakes the plist.
23
+ *
24
+ * WHY PARSE RATHER THAN BYTE-COMPARE THE SCRIPT. `renderLauncher` embeds the
25
+ * INSTALL-TIME PATH (`export PATH='<installPath>':"$PATH"`), taken from whichever
26
+ * shell ran the install. A whole-file comparison therefore reports "different"
27
+ * whenever the customer opens a new terminal, installs a brew formula, or
28
+ * switches node version manager — making the config-losing re-install the DEFAULT
29
+ * path. The entrypoint is the durable half, and the only half that can go stale
30
+ * (an npm cache gets pruned; the launcher keeps pointing at it).
31
+ *
32
+ * Returns undefined when there is no file, or when it is not one of ours.
33
+ */
34
+ export declare function parseLauncherEntrypoint(script: string | undefined): string | undefined;
16
35
  //# sourceMappingURL=launcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../../src/service/launcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKhD,eAAO,MAAM,cAAc,UAO1B,CAAC;AAcF;;;wEAGwE;AACxE,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,GAAG,MAAM,CAoBT;AAED;kEACkE;AAClE,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAIxE"}
1
+ {"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../../src/service/launcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKhD,eAAO,MAAM,cAAc,UAO1B,CAAC;AAcF;;;wEAGwE;AACxE,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,GAAG,MAAM,CAoBT;AAED;kEACkE;AAClE,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAIxE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAOtF"}
package/package.json CHANGED
@@ -1,13 +1,21 @@
1
1
  {
2
2
  "name": "@agentvault/grok-bridge",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "AgentVault Grok Bridge — daemon bridging a Grok agent into E2E-encrypted AgentVault direct messages and rooms.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
- "bin": { "agentvault-grok-bridge": "dist/index.js" },
9
- "engines": { "node": ">=18" },
10
- "files": ["dist", "README.md", "!dist/*.map"],
8
+ "bin": {
9
+ "agentvault-grok-bridge": "dist/index.js"
10
+ },
11
+ "engines": {
12
+ "node": ">=18"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "!dist/*.map"
18
+ ],
11
19
  "scripts": {
12
20
  "build": "npm --prefix ../plugin run build && tsc --emitDeclarationOnly && node build.mjs",
13
21
  "start": "node dist/index.js",
@@ -16,7 +24,10 @@
16
24
  "typecheck": "tsc -p tsconfig.test.json",
17
25
  "prepublishOnly": "npm run build"
18
26
  },
19
- "dependencies": { "ws": "^8.16.0", "zod": "^4.3.6" },
27
+ "dependencies": {
28
+ "ws": "^8.16.0",
29
+ "zod": "^4.3.6"
30
+ },
20
31
  "devDependencies": {
21
32
  "@agentvault/agentvault": "file:../plugin",
22
33
  "esbuild": "^0.27.3",
@@ -24,6 +35,8 @@
24
35
  "vitest": "^2.0.0",
25
36
  "@types/node": "^20.0.0"
26
37
  },
27
- "publishConfig": { "access": "public" },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
28
41
  "license": "MIT"
29
42
  }