agent-shim 7.14.0 → 8.0.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/dist/cli.cjs +661 -572
- package/dist/index.cjs +226 -150
- package/dist/index.mjs +226 -150
- package/dist/types/index.d.ts +36 -18
- package/package.json +1 -1
package/dist/types/index.d.ts
CHANGED
|
@@ -664,10 +664,12 @@ interface LayoutPaths {
|
|
|
664
664
|
readonly categoriesLocalFile: string;
|
|
665
665
|
/** Path to the claude-shim.json marker recording where `agent-shim shim enable` last placed a `claude`-named copy of this executable, and how. */
|
|
666
666
|
readonly claudeShimFile: string;
|
|
667
|
-
/** Directory holding the headroom daemon's coordination state: state
|
|
667
|
+
/** Directory holding the headroom daemon's coordination state: the state file, the start lock, the session registry, and the socket directory. */
|
|
668
668
|
readonly headroomDir: string;
|
|
669
|
-
/** Path to the headroom supervisor's state
|
|
669
|
+
/** Path to the headroom supervisor's state file: pids, socket path, version, allowlist hash, last error. Its name carries `HEADROOM_STATE_SCHEMA_VERSION`, so a supervisor from a release with another state schema keeps its own file instead of contending for this one. */
|
|
670
670
|
readonly headroomStateFile: string;
|
|
671
|
+
/** Directory, mode 0700, holding the headroom daemon's unix socket: one `<supervisor-pid>.sock` per supervisor generation. Only its owner can enter it, which is what authenticates the door's hop to the daemon. */
|
|
672
|
+
readonly headroomSocketDir: string;
|
|
671
673
|
/** Path to the exclusive-create marker guarding "who spawns the supervisor" so concurrent launches start at most one. */
|
|
672
674
|
readonly headroomLockFile: string;
|
|
673
675
|
/** Directory holding one `<launcher-pid>.json` session-registry entry per live launch routed through headroom. */
|
|
@@ -684,7 +686,7 @@ interface LayoutPaths {
|
|
|
684
686
|
readonly frontdoorCaCertFile: string;
|
|
685
687
|
/** Path to the front door's CA private key, mode 0600: whoever can read it can impersonate the door to every routed child. */
|
|
686
688
|
readonly frontdoorCaKeyFile: string;
|
|
687
|
-
/** Directory of combined CA bundles: a parent environment's own NODE_EXTRA_CA_CERTS file plus the front door's CA, one file per distinct combination, for
|
|
689
|
+
/** Directory of combined CA bundles: a parent environment's own NODE_EXTRA_CA_CERTS (for a routed child) or HEADROOM_CA_BUNDLE (for the headroom daemon) file plus the front door's CA, one file per distinct combination, for processes that must keep trusting both. */
|
|
688
690
|
readonly frontdoorCaBundlesDir: string;
|
|
689
691
|
/** Path to the front door's state.json: its supervisor pid, its port, and the sticky port it last served on. */
|
|
690
692
|
readonly frontdoorStateFile: string;
|
|
@@ -1157,8 +1159,8 @@ interface LogPort {
|
|
|
1157
1159
|
}
|
|
1158
1160
|
/** The running headroom daemon one launch routed through, as `HeadroomPort.ensure` reports it. The front door owns the session's routing; this names the daemon the door's headroom hop forwards to, and the project identity headroom scopes memory to. */
|
|
1159
1161
|
interface HeadroomUp {
|
|
1160
|
-
/** The
|
|
1161
|
-
readonly
|
|
1162
|
+
/** The unix socket the headroom daemon serves on, inside the owner-only socket directory: what the door's headroom hop dials. The child never reaches the daemon itself, so this is reported, never exported to its environment. */
|
|
1163
|
+
readonly socketPath: string;
|
|
1162
1164
|
/** The project identity headroom scopes memory state to: the git repo root of the launch directory, or the directory itself outside a repository. */
|
|
1163
1165
|
readonly projectId: string;
|
|
1164
1166
|
}
|
|
@@ -1468,7 +1470,7 @@ declare function mintLeaf(ca: CaMaterial, names: readonly string[], now: Readonl
|
|
|
1468
1470
|
/** Mints each host's leaf once from the CA and reuses it, because generating a 2048-bit RSA keypair costs noticeable time and nothing about the certificate changes between sessions. */
|
|
1469
1471
|
declare function createLeafCache(ca: CaMaterial, now: () => Date): (host: string) => LeafCert;
|
|
1470
1472
|
/**
|
|
1471
|
-
* Persistence for the CA: generated once on the machine's first
|
|
1473
|
+
* Persistence for the CA: generated once on the machine's first front-door start and reused forever after, because regenerating it would invalidate every child's `NODE_EXTRA_CA_CERTS` pointing at the old file. The real implementation writes the key with mode 0600; injected fakes keep it in memory.
|
|
1472
1474
|
*/
|
|
1473
1475
|
interface ConnectCertStore {
|
|
1474
1476
|
/** The persisted CA, or undefined when none exists yet (or only half of it does: a cert without its key can sign nothing, so it counts as absent). */
|
|
@@ -1784,21 +1786,39 @@ interface EnsureHeadroomPorts {
|
|
|
1784
1786
|
/**
|
|
1785
1787
|
* Brings the headroom daemon up for this launch, or finds it already running, and registers this launcher pid in its session registry (the fact that keeps the daemon alive and defers drift restarts until this launch is done).
|
|
1786
1788
|
*
|
|
1787
|
-
* The exclusive-create start lock decides which of several concurrent launches spawns the one supervisor: its holder spawns and keeps waiting like everyone else, everyone else waits on state
|
|
1789
|
+
* The exclusive-create start lock decides which of several concurrent launches spawns the one supervisor: its holder spawns and keeps waiting like everyone else, everyone else waits on the state file, and a lock whose holder has died is removed and retried. Readiness is "state names a live supervisor, a live headroom pid, and a socket path", which the supervisor only writes after its own `/readyz` probe over that socket has passed, so polling state alone never mistakes a bound-but-not-ready socket for a usable daemon.
|
|
1788
1790
|
*
|
|
1789
|
-
* Throws `HeadroomStartError` (naming the daemon log path and any recorded `lastError`) when the daemon is not up within `HEADROOM_START_TIMEOUT_MS`, or when a live supervisor has recorded a fatal error.
|
|
1791
|
+
* Throws `HeadroomStartError` (naming the daemon log path and any recorded `lastError`) when the daemon is not up within `HEADROOM_START_TIMEOUT_MS`, or when a live supervisor has recorded a fatal error (a platform without unix sockets and a socket directory that fails the owner-only check are recorded this way, so the launch fails with the reason).
|
|
1790
1792
|
*/
|
|
1791
1793
|
declare function ensureHeadroom(params: {
|
|
1792
1794
|
readonly paths: LayoutPaths;
|
|
1793
1795
|
readonly launcherPid: number;
|
|
1794
1796
|
readonly ports: EnsureHeadroomPorts;
|
|
1795
1797
|
}): {
|
|
1796
|
-
readonly
|
|
1798
|
+
readonly socketPath: string;
|
|
1797
1799
|
};
|
|
1798
1800
|
|
|
1799
1801
|
/** The token-saving settings of the `headroom` config block, without its install and lifecycle fields (`source`, `idleShutdownMinutes`). */
|
|
1800
1802
|
type HeadroomSettings = Pick<HeadroomGlobalConfig, "mode" | "targetRatio" | "ccr" | "rolloutChannel" | "interceptToolResults" | "readMaturation">;
|
|
1801
1803
|
|
|
1804
|
+
/** What an `lstat` (which never follows symlinks) reports about one path, narrowed to what authenticating the socket needs. */
|
|
1805
|
+
interface SocketPathStat {
|
|
1806
|
+
readonly kind: "dir" | "socket" | "symlink" | "other";
|
|
1807
|
+
/** The owning user id. */
|
|
1808
|
+
readonly uid: number;
|
|
1809
|
+
/** The permission bits, without the file type bits. */
|
|
1810
|
+
readonly mode: number;
|
|
1811
|
+
}
|
|
1812
|
+
/** The platform facts and the one filesystem call the socket checks need, injected so every refusal is unit-testable without a real socket, directory or second user. */
|
|
1813
|
+
interface HeadroomSocketTrustPorts {
|
|
1814
|
+
/** `process.platform` in the real implementation. */
|
|
1815
|
+
readonly platform: string;
|
|
1816
|
+
/** This process's user id. Only called on a platform with unix sockets, which all have POSIX user ids. */
|
|
1817
|
+
readonly currentUid: () => number;
|
|
1818
|
+
/** Stats one path without following symlinks, or returns undefined when nothing is there. */
|
|
1819
|
+
readonly lstat: (target: string) => SocketPathStat | undefined;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1802
1822
|
/** Everything the supervisor needs to run the daemon, resolved from the global config and the filesystem before it starts. */
|
|
1803
1823
|
interface HeadroomSupervisorConfig {
|
|
1804
1824
|
/** The install spec as configured (`headroom.source`), already defaulted. */
|
|
@@ -1826,20 +1846,18 @@ interface SupervisorPorts {
|
|
|
1826
1846
|
* Zombie-aware liveness, and for a daemon this supervisor spawned, exit-event-backed: the implementation keeps the spawned ChildProcess handle, consumes its exit event (which is also what reaps it), and reports the pid as not running from that moment. A bare signal-0 check is not enough, because a defunct process still answers it.
|
|
1827
1847
|
*/
|
|
1828
1848
|
readonly isRunning: (pid: number) => boolean;
|
|
1829
|
-
/**
|
|
1830
|
-
readonly
|
|
1831
|
-
/** Whether nothing is currently listening on one specific loopback port: the check that decides whether the sticky `lastPort` can be reused. The same bind-and-release technique `freePort` uses, applied to a number the caller already cares about. */
|
|
1832
|
-
readonly isPortFree: (port: number) => Promise<boolean>;
|
|
1849
|
+
/** The platform facts and owner-only checks the daemon's socket directory is prepared and verified with (see `prepareHeadroomSocket`). */
|
|
1850
|
+
readonly socketTrust: HeadroomSocketTrustPorts;
|
|
1833
1851
|
/**
|
|
1834
|
-
* Starts `headroom proxy`
|
|
1852
|
+
* Starts `headroom proxy` serving on the unix socket `socketPath` (and on no TCP port) with the given allowlist, returning its pid. Output goes to the daemon log. The implementation must keep the ChildProcess handle and attach an exit listener (detaching the process is fine; unref'ing a child you still need events from is not, since without the listener nothing reaps it and it lingers as a zombie).
|
|
1835
1853
|
*/
|
|
1836
|
-
readonly spawnHeadroom: (
|
|
1854
|
+
readonly spawnHeadroom: (socketPath: string, allowlist: readonly string[], settings: Readonly<HeadroomSettings>) => number;
|
|
1837
1855
|
/** Reads the headroom config block as it stands on disk now. The supervisor calls it every tick, so a changed `source` or setting is noticed while the daemon runs and applied by the same quiet-registry restart an allowlist change gets. */
|
|
1838
1856
|
readonly readConfig: () => HeadroomSupervisorConfig;
|
|
1839
1857
|
/** Stops a process the supervisor owns, escalating SIGTERM to SIGKILL on a bounded timeout (see `stopSupervisedProcess`). */
|
|
1840
1858
|
readonly stopProcess: (pid: number) => void;
|
|
1841
|
-
/** True once `GET /readyz`
|
|
1842
|
-
readonly ready: (
|
|
1859
|
+
/** True once `GET /readyz` over the unix socket succeeds. */
|
|
1860
|
+
readonly ready: (socketPath: string) => Promise<boolean>;
|
|
1843
1861
|
/** Runs `uv tool install <spec>`, logging output to the daemon log. Returns ok=false with the error when `uv` is absent or the install fails. */
|
|
1844
1862
|
readonly install: (spec: string) => {
|
|
1845
1863
|
readonly ok: boolean;
|
|
@@ -2644,7 +2662,7 @@ interface RunDoctorParams {
|
|
|
2644
2662
|
readonly run?: RunPort;
|
|
2645
2663
|
/** `process.platform` in real use; the Keychain check only ever runs when this is `"darwin"`. */
|
|
2646
2664
|
readonly platform: string;
|
|
2647
|
-
/** The headroom daemon's state.json plus a zombie-aware liveness predicate for the pids it names (a defunct daemon
|
|
2665
|
+
/** The headroom daemon's state.json plus a zombie-aware liveness predicate for the pids it names (a defunct daemon serves nothing but still answers signal 0). Omit the raw text when the daemon has never run; that is a pass, not a failure. */
|
|
2648
2666
|
readonly headroom: {
|
|
2649
2667
|
readonly state: DoctorFileInput;
|
|
2650
2668
|
readonly isRunning: (pid: number) => boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-shim",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.1",
|
|
4
4
|
"description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Joseph Mearman <joseph@mearman.co.uk>",
|