@integrity-labs/agt-cli 0.27.5 → 0.27.7-test.5

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.
@@ -1,101 +0,0 @@
1
- import {
2
- CLAUDE_AGENT_PROCESS_PATTERN,
3
- parsePsRows
4
- } from "./chunk-XWVM4KPK.js";
5
-
6
- // src/lib/claude-code-updater.ts
7
- import { execFile, execFileSync } from "child_process";
8
- import { promisify } from "util";
9
- var execFileAsync = promisify(execFile);
10
- var NPM_PKG = "@anthropic-ai/claude-code";
11
- var DEFAULT_INTERVAL_MS = 6 * 60 * 60 * 1e3;
12
- function intervalMs() {
13
- const raw = Number.parseInt(process.env["AGT_CLAUDE_UPDATE_INTERVAL_MS"] ?? "", 10);
14
- return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_INTERVAL_MS;
15
- }
16
- function parseVersion(output) {
17
- const m = output.match(/\d+\.\d+\.\d+[A-Za-z0-9.+-]*/);
18
- return m ? m[0] : null;
19
- }
20
- function isConcreteVersion(spec) {
21
- return /^\d+\.\d+\.\d+[A-Za-z0-9.+-]*$/.test(spec.trim());
22
- }
23
- var lastCheckAt = 0;
24
- function _resetClaudeCodeUpdaterState() {
25
- lastCheckAt = 0;
26
- }
27
- function defaultPsSnapshot() {
28
- return execFileSync("ps", ["-eo", "pid,ppid,args"], { encoding: "utf-8", maxBuffer: 16 * 1024 * 1024 });
29
- }
30
- function hostHasLiveAgent(psOutput) {
31
- return parsePsRows(psOutput).some((r) => CLAUDE_AGENT_PROCESS_PATTERN.test(r.args));
32
- }
33
- async function maybeUpdateClaudeCode(deps) {
34
- const now = deps.now ? deps.now() : Date.now();
35
- const run = deps.runCommand ?? ((cmd, args) => execFileAsync(cmd, args, { maxBuffer: 16 * 1024 * 1024 }));
36
- const ps = deps.psSnapshot ?? defaultPsSnapshot;
37
- const { log } = deps;
38
- if (now - lastCheckAt < intervalMs()) {
39
- return { action: "throttled" };
40
- }
41
- lastCheckAt = now;
42
- try {
43
- const spec = (deps.desiredVersion || "latest").trim();
44
- let target;
45
- if (isConcreteVersion(spec)) {
46
- target = spec;
47
- } else {
48
- const { stdout } = await run("npm", ["view", `${NPM_PKG}@${spec}`, "version"]);
49
- const resolved = parseVersion(stdout);
50
- if (!resolved) {
51
- log(`[claude-code-updater] could not resolve ${NPM_PKG}@${spec} to a version; skipping`);
52
- return { action: "failed", reason: "unresolved_target" };
53
- }
54
- target = resolved;
55
- }
56
- let current = deps.currentVersion ?? null;
57
- if (!current) {
58
- try {
59
- const { stdout } = await run("claude", ["--version"]);
60
- current = parseVersion(stdout);
61
- } catch {
62
- current = null;
63
- }
64
- }
65
- if (current && current === target) {
66
- return { action: "up_to_date", version: target };
67
- }
68
- if (hostHasLiveAgent(ps())) {
69
- log(`[claude-code-updater] host busy (live agent) \u2014 deferring update to ${target} to a later cycle`);
70
- return { action: "deferred_busy", target };
71
- }
72
- log(`[claude-code-updater] updating Claude Code ${current ?? "(none)"} \u2192 ${target}`);
73
- await run("npm", ["install", "-g", `${NPM_PKG}@${target}`]);
74
- let installed = null;
75
- try {
76
- const { stdout } = await run("claude", ["--version"]);
77
- installed = parseVersion(stdout);
78
- } catch {
79
- installed = null;
80
- }
81
- if (installed !== target) {
82
- log(
83
- `[claude-code-updater] post-install verify mismatch: expected ${target}, got ${installed ?? "unknown"}`
84
- );
85
- return { action: "failed", reason: "verify_mismatch" };
86
- }
87
- log(`[claude-code-updater] updated Claude Code ${current ?? "(none)"} \u2192 ${installed}`);
88
- return { action: "updated", from: current, to: installed };
89
- } catch (err) {
90
- log(`[claude-code-updater] update attempt failed: ${err instanceof Error ? err.message : String(err)}`);
91
- return { action: "failed", reason: "exception" };
92
- }
93
- }
94
- export {
95
- _resetClaudeCodeUpdaterState,
96
- hostHasLiveAgent,
97
- isConcreteVersion,
98
- maybeUpdateClaudeCode,
99
- parseVersion
100
- };
101
- //# sourceMappingURL=claude-code-updater-7BGQ6JCV.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/claude-code-updater.ts"],"sourcesContent":["// ENG-5672: idle-gated, server-pinned Claude Code auto-updater.\n//\n// Hosts install @anthropic-ai/claude-code once at bootstrap (host-bootstrap.ts)\n// and never refresh it, so a long-lived host runs a stale Claude Code forever.\n// This module brings a host to its *desired* version, which the server pins via\n// /host/refresh (per-host override, else fleet default, else 'latest').\n//\n// Safety properties (this runs npm install -g on live prod hosts):\n// • Idle-gated — we never install while a `claude --name agt-*` agent process\n// is running on the host (a running process holds the old binary anyway;\n// replacing it under a live session is pointless and risky). Busy → defer\n// to a later cycle.\n// • No-op when already on target — a concrete target equal to the installed\n// version short-circuits; a dist-tag ('latest') is resolved to a concrete\n// version via `npm view` and compared, so we don't reinstall every cycle.\n// • Host-global throttle — at most one check per interval (default 6h), so the\n// manager's ~10-30s poll doesn't shell out to npm/ps constantly.\n// • Log-and-continue — every failure is logged and swallowed; an error here\n// must never break the manager poll cycle.\n//\n// The *installed* version is reported separately as hosts.framework_version\n// (the claude-code framework adapter's getVersion() == `claude --version`), so\n// this module does not report it itself.\n\nimport { execFile, execFileSync } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { parsePsRows, CLAUDE_AGENT_PROCESS_PATTERN } from './orphan-channel-mcp-reaper.js';\n\nconst execFileAsync = promisify(execFile);\n\nconst NPM_PKG = '@anthropic-ai/claude-code';\n\n/** Default host-global check interval. Overridable via env for tuning. */\nconst DEFAULT_INTERVAL_MS = 6 * 60 * 60 * 1000; // 6h\n\nfunction intervalMs(): number {\n const raw = Number.parseInt(process.env['AGT_CLAUDE_UPDATE_INTERVAL_MS'] ?? '', 10);\n return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_INTERVAL_MS;\n}\n\n/** Extract a version string (leading semver) from `claude --version` / npm output. */\nexport function parseVersion(output: string): string | null {\n const m = output.match(/\\d+\\.\\d+\\.\\d+[A-Za-z0-9.+-]*/);\n return m ? m[0] : null;\n}\n\n/** A target spec is \"concrete\" when it already names an exact version. */\nexport function isConcreteVersion(spec: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+[A-Za-z0-9.+-]*$/.test(spec.trim());\n}\n\nexport interface ClaudeCodeUpdaterDeps {\n /** Server-pinned target: a concrete version or an npm dist-tag like 'latest'. */\n desiredVersion: string;\n /** Currently-installed version, if the caller already knows it (e.g. the\n * manager's cachedFrameworkVersion). When omitted we run `claude --version`. */\n currentVersion?: string | null;\n log: (msg: string) => void;\n /** Override \"now\" for tests. */\n now?: () => number;\n /** Run a command; injected in tests. Defaults to execFile. */\n runCommand?: (cmd: string, args: string[]) => Promise<{ stdout: string; stderr: string }>;\n /** Raw `ps -eo pid,ppid,args` snapshot; injected in tests. */\n psSnapshot?: () => string;\n}\n\nexport type UpdaterOutcome =\n | { action: 'throttled' }\n | { action: 'up_to_date'; version: string }\n | { action: 'deferred_busy'; target: string }\n | { action: 'updated'; from: string | null; to: string }\n | { action: 'failed'; reason: string };\n\n// ── Host-global throttle state (module-level; one updater per host process) ──\nlet lastCheckAt = 0;\n\n/** Test helper — reset module state. Not for production use. */\nexport function _resetClaudeCodeUpdaterState(): void {\n lastCheckAt = 0;\n}\n\nfunction defaultPsSnapshot(): string {\n return execFileSync('ps', ['-eo', 'pid,ppid,args'], { encoding: 'utf-8', maxBuffer: 16 * 1024 * 1024 });\n}\n\n/** True when at least one `claude --name agt-*` agent process is live. */\nexport function hostHasLiveAgent(psOutput: string): boolean {\n return parsePsRows(psOutput).some((r) => CLAUDE_AGENT_PROCESS_PATTERN.test(r.args));\n}\n\n/**\n * Bring Claude Code to the desired version if the host is idle and not already\n * on target. Safe to call every manager poll — internally throttled. Returns an\n * outcome describing what happened (for logging/observability/tests).\n */\nexport async function maybeUpdateClaudeCode(deps: ClaudeCodeUpdaterDeps): Promise<UpdaterOutcome> {\n const now = deps.now ? deps.now() : Date.now();\n const run = deps.runCommand ?? ((cmd, args) => execFileAsync(cmd, args, { maxBuffer: 16 * 1024 * 1024 }));\n const ps = deps.psSnapshot ?? defaultPsSnapshot;\n const { log } = deps;\n\n // 1. Host-global throttle.\n if (now - lastCheckAt < intervalMs()) {\n return { action: 'throttled' };\n }\n lastCheckAt = now;\n\n try {\n const spec = (deps.desiredVersion || 'latest').trim();\n\n // 2. Resolve the target to a concrete version (read-only). A concrete spec\n // is its own target; a dist-tag is resolved via `npm view`.\n let target: string;\n if (isConcreteVersion(spec)) {\n target = spec;\n } else {\n const { stdout } = await run('npm', ['view', `${NPM_PKG}@${spec}`, 'version']);\n const resolved = parseVersion(stdout);\n if (!resolved) {\n log(`[claude-code-updater] could not resolve ${NPM_PKG}@${spec} to a version; skipping`);\n return { action: 'failed', reason: 'unresolved_target' };\n }\n target = resolved;\n }\n\n // 3. Determine the installed version (prefer the caller's cached value).\n let current = deps.currentVersion ?? null;\n if (!current) {\n try {\n const { stdout } = await run('claude', ['--version']);\n current = parseVersion(stdout);\n } catch {\n current = null; // claude missing/broken — treat as \"needs install\".\n }\n }\n\n // 4. Already on target → nothing to do.\n if (current && current === target) {\n return { action: 'up_to_date', version: target };\n }\n\n // 5. Idle gate — never replace the binary under a live agent session.\n if (hostHasLiveAgent(ps())) {\n log(`[claude-code-updater] host busy (live agent) — deferring update to ${target} to a later cycle`);\n return { action: 'deferred_busy', target };\n }\n\n // 6. Install the target, then verify.\n log(`[claude-code-updater] updating Claude Code ${current ?? '(none)'} → ${target}`);\n await run('npm', ['install', '-g', `${NPM_PKG}@${target}`]);\n\n let installed: string | null = null;\n try {\n const { stdout } = await run('claude', ['--version']);\n installed = parseVersion(stdout);\n } catch {\n installed = null;\n }\n\n if (installed !== target) {\n log(\n `[claude-code-updater] post-install verify mismatch: expected ${target}, got ${installed ?? 'unknown'}`,\n );\n return { action: 'failed', reason: 'verify_mismatch' };\n }\n\n log(`[claude-code-updater] updated Claude Code ${current ?? '(none)'} → ${installed}`);\n return { action: 'updated', from: current, to: installed };\n } catch (err) {\n // Log-and-continue: an updater failure must never break the manager tick.\n log(`[claude-code-updater] update attempt failed: ${err instanceof Error ? err.message : String(err)}`);\n return { action: 'failed', reason: 'exception' };\n }\n}\n"],"mappings":"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","names":[]}