@agentvault/claude-bridge 0.7.16 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge.d.ts +9 -0
- package/dist/config.d.ts +44 -0
- package/dist/index.js +629 -70
- package/dist/log.d.ts +12 -0
- package/dist/pending-invite.d.ts +24 -0
- package/dist/service/backend.d.ts +15 -0
- package/dist/service/backend.d.ts.map +1 -1
- package/dist/service/doctor-cli.d.ts +36 -0
- package/dist/service/doctor-cli.d.ts.map +1 -0
- package/dist/service/doctor.d.ts +93 -0
- package/dist/service/doctor.d.ts.map +1 -0
- package/dist/service/launchd.d.ts +27 -1
- package/dist/service/launchd.d.ts.map +1 -1
- package/dist/service/self-uninstall.d.ts +19 -0
- package/dist/service/self-uninstall.d.ts.map +1 -0
- package/dist/service/subcommand.d.ts +12 -0
- package/dist/service/subcommand.d.ts.map +1 -1
- package/dist/service/systemd.d.ts +19 -1
- package/dist/service/systemd.d.ts.map +1 -1
- package/package.json +1 -1
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
|
+
* `doctor`'s report — not for daemon logging.
|
|
25
|
+
*
|
|
26
|
+
* `logLine` stamps every line and writes to stderr, which is right for a daemon
|
|
27
|
+
* and wrong here: a report a customer is meant to read must not wear a date on
|
|
28
|
+
* every row. Anything a script would parse belongs on stdout.
|
|
29
|
+
*
|
|
30
|
+
* This lives in log.ts because the "no bare console sinks" guard excludes only
|
|
31
|
+
* 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
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare function pendingInvitePath(dataDir: string): string;
|
|
2
|
+
/** Persist the token for the service's first boot. Owner-only, unconditionally. */
|
|
3
|
+
export declare function writePendingInvite(dataDir: string, token: string): void;
|
|
4
|
+
/** The pending token, or undefined when there is none to consume. */
|
|
5
|
+
export declare function readPendingInvite(dataDir: string): string | undefined;
|
|
6
|
+
/** Remove it. Idempotent: nothing to remove is a success, not an error. */
|
|
7
|
+
export declare function clearPendingInvite(dataDir: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Delete the pending invite once there is EVIDENCE it was consumed — i.e.
|
|
10
|
+
* credentials have actually persisted to the data dir.
|
|
11
|
+
*
|
|
12
|
+
* Timing is the whole design here. Deleting on READ (inside loadConfig) would
|
|
13
|
+
* throw the token away before enrolment finished, so a process that died in
|
|
14
|
+
* between would leave a service that can never enrol and a customer whose
|
|
15
|
+
* single-use invite is gone. Never deleting leaves a credential on disk long
|
|
16
|
+
* after it is useless.
|
|
17
|
+
*
|
|
18
|
+
* `credsExist` is injected so the caller supplies `hasPersistedCreds` and this
|
|
19
|
+
* module stays free of a config import cycle.
|
|
20
|
+
*
|
|
21
|
+
* @returns true when a file was actually removed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function clearPendingInviteIfConsumed(dataDir: string, credsExist: (dataDir: string) => boolean): boolean;
|
|
24
|
+
//# sourceMappingURL=pending-invite.d.ts.map
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import type { ServiceSpec } from "./spec.js";
|
|
2
|
+
/** One installed service found on disk, with the env its definition bakes in. */
|
|
3
|
+
export interface InstalledService {
|
|
4
|
+
label: string;
|
|
5
|
+
/** Absolute path of the plist / unit file. */
|
|
6
|
+
path: string;
|
|
7
|
+
env: Record<string, string>;
|
|
8
|
+
}
|
|
2
9
|
export interface ServiceBackend {
|
|
3
10
|
install(spec: ServiceSpec): void;
|
|
4
11
|
uninstall(label: string): void;
|
|
12
|
+
/** #913 item 3 — every installed service whose label starts with `prefix`,
|
|
13
|
+
* read from the definition DIRECTORY, not from the running-service list. An
|
|
14
|
+
* orphan is installed-but-not-running by construction. */
|
|
15
|
+
list(prefix: string): InstalledService[];
|
|
5
16
|
/** Restart an ALREADY-INSTALLED service without touching its spec (#837).
|
|
6
17
|
* Distinct from install, which re-bakes the plist from the current shell. */
|
|
7
18
|
restart(label: string): void;
|
|
@@ -23,6 +34,10 @@ export interface BackendDeps {
|
|
|
23
34
|
chmod: (path: string, mode: number) => void;
|
|
24
35
|
rm: (path: string) => void;
|
|
25
36
|
exists: (path: string) => boolean;
|
|
37
|
+
/** Names in a directory. Returns [] when the directory does not exist — a
|
|
38
|
+
* machine with no services installed is not an error. */
|
|
39
|
+
readdir: (path: string) => string[];
|
|
40
|
+
readFile: (path: string) => string;
|
|
26
41
|
}
|
|
27
42
|
export declare function defaultDeps(): BackendDeps;
|
|
28
43
|
export declare function selectBackend(platform: NodeJS.Platform, deps?: BackendDeps): ServiceBackend;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/service/backend.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;kFAC8E;IAC9E,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/service/backend.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;+DAE2D;IAC3D,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAAC;IACzC;kFAC8E;IAC9E,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC;8DAC0D;IAC1D,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACpC;AAED,wBAAgB,WAAW,IAAI,WAAW,CAoCzC;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,cAAc,CAU3F"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type DeviceProbe, type DoctorDeps } from "./doctor.js";
|
|
2
|
+
/**
|
|
3
|
+
* Ask the public status endpoint about one device.
|
|
4
|
+
*
|
|
5
|
+
* `GET /api/v1/devices/{id}/status` needs no auth and is rate limited per
|
|
6
|
+
* device_id, so checking several agents in a row is fine.
|
|
7
|
+
*
|
|
8
|
+
* ⚠️ A 404 is the strongest claim this command makes — it is what turns into
|
|
9
|
+
* "orphan, run uninstall". So it is only believed when the body is JSON, i.e.
|
|
10
|
+
* an actual API error shape. `agentvault.dev` serves a GoDaddy parked page that
|
|
11
|
+
* answers with confident HTTP codes and no API behind it, and on 2026-07-25
|
|
12
|
+
* that fooled a smoke check into reporting a healthy backend. A wrong
|
|
13
|
+
* AV_API_URL must read as "I could not check", never as "all your agents are
|
|
14
|
+
* dead".
|
|
15
|
+
*/
|
|
16
|
+
export declare function fetchDeviceStatus(apiUrl: string, deviceId: string, doFetch?: typeof fetch): Promise<DeviceProbe>;
|
|
17
|
+
export declare function realDoctorDeps(env: NodeJS.ProcessEnv): DoctorDeps;
|
|
18
|
+
/**
|
|
19
|
+
* Handle `agentvault-claude-bridge doctor`. Returns true when handled.
|
|
20
|
+
*
|
|
21
|
+
* Async, which is why it does not live in maybeRunServiceSubcommand: that
|
|
22
|
+
* dispatcher is synchronous and doctor has to await the network. index.ts runs
|
|
23
|
+
* this FIRST. If it ever did not, `doctor` would fall through to the
|
|
24
|
+
* unknown-subcommand guard and throw — loud, and safe. A typo must never boot a
|
|
25
|
+
* daemon (#921), and neither must a command we forgot to wire.
|
|
26
|
+
*
|
|
27
|
+
* READ-ONLY by construction: it never calls install, uninstall or restart. It
|
|
28
|
+
* prints the command instead, because deciding to remove a customer's service
|
|
29
|
+
* is theirs to make with the evidence in front of them.
|
|
30
|
+
*/
|
|
31
|
+
export declare function maybeRunDoctorSubcommand(argv: string[], opts?: {
|
|
32
|
+
log?: (msg: string) => void;
|
|
33
|
+
deps?: DoctorDeps;
|
|
34
|
+
env?: NodeJS.ProcessEnv;
|
|
35
|
+
}): Promise<boolean>;
|
|
36
|
+
//# sourceMappingURL=doctor-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-cli.d.ts","sourceRoot":"","sources":["../../src/service/doctor-cli.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAsBrB;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,WAAW,CAAC,CAuBtB;AA+BD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,UAAU,CAsBjE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAO,GACrF,OAAO,CAAC,OAAO,CAAC,CAelB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ServiceBackend } from "./backend.js";
|
|
2
|
+
/**
|
|
3
|
+
* #913 item 3 — `doctor`: list what is installed, check each against the API,
|
|
4
|
+
* and say which ones are orphaned.
|
|
5
|
+
*
|
|
6
|
+
* Deleting an agent in the dashboard removes the DEVICE ROW and nothing else.
|
|
7
|
+
* The service stays installed, pointing at a device that no longer exists, and
|
|
8
|
+
* the customer has no signal at all. Item 1 names the uninstall command AT
|
|
9
|
+
* DELETE TIME; item 2 removes the service when the running bridge is told the
|
|
10
|
+
* device is gone. Neither helps the machine that already accumulated orphans —
|
|
11
|
+
* from a delete done in another browser, before those shipped, or while the
|
|
12
|
+
* bridge was not running. That is what this reads.
|
|
13
|
+
*
|
|
14
|
+
* TWO RULES, both earned the hard way:
|
|
15
|
+
*
|
|
16
|
+
* 1. AN UNREACHABLE API IS NOT AN ORPHAN. #919: a default for a missing input is
|
|
17
|
+
* a guess, and reporting on it is a lie. Every inconclusive probe is SKIP,
|
|
18
|
+
* never FAIL — including a status string this build does not recognise.
|
|
19
|
+
* Printing FAIL on a network blip sends a customer to uninstall a healthy
|
|
20
|
+
* agent.
|
|
21
|
+
*
|
|
22
|
+
* 2. SILENCE ABOUT WHAT IT CANNOT SEE IS A FALSE ALL-CLEAR. #941: the delete
|
|
23
|
+
* modal covered 3 of 15 devices and said nothing for the other 12, which
|
|
24
|
+
* reads as "nothing to clean up". This command can only check its own family
|
|
25
|
+
* — the agents whose credentials it can read — so it LISTS the other
|
|
26
|
+
* AgentVault services it found and says it did not check them.
|
|
27
|
+
*/
|
|
28
|
+
export type Verdict = "PASS" | "WARN" | "FAIL" | "SKIP";
|
|
29
|
+
/** What the server said about one device. Exactly one field is meaningful. */
|
|
30
|
+
export interface DeviceProbe {
|
|
31
|
+
/** The device's status string, verbatim from the API. */
|
|
32
|
+
status?: string;
|
|
33
|
+
/** 404 — the device row does not exist. */
|
|
34
|
+
gone?: boolean;
|
|
35
|
+
/** Why the check could not conclude. Present => the answer is UNKNOWN. */
|
|
36
|
+
inconclusive?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ServiceFinding {
|
|
39
|
+
label: string;
|
|
40
|
+
agentName: string;
|
|
41
|
+
dataDir: string;
|
|
42
|
+
loaded: boolean;
|
|
43
|
+
deviceId: string;
|
|
44
|
+
verdict: Verdict;
|
|
45
|
+
detail: string;
|
|
46
|
+
/** The exact command that fixes it. Set ONLY for a confirmed orphan. */
|
|
47
|
+
fix?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface UnsupervisedFinding {
|
|
50
|
+
dataDir: string;
|
|
51
|
+
agentName: string;
|
|
52
|
+
deviceId: string;
|
|
53
|
+
verdict: Verdict;
|
|
54
|
+
detail: string;
|
|
55
|
+
/** How to bring a live agent back. Never a suggestion to delete key material:
|
|
56
|
+
* `uninstall` leaves credentials ON PURPOSE, and a doctor that says "remove
|
|
57
|
+
* this directory" turns a recoverable mistake into a lost identity. */
|
|
58
|
+
fix?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface DoctorReport {
|
|
61
|
+
labelPrefixes: string[];
|
|
62
|
+
services: ServiceFinding[];
|
|
63
|
+
/** Credentials on disk that no installed service points at — CHECKED against
|
|
64
|
+
* the API, not merely listed. "Leftover from a deleted agent" and "a live
|
|
65
|
+
* agent whose service was removed" look identical on disk and need opposite
|
|
66
|
+
* responses, so the verdict is the whole point. */
|
|
67
|
+
unsupervised: UnsupervisedFinding[];
|
|
68
|
+
/** AgentVault services from other families. Found, listed, NOT checked. */
|
|
69
|
+
notChecked: string[];
|
|
70
|
+
}
|
|
71
|
+
export interface DoctorDeps {
|
|
72
|
+
backend: ServiceBackend;
|
|
73
|
+
/** Every label prefix this bridge OWNS — plural because #837 renamed the
|
|
74
|
+
* claude label and pre-rename installs are still registered under the old
|
|
75
|
+
* one. Those are the longest-running services on customer machines, i.e. the
|
|
76
|
+
* ones most likely to have accumulated an orphan. */
|
|
77
|
+
labelPrefixes: string[];
|
|
78
|
+
base: string;
|
|
79
|
+
apiUrl: string;
|
|
80
|
+
uninstallCommand: (agentName: string) => string;
|
|
81
|
+
installCommand: (agentName: string) => string;
|
|
82
|
+
readCreds: (dataDir: string) => {
|
|
83
|
+
deviceId?: string;
|
|
84
|
+
} | null;
|
|
85
|
+
/** Absolute data dirs under `base` that hold credentials. */
|
|
86
|
+
credDirs: () => string[];
|
|
87
|
+
fetchStatus: (apiUrl: string, deviceId: string) => Promise<DeviceProbe>;
|
|
88
|
+
/** Labels belonging to other AgentVault families installed on this machine. */
|
|
89
|
+
otherLabels: () => string[];
|
|
90
|
+
}
|
|
91
|
+
export declare function buildDoctorReport(d: DoctorDeps): Promise<DoctorReport>;
|
|
92
|
+
export declare function formatDoctorReport(r: DoctorReport): string[];
|
|
93
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/service/doctor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAExD,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf;;4EAEwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B;;;wDAGoD;IACpD,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,2EAA2E;IAC3E,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,cAAc,CAAC;IACxB;;;0DAGsD;IACtD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7D,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,MAAM,EAAE,CAAC;IACzB,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACxE,+EAA+E;IAC/E,WAAW,EAAE,MAAM,MAAM,EAAE,CAAC;CAC7B;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAqB5E;AAiJD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,EAAE,CAqD5D"}
|
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
import type { ServiceSpec } from "./spec.js";
|
|
2
|
-
import type { BackendDeps, ServiceBackend } from "./backend.js";
|
|
2
|
+
import type { BackendDeps, ServiceBackend, InstalledService } from "./backend.js";
|
|
3
3
|
/** Render a launchd LaunchAgent plist for a bridge ServiceSpec. Pure string. */
|
|
4
4
|
export declare function renderLaunchdPlist(spec: ServiceSpec): string;
|
|
5
|
+
/**
|
|
6
|
+
* Read the EnvironmentVariables dict back out of an installed plist.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately tolerant of formatting, and deliberately NOT tolerant of scope.
|
|
9
|
+
*
|
|
10
|
+
* TOLERANT: `loopita`'s plist is hand-edited, so it keeps none of the
|
|
11
|
+
* whitespace `renderLaunchdPlist` writes. A parser that only understood its own
|
|
12
|
+
* output would report "no data dir" for precisely the long-lived install a
|
|
13
|
+
* customer is most likely to be asking `doctor` about.
|
|
14
|
+
*
|
|
15
|
+
* NOT TOLERANT: it reads ONLY the EnvironmentVariables dict. `Label`,
|
|
16
|
+
* `WorkingDirectory` and `StandardOutPath` are <key>/<string> pairs too, so a
|
|
17
|
+
* whole-file scan would hand back a log path as if it were AV_DATA_DIR and then
|
|
18
|
+
* go looking for credentials in it.
|
|
19
|
+
*
|
|
20
|
+
* Returns {} for junk. A doctor that throws on one malformed plist reports
|
|
21
|
+
* nothing about the other five.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parsePlistEnv(xml: string): Record<string, string>;
|
|
5
24
|
export declare class LaunchdBackend implements ServiceBackend {
|
|
6
25
|
private deps;
|
|
7
26
|
constructor(deps: BackendDeps);
|
|
@@ -17,5 +36,12 @@ export declare class LaunchdBackend implements ServiceBackend {
|
|
|
17
36
|
loaded: boolean;
|
|
18
37
|
detail: string;
|
|
19
38
|
};
|
|
39
|
+
/** #913 item 3 — every installed service whose label starts with `prefix`.
|
|
40
|
+
*
|
|
41
|
+
* Reads the plist directory rather than `launchctl list`, because an ORPHAN
|
|
42
|
+
* is typically installed-but-not-loaded: the revoked device exits 0 and
|
|
43
|
+
* KeepAlive leaves it down. `launchctl list` would not show the very thing
|
|
44
|
+
* doctor exists to find. */
|
|
45
|
+
list(prefix: string): InstalledService[];
|
|
20
46
|
}
|
|
21
47
|
//# sourceMappingURL=launchd.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launchd.d.ts","sourceRoot":"","sources":["../../src/service/launchd.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"launchd.d.ts","sourceRoot":"","sources":["../../src/service/launchd.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUlF,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAkC5D;AAGD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA6BjE;AAOD,qBAAa,cAAe,YAAW,cAAc;IACvC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,WAAW;IAErC,OAAO,CAAC,SAAS;IAGjB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IA2BhC,OAAO,CAAC,aAAa;IAOrB;+EAC2E;IAC3E,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI5B,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM9B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAK1D;;;;;iCAK6B;IAC7B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE;CAezC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ServiceBackend } from "./backend.js";
|
|
2
|
+
export interface SelfUninstallDeps {
|
|
3
|
+
backend?: ServiceBackend;
|
|
4
|
+
log?: (msg: string) => void;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Remove this agent's supervised service after its device was revoked.
|
|
8
|
+
*
|
|
9
|
+
* Returns whether a service was actually removed. NEVER throws: this runs on
|
|
10
|
+
* the shutdown path, and a cleanup that cannot happen must not stop the bridge
|
|
11
|
+
* exiting cleanly — `KeepAlive {SuccessfulExit: false}` depends on that exit(0)
|
|
12
|
+
* to leave the service down rather than crash-looping it.
|
|
13
|
+
*
|
|
14
|
+
* The stakes are asymmetric and worth stating: `uninstall` removes the SERVICE
|
|
15
|
+
* and LEAVES the credentials on disk. A false positive therefore costs the
|
|
16
|
+
* customer a re-install, not their identity.
|
|
17
|
+
*/
|
|
18
|
+
export declare function selfUninstallOnTerminal(reason: string, env: NodeJS.ProcessEnv, deps?: SelfUninstallDeps): boolean;
|
|
19
|
+
//# sourceMappingURL=self-uninstall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-uninstall.d.ts","sourceRoot":"","sources":["../../src/service/self-uninstall.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAuBlE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAoCT"}
|
|
@@ -4,6 +4,18 @@ export interface ServiceCmdDeps {
|
|
|
4
4
|
entrypoint: string;
|
|
5
5
|
log: (msg: string) => void;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* #837: resolve the label that is ACTUALLY loaded rather than assuming the
|
|
9
|
+
* computed one.
|
|
10
|
+
*
|
|
11
|
+
* The launchd label was renamed (`com.agentvault.claude-bridge.<name>` ->
|
|
12
|
+
* `dev.agentvault.bridge-<name>`), so a hardcoded name fails with "Could not
|
|
13
|
+
* find service" on precisely the long-standing installs where an operator most
|
|
14
|
+
* needs `restart` or `status` to work. Prefer the current label, fall back to
|
|
15
|
+
* the legacy one, and if neither is loaded return the current label so the
|
|
16
|
+
* error names what we tried.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveLabel(backend: ServiceBackend, env: NodeJS.ProcessEnv): string;
|
|
7
19
|
/** Handle `agentvault-claude-bridge <install|uninstall|status|logs>` before boot.
|
|
8
20
|
* Returns true when handled (caller should exit). Secrets are never placed on
|
|
9
21
|
* the service command line — config flows via the plist env; the token is omitted. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subcommand.d.ts","sourceRoot":"","sources":["../../src/service/subcommand.ts"],"names":[],"mappings":"
|
|
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,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B;AAoCD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAMpF;AAED;;uFAEuF;AACvF,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAsHT"}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import type { ServiceSpec } from "./spec.js";
|
|
2
|
-
import type { BackendDeps, ServiceBackend } from "./backend.js";
|
|
2
|
+
import type { BackendDeps, ServiceBackend, InstalledService } from "./backend.js";
|
|
3
3
|
/** Render a systemd --user service unit for a bridge ServiceSpec. Pure string.
|
|
4
4
|
* Restart=on-failure + SuccessExitStatus=0 is the KeepAlive={SuccessfulExit:false}
|
|
5
5
|
* equivalent: a clean exit(0) (revoked device) is treated as success => NOT
|
|
6
6
|
* restarted; a non-zero crash is restarted. WantedBy=default.target is RunAtLoad. */
|
|
7
7
|
export declare function renderSystemdUnit(spec: ServiceSpec): string;
|
|
8
|
+
/**
|
|
9
|
+
* Read `Environment=` back out of an installed systemd unit.
|
|
10
|
+
*
|
|
11
|
+
* Reverses `q` exactly: un-double the '%' first, then walk the backslash
|
|
12
|
+
* escapes left to right in ONE pass. Two passes would turn a literal
|
|
13
|
+
* backslash-n (written as \\n) into a newline.
|
|
14
|
+
*
|
|
15
|
+
* Returns {} for junk — a doctor that throws on one malformed unit reports
|
|
16
|
+
* nothing about the others.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseUnitEnv(text: string): Record<string, string>;
|
|
8
19
|
export declare class SystemdBackend implements ServiceBackend {
|
|
9
20
|
private deps;
|
|
10
21
|
constructor(deps: BackendDeps);
|
|
@@ -18,5 +29,12 @@ export declare class SystemdBackend implements ServiceBackend {
|
|
|
18
29
|
loaded: boolean;
|
|
19
30
|
detail: string;
|
|
20
31
|
};
|
|
32
|
+
/** #913 item 3 — every installed unit whose label starts with `prefix`.
|
|
33
|
+
*
|
|
34
|
+
* Reads the unit directory rather than `systemctl list-units`, because an
|
|
35
|
+
* ORPHAN is installed-but-inactive: the revoked device exits 0, which
|
|
36
|
+
* SuccessExitStatus=0 treats as success, so it is never restarted. A
|
|
37
|
+
* running-units listing would not show the thing doctor exists to find. */
|
|
38
|
+
list(prefix: string): InstalledService[];
|
|
21
39
|
}
|
|
22
40
|
//# sourceMappingURL=systemd.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"systemd.d.ts","sourceRoot":"","sources":["../../src/service/systemd.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"systemd.d.ts","sourceRoot":"","sources":["../../src/service/systemd.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAmBlF;;;sFAGsF;AACtF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAsB3D;AAGD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWjE;AAQD,qBAAa,cAAe,YAAW,cAAc;IACvC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,WAAW;IAErC,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAoBhC;2DACuD;IACvD,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI5B,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQ9B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAS1D;;;;;gFAK4E;IAC5E,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE;CAczC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/claude-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgentVault Claude Bridge — daemon for bridging a Claude agent into secure E2E-encrypted AgentVault 1:1 direct messages and rooms.",
|
|
6
6
|
"main": "dist/index.js",
|