@miller-tech/uap 1.119.3 → 1.120.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.
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Reference-counted, session-scoped proxy lifecycle.
3
+ *
4
+ * Goal (per product spec): the UAP Anthropic proxy should start when an agent
5
+ * session is triggered (via the SessionStart hook) and exit WITH the agent —
6
+ * but only when it is safe to do so:
7
+ *
8
+ * - If a proxy is already running, ADOPT it (reuse) — never start a second.
9
+ * - A proxy we adopted (already up), OR a systemd-managed unit, is NEVER
10
+ * stopped by us on session end (systemd units are persistent infra).
11
+ * - A proxy WE spawned as a plain process is reference-counted across
12
+ * sessions: it is stopped on release only when the LAST client (agent
13
+ * session) leaves. If any other client is still using it, it keeps running.
14
+ *
15
+ * This module holds the pure decision logic + a tiny file-backed registry so it
16
+ * is unit-testable without spawning a real proxy or touching systemd. The real
17
+ * side effects (spawn / systemctl / HTTP health probe) are injected as `deps`.
18
+ *
19
+ * Liveness note: a client's `pid` MUST be the long-lived AGENT process (the one
20
+ * that spawns SessionStart and SessionEnd), NOT the ephemeral hook/CLI process
21
+ * — otherwise the dead-pid pruning below evicts every live session moments
22
+ * after it registers. The hook passes `$PPID` (the agent) via `--client-pid`.
23
+ */
24
+ import { mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, existsSync, } from 'fs';
25
+ import { join } from 'path';
26
+ import os from 'os';
27
+ /** Default port the proxy listens on (matches run-anthropic-proxy-continuity.sh). */
28
+ export const DEFAULT_PROXY_PORT = Number(process.env.PROXY_PORT ?? 4000);
29
+ const DIR_MODE = 0o700;
30
+ const FILE_MODE = 0o600;
31
+ /**
32
+ * Runtime root for the client registry + owner marker. Prefer XDG_RUNTIME_DIR
33
+ * (tmpfs at /run/user/<uid>, mode 0700, cleared on logout) and fall back to
34
+ * ~/.cache. Overridable via UAP_PROXY_RUNTIME_DIR for tests.
35
+ */
36
+ export function runtimeDir() {
37
+ const override = process.env.UAP_PROXY_RUNTIME_DIR;
38
+ if (override)
39
+ return override;
40
+ const base = process.env.XDG_RUNTIME_DIR || join(os.homedir(), '.cache');
41
+ return join(base, 'uap-proxy');
42
+ }
43
+ function clientsDir(rt) {
44
+ return join(rt, 'clients');
45
+ }
46
+ function ownerPath(rt) {
47
+ return join(rt, 'owner.json');
48
+ }
49
+ function lockPath(rt) {
50
+ return join(rt, 'start.lock');
51
+ }
52
+ /** Is a pid still alive? `kill(pid, 0)` throws ESRCH when it isn't. */
53
+ export function isPidAlive(pid) {
54
+ if (!Number.isInteger(pid) || pid <= 0)
55
+ return false;
56
+ try {
57
+ process.kill(pid, 0);
58
+ return true;
59
+ }
60
+ catch (e) {
61
+ // EPERM means the process exists but we can't signal it — still alive.
62
+ return e.code === 'EPERM';
63
+ }
64
+ }
65
+ function safeFilename(clientId) {
66
+ // Registry files are named by client id; keep them filesystem-safe and
67
+ // incapable of escaping the clients dir (no path separators survive).
68
+ return clientId.replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 200) || 'anon';
69
+ }
70
+ /** Derive the agent pid encoded in a `ppid-<n>` fallback client id, if present. */
71
+ export function pidFromClientId(clientId) {
72
+ const m = /^ppid-(\d+)$/.exec(clientId);
73
+ if (!m)
74
+ return null;
75
+ const n = Number(m[1]);
76
+ return Number.isInteger(n) && n > 0 ? n : null;
77
+ }
78
+ /** Atomic write (tmp + rename) with restrictive mode so a torn read is impossible. */
79
+ function atomicWrite(path, data) {
80
+ const tmp = `${path}.tmp.${process.pid}.${Math.floor(process.hrtime()[1] % 1e6)}`;
81
+ writeFileSync(tmp, data, { mode: FILE_MODE });
82
+ renameSync(tmp, path);
83
+ }
84
+ /** Register (or refresh) a client in the registry. Idempotent per clientId. */
85
+ export function registerClient(rt, rec) {
86
+ const dir = clientsDir(rt);
87
+ mkdirSync(dir, { recursive: true, mode: DIR_MODE });
88
+ atomicWrite(join(dir, safeFilename(rec.clientId)), JSON.stringify(rec));
89
+ }
90
+ /** Remove a client from the registry (no-op if absent). */
91
+ export function deregisterClient(rt, clientId) {
92
+ try {
93
+ rmSync(join(clientsDir(rt), safeFilename(clientId)), { force: true });
94
+ }
95
+ catch {
96
+ /* best-effort */
97
+ }
98
+ }
99
+ /** List live clients, pruning any whose AGENT process has died (self-healing). */
100
+ export function listClients(rt, opts = {}) {
101
+ const dir = clientsDir(rt);
102
+ if (!existsSync(dir))
103
+ return [];
104
+ const out = [];
105
+ let entries = [];
106
+ try {
107
+ entries = readdirSync(dir);
108
+ }
109
+ catch {
110
+ return [];
111
+ }
112
+ for (const name of entries) {
113
+ if (name.endsWith('.tmp') || name.includes('.tmp.'))
114
+ continue; // in-flight write
115
+ const full = join(dir, name);
116
+ let rec = null;
117
+ try {
118
+ rec = JSON.parse(readFileSync(full, 'utf8'));
119
+ }
120
+ catch {
121
+ rec = null;
122
+ }
123
+ if (!rec || typeof rec.clientId !== 'string') {
124
+ if (opts.prune !== false)
125
+ rmSync(full, { force: true });
126
+ continue;
127
+ }
128
+ // A client whose AGENT process is gone ended without a clean release
129
+ // (crash). Drop it so it never pins the proxy alive forever. pid<=0 means
130
+ // "liveness unknown" — keep it (rely on clean release) rather than evict.
131
+ if (typeof rec.pid === 'number' && rec.pid > 0 && !isPidAlive(rec.pid)) {
132
+ if (opts.prune !== false)
133
+ rmSync(full, { force: true });
134
+ continue;
135
+ }
136
+ out.push(rec);
137
+ }
138
+ return out;
139
+ }
140
+ export function readOwner(rt) {
141
+ const p = ownerPath(rt);
142
+ if (!existsSync(p))
143
+ return null;
144
+ try {
145
+ const rec = JSON.parse(readFileSync(p, 'utf8'));
146
+ if (rec && (rec.kind === 'process' || rec.kind === 'systemd'))
147
+ return rec;
148
+ }
149
+ catch {
150
+ /* fall through */
151
+ }
152
+ return null;
153
+ }
154
+ export function writeOwner(rt, rec) {
155
+ mkdirSync(rt, { recursive: true, mode: DIR_MODE });
156
+ atomicWrite(ownerPath(rt), JSON.stringify(rec));
157
+ }
158
+ export function clearOwner(rt) {
159
+ try {
160
+ rmSync(ownerPath(rt), { force: true });
161
+ }
162
+ catch {
163
+ /* best-effort */
164
+ }
165
+ }
166
+ // ---------------------------------------------------------------------------
167
+ // Start lock — serialize the probe->start->writeOwner critical section so two
168
+ // simultaneous SessionStarts don't both spawn a proxy onto the same port.
169
+ // ---------------------------------------------------------------------------
170
+ const LOCK_STALE_MS = 30_000;
171
+ export async function acquireStartLock(rt, timeoutMs = 5000) {
172
+ mkdirSync(rt, { recursive: true, mode: DIR_MODE });
173
+ const p = lockPath(rt);
174
+ const deadline = Date.now() + timeoutMs;
175
+ for (;;) {
176
+ try {
177
+ mkdirSync(p); // atomic: succeeds only if it did not exist
178
+ return true;
179
+ }
180
+ catch {
181
+ // Held. Steal it if it is stale (a crashed holder).
182
+ try {
183
+ const st = statSync(p);
184
+ if (Date.now() - st.mtimeMs > LOCK_STALE_MS) {
185
+ rmSync(p, { recursive: true, force: true });
186
+ continue;
187
+ }
188
+ }
189
+ catch {
190
+ continue; // vanished between attempts — retry
191
+ }
192
+ if (Date.now() >= deadline)
193
+ return false; // give up; caller proceeds (fail-open)
194
+ await new Promise((r) => setTimeout(r, 100));
195
+ }
196
+ }
197
+ }
198
+ export function releaseStartLock(rt) {
199
+ try {
200
+ rmSync(lockPath(rt), { recursive: true, force: true });
201
+ }
202
+ catch {
203
+ /* best-effort */
204
+ }
205
+ }
206
+ /**
207
+ * Ensure a proxy is available for this client.
208
+ * - already healthy -> ADOPT (reuse); register client; claim no ownership.
209
+ * - not healthy -> take the start lock, re-probe, then start. Claim ownership
210
+ * ONLY if the adapter returns a stoppable owner (a process we spawned).
211
+ * Fail-open: a failed start returns 'start-failed' and never throws.
212
+ */
213
+ export async function ensureProxy(rt, client, port, deps) {
214
+ listClients(rt); // prune dead clients first
215
+ registerClient(rt, client);
216
+ if (await deps.probeHealth(port)) {
217
+ return { action: 'reused', port, owner: readOwner(rt), clients: listClients(rt).length };
218
+ }
219
+ const locked = await acquireStartLock(rt);
220
+ try {
221
+ // Another session may have started it while we waited for the lock.
222
+ if (await deps.probeHealth(port)) {
223
+ return { action: 'reused', port, owner: readOwner(rt), clients: listClients(rt).length };
224
+ }
225
+ const res = await deps.startProxy(port);
226
+ if (!res.healthy) {
227
+ return { action: 'start-failed', port, owner: null, clients: listClients(rt).length };
228
+ }
229
+ // Own ONLY a process we spawned + confirmed. systemd/adopted -> owner null.
230
+ if (res.owner)
231
+ writeOwner(rt, res.owner);
232
+ return { action: 'started', port, owner: res.owner, clients: listClients(rt).length };
233
+ }
234
+ finally {
235
+ if (locked)
236
+ releaseStartLock(rt);
237
+ }
238
+ }
239
+ /**
240
+ * Release this client. Stop the proxy ONLY when:
241
+ * - we own it (a process WE spawned — owner marker present), AND
242
+ * - no other client remains after removing this one.
243
+ * Otherwise leave it running:
244
+ * - 'left-other-clients' — another agent session is still using it.
245
+ * - 'left-adopted' — we never owned it (pre-existing / systemd / external).
246
+ */
247
+ export async function releaseProxy(rt, clientId, deps) {
248
+ deregisterClient(rt, clientId);
249
+ const remaining = listClients(rt); // prunes dead clients too
250
+ const owner = readOwner(rt);
251
+ if (!owner) {
252
+ return { action: 'left-adopted', remainingClients: remaining.length };
253
+ }
254
+ if (remaining.length > 0) {
255
+ return { action: 'left-other-clients', remainingClients: remaining.length };
256
+ }
257
+ // We own it and we are the last client — shut it down with the agent.
258
+ try {
259
+ await deps.stopProxy(owner);
260
+ }
261
+ finally {
262
+ clearOwner(rt);
263
+ }
264
+ return { action: 'stopped', remainingClients: 0 };
265
+ }
266
+ /**
267
+ * Derive a stable client id shared across a session's SessionStart + Stop
268
+ * hooks. Order MATCHES the hook derivation (session-start.sh) so a manual CLI
269
+ * call and the hook agree: explicit flag, then harness session ids, then the
270
+ * parent pid (the agent) — spelled `ppid-<n>` to match the hook.
271
+ */
272
+ export function resolveClientId(explicit) {
273
+ const candidates = [
274
+ explicit,
275
+ process.env.CLAUDE_SESSION_ID,
276
+ process.env.FACTORY_SESSION_ID,
277
+ process.env.CURSOR_SESSION_ID,
278
+ process.env.UAP_SESSION_ID,
279
+ ];
280
+ for (const c of candidates) {
281
+ if (c && c.trim())
282
+ return c.trim();
283
+ }
284
+ return `ppid-${process.ppid}`;
285
+ }
286
+ //# sourceMappingURL=proxy-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-lifecycle.js","sourceRoot":"","sources":["../../src/cli/proxy-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * `uap proxy` — reference-counted, session-scoped lifecycle for the UAP
3
+ * Anthropic proxy.
4
+ *
5
+ * uap proxy ensure [--client <id>] [--client-pid <n>] [--port <n>] [--quiet]
6
+ * Start the proxy if none is running, or ADOPT an already-running one.
7
+ * Registers this session as a client. Wired into the SessionStart hook.
8
+ * uap proxy release [--client <id>] [--quiet]
9
+ * Deregister this session; stop the proxy ONLY if WE spawned it (as a
10
+ * plain process) AND no other client remains. systemd/adopted proxies are
11
+ * never stopped. Wired into the Stop / SessionEnd hook.
12
+ * uap proxy status [--port <n>] [--json]
13
+ * uap proxy start | stop | restart manual controls
14
+ * uap proxy enable | disable toggle .uap.json proxy.autostart
15
+ *
16
+ * Everything is fail-open: hook-driven calls never throw and never block the
17
+ * session — a proxy that won't start just means the agent runs without it.
18
+ *
19
+ * Env overrides (test/advanced):
20
+ * PROXY_PORT default port (4000)
21
+ * UAP_PROXY_RUNTIME_DIR registry root (default $XDG_RUNTIME_DIR/uap-proxy)
22
+ * UAP_PROXY_RUN_SCRIPT proxy launch script (default: bundled continuity script)
23
+ * UAP_PROXY_NO_SYSTEMD=1 force the detached-process path (skip systemd)
24
+ * UAP_PROXY_HEALTH_WAIT_MS how long to wait for /health after launch (15000)
25
+ */
26
+ export interface ProxyOptions {
27
+ client?: string;
28
+ clientPid?: string | number;
29
+ ifEnabled?: boolean;
30
+ port?: string | number;
31
+ quiet?: boolean;
32
+ json?: boolean;
33
+ }
34
+ export declare function proxyCommand(sub: string | undefined, options?: ProxyOptions): Promise<void>;
35
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/cli/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AA0PH,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CA8If"}