@agentvault/grok-bridge 0.1.0 → 0.2.1
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 +54 -0
- package/dist/config.d.ts +45 -0
- package/dist/index.js +290 -11
- package/dist/log.d.ts +12 -0
- package/dist/reap.d.ts +66 -0
- package/dist/service/install-on-ready.d.ts +57 -0
- package/dist/service/install-on-ready.d.ts.map +1 -0
- package/dist/service/launcher.d.ts +19 -0
- package/dist/service/launcher.d.ts.map +1 -1
- package/dist/service/subcommand.d.ts.map +1 -1
- package/package.json +19 -6
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,40 @@
|
|
|
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
|
+
/** Informational flags. Handled BEFORE loadConfig in index.ts, because asking
|
|
15
|
+
* "what version is this?" must not require an invite token. Listed here too so
|
|
16
|
+
* the strict unknown-flag check does not reject them. */
|
|
17
|
+
export declare const VERSION_FLAGS: readonly ["--version", "-v"];
|
|
18
|
+
export declare const HELP_FLAGS: readonly ["--help", "-h"];
|
|
19
|
+
/** Pure argv read for the informational flags. Deliberately independent of
|
|
20
|
+
* loadConfig: that throws without a token, and `--version` must answer anyway. */
|
|
21
|
+
export declare function parseInfoFlags(argv: string[]): {
|
|
22
|
+
showVersion: boolean;
|
|
23
|
+
showHelp: boolean;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Package-manager scratch caches. `service/subcommand.ts` bakes `process.argv[1]`
|
|
27
|
+
* into the launcher script, so a service installed from one of these points at a
|
|
28
|
+
* directory the package manager later prunes — it works today and dies weeks later
|
|
29
|
+
* with nothing naming the cause.
|
|
30
|
+
*
|
|
31
|
+
* A DENYLIST, not proof of durability: it covers npm's npx cache, pnpm/yarn `dlx`,
|
|
32
|
+
* `bunx`, and anything under the OS temp dir. Other ephemeral runners are knowingly
|
|
33
|
+
* unhandled.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isEphemeralEntrypoint(entrypoint: string): boolean;
|
|
36
|
+
/** Throw unless `entrypoint` is durable enough to bake into a supervised service. */
|
|
37
|
+
export declare function assertInstallableEntrypoint(entrypoint: string): void;
|
|
1
38
|
export declare function hasPersistedCreds(dataDir: string): boolean;
|
|
2
39
|
export declare function slugify(name: string): string;
|
|
3
40
|
export type DataDirSource = "explicit" | "per-agent";
|
|
@@ -38,6 +75,14 @@ export interface BridgeConfig {
|
|
|
38
75
|
* instructs the agent. It does not: given a bare message the model answered
|
|
39
76
|
* in prose and never called room__say, so the reply was discarded. */
|
|
40
77
|
systemPrompt?: string;
|
|
78
|
+
/** Set by --install: enrol in the foreground, install the supervised service,
|
|
79
|
+
* then EXIT so the customer's terminal returns. False leaves a bare run
|
|
80
|
+
* byte-for-byte as it behaves today. */
|
|
81
|
+
installAfterEnroll: boolean;
|
|
82
|
+
/** --version / -v. index.ts answers these before loadConfig runs. */
|
|
83
|
+
showVersion: boolean;
|
|
84
|
+
/** --help / -h. Same. */
|
|
85
|
+
showHelp: boolean;
|
|
41
86
|
}
|
|
42
87
|
export declare function loadConfig(env: NodeJS.ProcessEnv, argv?: string[]): BridgeConfig;
|
|
43
88
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,24 @@ 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 parseInfoFlags(argv) {
|
|
17
|
+
return {
|
|
18
|
+
showVersion: argv.some((a2) => VERSION_FLAGS.includes(a2)),
|
|
19
|
+
showHelp: argv.some((a2) => HELP_FLAGS.includes(a2))
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function isEphemeralEntrypoint(entrypoint) {
|
|
23
|
+
return entrypoint.includes("/_npx/") || entrypoint.includes("/dlx-") || entrypoint.includes("/.bun/install/cache/") || entrypoint.startsWith(tmpdir() + sep);
|
|
24
|
+
}
|
|
25
|
+
function assertInstallableEntrypoint(entrypoint) {
|
|
26
|
+
if (isEphemeralEntrypoint(entrypoint)) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`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:
|
|
29
|
+
npm i -g @agentvault/grok-bridge && agentvault-grok-bridge ` + INSTALL_FLAG
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
15
33
|
function hasRecoverableBackup(dataDir) {
|
|
16
34
|
try {
|
|
17
35
|
const parsed = JSON.parse(readFileSync2(join5(dataDir, BACKUP_FILE), "utf-8"));
|
|
@@ -35,6 +53,12 @@ function resolveDataDir(env) {
|
|
|
35
53
|
function loadConfig(env, argv = []) {
|
|
36
54
|
const { dataDir, source: dataDirSource } = resolveDataDir(env);
|
|
37
55
|
const inviteToken = (argv[0] && !argv[0].startsWith("-") ? argv[0] : "") || env.AV_INVITE_TOKEN || "";
|
|
56
|
+
const unknownFlags = argv.filter((a2) => a2.startsWith("-") && !KNOWN_FLAGS.has(a2));
|
|
57
|
+
if (unknownFlags.length) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`unknown option(s): ${unknownFlags.join(", ")} \u2014 supported flags are ${[...KNOWN_FLAGS].join(", ")}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
38
62
|
const workspaceDir = env.AV_WORKSPACE_DIR || join5(dirname(dataDir), "workspaces", slugify2(env.AV_AGENT_NAME ?? "grok"));
|
|
39
63
|
const wsReal = resolve2(workspaceDir);
|
|
40
64
|
const ddReal = resolve2(dataDir);
|
|
@@ -60,6 +84,10 @@ function loadConfig(env, argv = []) {
|
|
|
60
84
|
inviteToken,
|
|
61
85
|
dataDir,
|
|
62
86
|
dataDirSource,
|
|
87
|
+
// One-paste install (--install): enrol, hand off to the supervisor, exit.
|
|
88
|
+
// Default false keeps a bare `npx @agentvault/grok-bridge` run identical.
|
|
89
|
+
installAfterEnroll: argv.includes(INSTALL_FLAG),
|
|
90
|
+
...parseInfoFlags(argv),
|
|
63
91
|
apiUrl: env.AV_API_URL ?? "https://api.agentvault.chat",
|
|
64
92
|
// Identity shown to the agent's own session. Set this to the agent's
|
|
65
93
|
// AgentVault name via AV_AGENT_NAME (the native connect command passes it).
|
|
@@ -76,10 +104,14 @@ function loadConfig(env, argv = []) {
|
|
|
76
104
|
systemPrompt: env.AV_SYSTEM_PROMPT || void 0
|
|
77
105
|
};
|
|
78
106
|
}
|
|
79
|
-
var CRED_FILES, BACKUP_FILE;
|
|
107
|
+
var INSTALL_FLAG, VERSION_FLAGS, HELP_FLAGS, KNOWN_FLAGS, CRED_FILES, BACKUP_FILE;
|
|
80
108
|
var init_config = __esm({
|
|
81
109
|
"src/config.ts"() {
|
|
82
110
|
"use strict";
|
|
111
|
+
INSTALL_FLAG = "--install";
|
|
112
|
+
VERSION_FLAGS = ["--version", "-v"];
|
|
113
|
+
HELP_FLAGS = ["--help", "-h"];
|
|
114
|
+
KNOWN_FLAGS = /* @__PURE__ */ new Set([INSTALL_FLAG, ...VERSION_FLAGS, ...HELP_FLAGS]);
|
|
83
115
|
CRED_FILES = ["agentvault.json", "secure-channel.json"];
|
|
84
116
|
BACKUP_FILE = "agentvault.json.bak";
|
|
85
117
|
}
|
|
@@ -94,6 +126,9 @@ function stamp(message) {
|
|
|
94
126
|
function logLine(...args) {
|
|
95
127
|
console.error(stamp(format(...args)));
|
|
96
128
|
}
|
|
129
|
+
function printLine(text) {
|
|
130
|
+
console.log(text);
|
|
131
|
+
}
|
|
97
132
|
var init_log = __esm({
|
|
98
133
|
"src/log.ts"() {
|
|
99
134
|
"use strict";
|
|
@@ -130,6 +165,16 @@ function writeLauncher(deps, spec) {
|
|
|
130
165
|
deps.writeFile(spec.launcherPath, spec.launcherScript);
|
|
131
166
|
deps.chmod(spec.launcherPath, 493);
|
|
132
167
|
}
|
|
168
|
+
function parseLauncherEntrypoint(script) {
|
|
169
|
+
if (!script) return void 0;
|
|
170
|
+
const m2 = /^if n=\$\(command -v .+\); then exec "\$n" (.+) "\$@"; fi$/m.exec(script);
|
|
171
|
+
if (!m2) return void 0;
|
|
172
|
+
return unSq(m2[1]);
|
|
173
|
+
}
|
|
174
|
+
function unSq(quoted) {
|
|
175
|
+
if (quoted.length < 2 || !quoted.startsWith("'") || !quoted.endsWith("'")) return void 0;
|
|
176
|
+
return quoted.slice(1, -1).replace(/'\\''/g, "'");
|
|
177
|
+
}
|
|
133
178
|
var FALLBACK_NODES;
|
|
134
179
|
var init_launcher = __esm({
|
|
135
180
|
"src/service/launcher.ts"() {
|
|
@@ -353,6 +398,11 @@ var init_systemd = __esm({
|
|
|
353
398
|
});
|
|
354
399
|
|
|
355
400
|
// src/service/backend.ts
|
|
401
|
+
var backend_exports = {};
|
|
402
|
+
__export(backend_exports, {
|
|
403
|
+
defaultDeps: () => defaultDeps,
|
|
404
|
+
selectBackend: () => selectBackend
|
|
405
|
+
});
|
|
356
406
|
import { spawnSync } from "node:child_process";
|
|
357
407
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2, rmSync, existsSync as existsSync3, chmodSync as chmodSync4 } from "node:fs";
|
|
358
408
|
import { userInfo } from "node:os";
|
|
@@ -399,7 +449,17 @@ function labelFor(env) {
|
|
|
399
449
|
}
|
|
400
450
|
function maybeRunServiceSubcommand(argv, env, deps) {
|
|
401
451
|
const cmd = argv[2];
|
|
402
|
-
if (!cmd
|
|
452
|
+
if (!cmd) return false;
|
|
453
|
+
if (!SERVICE_CMDS.has(cmd)) {
|
|
454
|
+
const looksLikeToken = cmd.startsWith("av_tok_");
|
|
455
|
+
const looksLikeFlag = cmd.startsWith("-");
|
|
456
|
+
if (!looksLikeToken && !looksLikeFlag) {
|
|
457
|
+
throw new Error(
|
|
458
|
+
`unknown subcommand: ${cmd} \u2014 expected one of ${[...SERVICE_CMDS].join(", ")}, an invite token, or no argument at all`
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
403
463
|
const backend = deps?.backend ?? selectBackend(process.platform);
|
|
404
464
|
const entrypoint = deps?.entrypoint ?? process.argv[1];
|
|
405
465
|
const log = deps?.log ?? ((m2) => logLine("[grok-bridge] " + m2));
|
|
@@ -420,7 +480,7 @@ function maybeRunServiceSubcommand(argv, env, deps) {
|
|
|
420
480
|
path: env.PATH ?? ""
|
|
421
481
|
});
|
|
422
482
|
backend.install(spec);
|
|
423
|
-
log(`installed service ${spec.label} \u2014 logs: ${spec.
|
|
483
|
+
log(`installed service ${spec.label} \u2014 logs: ${spec.logErr}`);
|
|
424
484
|
log(`check it with: agentvault-grok-bridge status`);
|
|
425
485
|
} else if (cmd === "uninstall") {
|
|
426
486
|
const label = labelFor(env);
|
|
@@ -456,10 +516,103 @@ var init_subcommand = __esm({
|
|
|
456
516
|
}
|
|
457
517
|
});
|
|
458
518
|
|
|
519
|
+
// src/service/install-on-ready.ts
|
|
520
|
+
var install_on_ready_exports = {};
|
|
521
|
+
__export(install_on_ready_exports, {
|
|
522
|
+
installOnReady: () => installOnReady
|
|
523
|
+
});
|
|
524
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
|
|
525
|
+
import { join as join12 } from "node:path";
|
|
526
|
+
function installOnReady(channel, cfg, deps) {
|
|
527
|
+
const hasCreds = deps.hasCreds ?? hasPersistedCreds;
|
|
528
|
+
const readLauncher = deps.readLauncher ?? ((p2) => existsSync4(p2) ? readFileSync3(p2, "utf8") : void 0);
|
|
529
|
+
const fenceAuthExists = deps.fenceAuthExists ?? ((home) => existsSync4(join12(home, ".grok", "auth.json")));
|
|
530
|
+
let fired = false;
|
|
531
|
+
channel.on("ready", () => {
|
|
532
|
+
if (fired) return;
|
|
533
|
+
fired = true;
|
|
534
|
+
void (async () => {
|
|
535
|
+
try {
|
|
536
|
+
if (!hasCreds(cfg.dataDir)) {
|
|
537
|
+
throw new Error(
|
|
538
|
+
`enrolment did not persist credentials to ${cfg.dataDir} \u2014 refusing to install a service that would crash-loop`
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
if (!fenceAuthExists(cfg.fenceHome)) {
|
|
542
|
+
throw new Error(
|
|
543
|
+
"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."
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
if (deps.waitForIdle) {
|
|
547
|
+
const timeoutMs = deps.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
548
|
+
let timer;
|
|
549
|
+
const timedOut = await Promise.race([
|
|
550
|
+
deps.waitForIdle().then(() => false),
|
|
551
|
+
new Promise((r2) => {
|
|
552
|
+
timer = setTimeout(() => r2(true), timeoutMs);
|
|
553
|
+
})
|
|
554
|
+
]);
|
|
555
|
+
if (timer) clearTimeout(timer);
|
|
556
|
+
if (timedOut) {
|
|
557
|
+
deps.log(
|
|
558
|
+
`agent still busy after ${timeoutMs}ms \u2014 handing off anyway; that reply may be lost`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const reaped = await deps.reap();
|
|
563
|
+
if (!reaped.ok) {
|
|
564
|
+
throw new Error(
|
|
565
|
+
`teardown did not complete (${reaped.failures.join("; ")}) \u2014 refusing to install over a session that may still be open`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
const backend = deps.backend ?? selectBackend(process.platform);
|
|
569
|
+
const spec = buildServiceSpec(cfg, {
|
|
570
|
+
entrypoint: deps.entrypoint,
|
|
571
|
+
home: deps.env.HOME ?? "",
|
|
572
|
+
path: deps.env.PATH ?? ""
|
|
573
|
+
});
|
|
574
|
+
const installedEntrypoint = parseLauncherEntrypoint(readLauncher(spec.launcherPath));
|
|
575
|
+
const entrypointIsCurrent = installedEntrypoint !== void 0 && !isEphemeralEntrypoint(installedEntrypoint) && installedEntrypoint === deps.entrypoint;
|
|
576
|
+
if (backend.status(spec.label).loaded && entrypointIsCurrent) {
|
|
577
|
+
backend.restart(spec.label);
|
|
578
|
+
deps.log(`service ${spec.label} already installed and current \u2014 restarted`);
|
|
579
|
+
} else {
|
|
580
|
+
backend.install(spec);
|
|
581
|
+
deps.log(
|
|
582
|
+
installedEntrypoint && !entrypointIsCurrent ? `service ${spec.label} pointed at a stale entrypoint (${installedEntrypoint}) \u2014 re-installed` : `installed service ${spec.label}`
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
const after = backend.status(spec.label);
|
|
586
|
+
if (!after.loaded) {
|
|
587
|
+
throw new Error(`service did not load: ${after.detail}`);
|
|
588
|
+
}
|
|
589
|
+
deps.log(`it is running now. logs: ${spec.logErr}`);
|
|
590
|
+
deps.log(`check it: agentvault-grok-bridge status`);
|
|
591
|
+
deps.log(`restart it: agentvault-grok-bridge restart (never 'install')`);
|
|
592
|
+
deps.exit(0);
|
|
593
|
+
} catch (e) {
|
|
594
|
+
deps.log(`--install failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
595
|
+
deps.exit(1);
|
|
596
|
+
}
|
|
597
|
+
})();
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
var DEFAULT_IDLE_TIMEOUT_MS;
|
|
601
|
+
var init_install_on_ready = __esm({
|
|
602
|
+
"src/service/install-on-ready.ts"() {
|
|
603
|
+
"use strict";
|
|
604
|
+
init_config();
|
|
605
|
+
init_spec();
|
|
606
|
+
init_launcher();
|
|
607
|
+
init_backend();
|
|
608
|
+
DEFAULT_IDLE_TIMEOUT_MS = 3e4;
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
|
|
459
612
|
// src/index.ts
|
|
460
613
|
import { spawn } from "node:child_process";
|
|
461
614
|
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
462
|
-
import { join as
|
|
615
|
+
import { join as join13 } from "node:path";
|
|
463
616
|
|
|
464
617
|
// ../plugin/dist/index.js
|
|
465
618
|
import * as nc from "node:crypto";
|
|
@@ -69938,7 +70091,8 @@ ${messageText}`;
|
|
|
69938
70091
|
const candidateCount = candidates.filter(Boolean).length;
|
|
69939
70092
|
const joined = await joinWithFirstMatchingKp(welcomeBytes, candidates);
|
|
69940
70093
|
if (!joined) {
|
|
69941
|
-
console.error(`[SecureChannel] No matching KeyPackage found for Welcome \u2014 cannot join group ${groupId?.slice(0, 8)}. The Welcome is encrypted to a published KP's public key; none of our ${candidateCount} candidate bundle(s) decrypted it.
|
|
70094
|
+
console.error(`[SecureChannel] No matching KeyPackage found for Welcome \u2014 cannot join group ${groupId?.slice(0, 8)}. The Welcome is encrypted to a published KP's public key; none of our ${candidateCount} candidate bundle(s) decrypted it. Requesting a re-Welcome with a fresh KeyPackage.`);
|
|
70095
|
+
if (groupId) void this._requestWelcomeSelfHeal(groupId);
|
|
69942
70096
|
return;
|
|
69943
70097
|
}
|
|
69944
70098
|
const mgr = joined.mgr;
|
|
@@ -98846,16 +99000,76 @@ function buildClientVersion2(version2) {
|
|
|
98846
99000
|
return `@agentvault/grok-bridge@${version2 ?? "dev"}`;
|
|
98847
99001
|
}
|
|
98848
99002
|
|
|
99003
|
+
// src/reap.ts
|
|
99004
|
+
var DEFAULT_KILL_TIMEOUT_MS = 5e3;
|
|
99005
|
+
var SIGKILL_GRACE_MS = 250;
|
|
99006
|
+
function childHasExited(child) {
|
|
99007
|
+
return child.exitCode !== null && child.exitCode !== void 0 || child.signalCode !== null && child.signalCode !== void 0;
|
|
99008
|
+
}
|
|
99009
|
+
function waitForChildExit(child, killTimeoutMs, log) {
|
|
99010
|
+
if (childHasExited(child)) return Promise.resolve();
|
|
99011
|
+
return new Promise((resolve3) => {
|
|
99012
|
+
let settled = false;
|
|
99013
|
+
const timers = [];
|
|
99014
|
+
const done = () => {
|
|
99015
|
+
if (settled) return;
|
|
99016
|
+
settled = true;
|
|
99017
|
+
for (const t2 of timers) clearTimeout(t2);
|
|
99018
|
+
resolve3();
|
|
99019
|
+
};
|
|
99020
|
+
child.on("exit", done);
|
|
99021
|
+
timers.push(
|
|
99022
|
+
setTimeout(() => {
|
|
99023
|
+
log(`grok child ignored SIGTERM after ${killTimeoutMs}ms \u2014 escalating to SIGKILL`);
|
|
99024
|
+
try {
|
|
99025
|
+
child.kill?.("SIGKILL");
|
|
99026
|
+
} catch {
|
|
99027
|
+
}
|
|
99028
|
+
timers.push(setTimeout(done, SIGKILL_GRACE_MS));
|
|
99029
|
+
}, killTimeoutMs)
|
|
99030
|
+
);
|
|
99031
|
+
});
|
|
99032
|
+
}
|
|
99033
|
+
async function reap(r2, opts = {}) {
|
|
99034
|
+
const log = opts.log ?? (() => {
|
|
99035
|
+
});
|
|
99036
|
+
const killTimeoutMs = opts.killTimeoutMs ?? DEFAULT_KILL_TIMEOUT_MS;
|
|
99037
|
+
const failures = [];
|
|
99038
|
+
const attempt = async (label, fn) => {
|
|
99039
|
+
try {
|
|
99040
|
+
await fn();
|
|
99041
|
+
} catch (e) {
|
|
99042
|
+
const msg = `${label}: ${e instanceof Error ? e.message : String(e)}`;
|
|
99043
|
+
failures.push(msg);
|
|
99044
|
+
log(`reap: ${msg}`);
|
|
99045
|
+
}
|
|
99046
|
+
};
|
|
99047
|
+
if (r2.channel) await attempt("channel.stop", () => r2.channel.stop());
|
|
99048
|
+
if (r2.driver) await attempt("driver.stop", () => r2.driver.stop());
|
|
99049
|
+
if (r2.child) await waitForChildExit(r2.child, killTimeoutMs, log);
|
|
99050
|
+
if (r2.mcp) await attempt("mcp.close", () => r2.mcp.close());
|
|
99051
|
+
return { ok: failures.length === 0, failures };
|
|
99052
|
+
}
|
|
99053
|
+
|
|
98849
99054
|
// src/index.ts
|
|
98850
99055
|
var shuttingDown = false;
|
|
99056
|
+
var exiting = false;
|
|
98851
99057
|
var driver;
|
|
98852
99058
|
var mcp;
|
|
98853
|
-
var
|
|
98854
|
-
|
|
99059
|
+
var channelRef;
|
|
99060
|
+
var childRef;
|
|
99061
|
+
var reapAll = async () => {
|
|
98855
99062
|
shuttingDown = true;
|
|
99063
|
+
return reap(
|
|
99064
|
+
{ channel: channelRef, driver, mcp, child: childRef },
|
|
99065
|
+
{ log: (m2) => logLine("[grok-bridge] " + m2) }
|
|
99066
|
+
);
|
|
99067
|
+
};
|
|
99068
|
+
var shutdown = async (code = 0) => {
|
|
99069
|
+
if (exiting) return;
|
|
99070
|
+
exiting = true;
|
|
98856
99071
|
try {
|
|
98857
|
-
await
|
|
98858
|
-
await mcp?.close();
|
|
99072
|
+
await reapAll();
|
|
98859
99073
|
} finally {
|
|
98860
99074
|
process.exit(code);
|
|
98861
99075
|
}
|
|
@@ -98867,7 +99081,53 @@ async function main() {
|
|
|
98867
99081
|
})) {
|
|
98868
99082
|
process.exit(process.exitCode ?? 0);
|
|
98869
99083
|
}
|
|
99084
|
+
const info = parseInfoFlags(process.argv.slice(2));
|
|
99085
|
+
if (info.showVersion || info.showHelp) {
|
|
99086
|
+
const version2 = buildClientVersion2(
|
|
99087
|
+
true ? "0.2.1" : void 0
|
|
99088
|
+
);
|
|
99089
|
+
if (info.showVersion) printLine(version2);
|
|
99090
|
+
if (info.showHelp) {
|
|
99091
|
+
printLine(
|
|
99092
|
+
[
|
|
99093
|
+
"",
|
|
99094
|
+
`${version2}`,
|
|
99095
|
+
"",
|
|
99096
|
+
" Bridges a Grok agent into E2E-encrypted AgentVault DMs and rooms.",
|
|
99097
|
+
"",
|
|
99098
|
+
"USAGE",
|
|
99099
|
+
" agentvault-grok-bridge [<invite-token>] [--install]",
|
|
99100
|
+
" agentvault-grok-bridge <install|uninstall|restart|status|logs>",
|
|
99101
|
+
"",
|
|
99102
|
+
"FLAGS",
|
|
99103
|
+
" --install Enrol, install a supervised OS service, then EXIT so",
|
|
99104
|
+
" your terminal returns. Requires a global install:",
|
|
99105
|
+
" npm i -g @agentvault/grok-bridge",
|
|
99106
|
+
" --version, -v Print the version.",
|
|
99107
|
+
" --help, -h Print this.",
|
|
99108
|
+
"",
|
|
99109
|
+
"ENV",
|
|
99110
|
+
" AV_AGENT_NAME The agent's AgentVault name. Required.",
|
|
99111
|
+
" AV_INVITE_TOKEN First enrolment only; a saved data dir reconnects without it.",
|
|
99112
|
+
" AV_DATA_DIR Credential/state dir. Defaults per agent.",
|
|
99113
|
+
"",
|
|
99114
|
+
"NOTES",
|
|
99115
|
+
" Run `grok login` first. XAI_API_KEY from your shell is NOT inherited by a",
|
|
99116
|
+
" supervised service, so --install refuses rather than run mute.",
|
|
99117
|
+
" To restart later use `restart`, never `install` \u2014 install re-bakes the",
|
|
99118
|
+
" service from your current shell and drops env from the original install.",
|
|
99119
|
+
""
|
|
99120
|
+
].join("\n")
|
|
99121
|
+
);
|
|
99122
|
+
}
|
|
99123
|
+
process.exit(0);
|
|
99124
|
+
}
|
|
98870
99125
|
const cfg = loadConfig(process.env, process.argv.slice(2));
|
|
99126
|
+
if (cfg.installAfterEnroll) {
|
|
99127
|
+
assertInstallableEntrypoint(process.argv[1]);
|
|
99128
|
+
const { selectBackend: selectBackend2 } = await Promise.resolve().then(() => (init_backend(), backend_exports));
|
|
99129
|
+
selectBackend2(process.platform);
|
|
99130
|
+
}
|
|
98871
99131
|
mkdirSync5(cfg.workspaceDir, { recursive: true });
|
|
98872
99132
|
mkdirSync5(cfg.fenceHome, { recursive: true });
|
|
98873
99133
|
seedFenceCredential({
|
|
@@ -98882,9 +99142,10 @@ async function main() {
|
|
|
98882
99142
|
agentName: cfg.agentName,
|
|
98883
99143
|
platform: "node",
|
|
98884
99144
|
clientVersion: buildClientVersion2(
|
|
98885
|
-
true ? "0.1
|
|
99145
|
+
true ? "0.2.1" : void 0
|
|
98886
99146
|
)
|
|
98887
99147
|
});
|
|
99148
|
+
channelRef = channel;
|
|
98888
99149
|
let session;
|
|
98889
99150
|
mcp = await startRoomMcpServer({
|
|
98890
99151
|
// `session` is assigned further down. The current ordering is safe — grok
|
|
@@ -98902,7 +99163,7 @@ async function main() {
|
|
|
98902
99163
|
},
|
|
98903
99164
|
log: (m2) => logLine(m2)
|
|
98904
99165
|
});
|
|
98905
|
-
const pluginDir =
|
|
99166
|
+
const pluginDir = join13(cfg.fenceHome, "av-room-plugin");
|
|
98906
99167
|
writeRoomPluginHttp(pluginDir, mcp.url);
|
|
98907
99168
|
const spawnSpec = grokSpawnArgs({
|
|
98908
99169
|
grokPath: cfg.grokPath,
|
|
@@ -98917,6 +99178,7 @@ async function main() {
|
|
|
98917
99178
|
env: spawnSpec.env,
|
|
98918
99179
|
stdio: ["pipe", "pipe", "pipe"]
|
|
98919
99180
|
});
|
|
99181
|
+
childRef = child;
|
|
98920
99182
|
child.on("error", (err) => {
|
|
98921
99183
|
logLine(`[grok-bridge] fatal: ${describeSpawnFailure(err, spawnSpec)}`);
|
|
98922
99184
|
process.exit(1);
|
|
@@ -98966,6 +99228,23 @@ async function main() {
|
|
|
98966
99228
|
log: (m2) => logLine("[grok-bridge] " + m2),
|
|
98967
99229
|
onTerminal: (_reason, o2) => void shutdown(o2.restart ? 1 : 0)
|
|
98968
99230
|
});
|
|
99231
|
+
if (cfg.installAfterEnroll) {
|
|
99232
|
+
const { installOnReady: installOnReady2 } = await Promise.resolve().then(() => (init_install_on_ready(), install_on_ready_exports));
|
|
99233
|
+
installOnReady2(channel, cfg, {
|
|
99234
|
+
entrypoint: process.argv[1],
|
|
99235
|
+
env: process.env,
|
|
99236
|
+
// FOUND LIVE 2026-08-16: the owner's activation greeting is usually already
|
|
99237
|
+
// being answered when `ready` fires, and reaping mid-turn killed the reply
|
|
99238
|
+
// for good — the message was acked, so the service never saw it again.
|
|
99239
|
+
// GrokSession.idle() resolves when the queue drains.
|
|
99240
|
+
waitForIdle: () => session.idle(),
|
|
99241
|
+
// The one shared teardown. `exiting` is deliberately NOT set here: Ctrl-C
|
|
99242
|
+
// must keep working for the whole multi-second launchctl window.
|
|
99243
|
+
reap: reapAll,
|
|
99244
|
+
exit: (code) => process.exit(code),
|
|
99245
|
+
log: (m2) => logLine("[grok-bridge] " + m2)
|
|
99246
|
+
});
|
|
99247
|
+
}
|
|
98969
99248
|
await channel.start();
|
|
98970
99249
|
}
|
|
98971
99250
|
main().catch(async (err) => {
|
package/dist/log.d.ts
CHANGED
|
@@ -19,4 +19,16 @@ export declare function stamp(message: string): string;
|
|
|
19
19
|
* just anchored to a clock.
|
|
20
20
|
*/
|
|
21
21
|
export declare function logLine(...args: unknown[]): void;
|
|
22
|
+
/**
|
|
23
|
+
* Plain stdout, no timestamp. For CLI answers a human or a script reads —
|
|
24
|
+
* `--version` and `--help` — not for daemon logging.
|
|
25
|
+
*
|
|
26
|
+
* `logLine` stamps every line and writes to stderr, which is right for a daemon
|
|
27
|
+
* and wrong here: `agentvault-grok-bridge --version` must print a version, not a
|
|
28
|
+
* version wearing a date. Anything a script would parse belongs on stdout.
|
|
29
|
+
*
|
|
30
|
+
* This lives in log.ts because the "no bare console sinks" guard test excludes
|
|
31
|
+
* only this file — the sink belongs here, and nowhere else.
|
|
32
|
+
*/
|
|
33
|
+
export declare function printLine(text: string): void;
|
|
22
34
|
//# sourceMappingURL=log.d.ts.map
|
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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subcommand.d.ts","sourceRoot":"","sources":["../../src/service/subcommand.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAQD;;uFAEuF;AACvF,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,
|
|
1
|
+
{"version":3,"file":"subcommand.d.ts","sourceRoot":"","sources":["../../src/service/subcommand.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAQD;;uFAEuF;AACvF,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAoFT"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/grok-bridge",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
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": {
|
|
9
|
-
|
|
10
|
-
|
|
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": {
|
|
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": {
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
28
41
|
"license": "MIT"
|
|
29
42
|
}
|