@phnx-labs/agents-cli 1.20.17 → 1.20.18

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +1 -1
  3. package/dist/commands/budget.d.ts +14 -0
  4. package/dist/commands/budget.js +137 -0
  5. package/dist/commands/cost.d.ts +12 -0
  6. package/dist/commands/cost.js +139 -0
  7. package/dist/commands/exec.d.ts +20 -0
  8. package/dist/commands/exec.js +382 -5
  9. package/dist/commands/secrets.d.ts +15 -0
  10. package/dist/commands/secrets.js +250 -4
  11. package/dist/commands/sessions.js +4 -0
  12. package/dist/index.js +4 -0
  13. package/dist/lib/budget/config.d.ts +9 -0
  14. package/dist/lib/budget/config.js +115 -0
  15. package/dist/lib/budget/enforce.d.ts +94 -0
  16. package/dist/lib/budget/enforce.js +151 -0
  17. package/dist/lib/budget/ledger.d.ts +61 -0
  18. package/dist/lib/budget/ledger.js +107 -0
  19. package/dist/lib/budget/preflight.d.ts +110 -0
  20. package/dist/lib/budget/preflight.js +200 -0
  21. package/dist/lib/checkpoint.d.ts +54 -0
  22. package/dist/lib/checkpoint.js +56 -0
  23. package/dist/lib/cloud/rush.js +18 -0
  24. package/dist/lib/exec.d.ts +36 -0
  25. package/dist/lib/exec.js +192 -4
  26. package/dist/lib/git.d.ts +18 -0
  27. package/dist/lib/git.js +67 -4
  28. package/dist/lib/loop.d.ts +145 -0
  29. package/dist/lib/loop.js +330 -0
  30. package/dist/lib/mcp.d.ts +7 -0
  31. package/dist/lib/mcp.js +24 -0
  32. package/dist/lib/models.d.ts +11 -0
  33. package/dist/lib/models.js +21 -0
  34. package/dist/lib/plugins.js +5 -2
  35. package/dist/lib/pricing/cost.d.ts +46 -0
  36. package/dist/lib/pricing/cost.js +71 -0
  37. package/dist/lib/pricing/index.d.ts +8 -0
  38. package/dist/lib/pricing/index.js +8 -0
  39. package/dist/lib/pricing/prices.json +138 -0
  40. package/dist/lib/pricing/table.d.ts +17 -0
  41. package/dist/lib/pricing/table.js +73 -0
  42. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  43. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  44. package/dist/lib/secrets/agent.d.ts +134 -0
  45. package/dist/lib/secrets/agent.js +501 -0
  46. package/dist/lib/secrets/bundles.d.ts +21 -0
  47. package/dist/lib/secrets/bundles.js +43 -0
  48. package/dist/lib/session/db.d.ts +40 -0
  49. package/dist/lib/session/db.js +84 -2
  50. package/dist/lib/session/discover.d.ts +2 -0
  51. package/dist/lib/session/discover.js +126 -2
  52. package/dist/lib/session/render.d.ts +2 -0
  53. package/dist/lib/session/render.js +1 -1
  54. package/dist/lib/session/types.d.ts +4 -0
  55. package/dist/lib/teams/agents.d.ts +32 -0
  56. package/dist/lib/teams/agents.js +66 -3
  57. package/dist/lib/teams/api.js +20 -0
  58. package/dist/lib/teams/parsers.js +16 -4
  59. package/dist/lib/types.d.ts +48 -0
  60. package/dist/lib/workflows.d.ts +56 -0
  61. package/dist/lib/workflows.js +72 -5
  62. package/package.json +2 -1
@@ -0,0 +1,138 @@
1
+ {
2
+ "version": "2026-06-24",
3
+ "models": {
4
+ "claude-opus-4": {
5
+ "inputPerToken": 0.000005,
6
+ "outputPerToken": 0.000025,
7
+ "cacheReadPerToken": 0.0000005,
8
+ "cacheWritePerToken": 0.00000625
9
+ },
10
+ "claude-sonnet-4": {
11
+ "inputPerToken": 0.000003,
12
+ "outputPerToken": 0.000015,
13
+ "cacheReadPerToken": 0.0000003,
14
+ "cacheWritePerToken": 0.00000375
15
+ },
16
+ "claude-haiku-4": {
17
+ "inputPerToken": 0.000001,
18
+ "outputPerToken": 0.000005,
19
+ "cacheReadPerToken": 0.0000001,
20
+ "cacheWritePerToken": 0.00000125
21
+ },
22
+ "claude-fable-5": {
23
+ "inputPerToken": 0.00001,
24
+ "outputPerToken": 0.00005,
25
+ "cacheReadPerToken": 0.000001,
26
+ "cacheWritePerToken": 0.0000125
27
+ },
28
+ "claude-mythos-5": {
29
+ "inputPerToken": 0.00001,
30
+ "outputPerToken": 0.00005,
31
+ "cacheReadPerToken": 0.000001,
32
+ "cacheWritePerToken": 0.0000125
33
+ },
34
+ "claude-3-5-sonnet": {
35
+ "inputPerToken": 0.000003,
36
+ "outputPerToken": 0.000015,
37
+ "cacheReadPerToken": 0.0000003,
38
+ "cacheWritePerToken": 0.00000375
39
+ },
40
+ "claude-3-5-haiku": {
41
+ "inputPerToken": 0.0000008,
42
+ "outputPerToken": 0.000004,
43
+ "cacheReadPerToken": 0.00000008,
44
+ "cacheWritePerToken": 0.000001
45
+ },
46
+ "claude-3-opus": {
47
+ "inputPerToken": 0.000015,
48
+ "outputPerToken": 0.000075,
49
+ "cacheReadPerToken": 0.0000015,
50
+ "cacheWritePerToken": 0.00001875
51
+ },
52
+ "gpt-5.5": {
53
+ "inputPerToken": 0.000005,
54
+ "outputPerToken": 0.00003,
55
+ "cacheReadPerToken": 0.0000005
56
+ },
57
+ "gpt-5.4": {
58
+ "inputPerToken": 0.0000025,
59
+ "outputPerToken": 0.000015,
60
+ "cacheReadPerToken": 0.00000025
61
+ },
62
+ "gpt-5.4-mini": {
63
+ "inputPerToken": 0.00000075,
64
+ "outputPerToken": 0.0000045,
65
+ "cacheReadPerToken": 0.000000075
66
+ },
67
+ "gpt-5.4-nano": {
68
+ "inputPerToken": 0.0000002,
69
+ "outputPerToken": 0.00000125,
70
+ "cacheReadPerToken": 0.00000002
71
+ },
72
+ "gpt-5": {
73
+ "inputPerToken": 0.00000125,
74
+ "outputPerToken": 0.00001,
75
+ "cacheReadPerToken": 0.000000125
76
+ },
77
+ "gpt-4.1": {
78
+ "inputPerToken": 0.000002,
79
+ "outputPerToken": 0.000008,
80
+ "cacheReadPerToken": 0.0000005
81
+ },
82
+ "gpt-4.1-mini": {
83
+ "inputPerToken": 0.0000004,
84
+ "outputPerToken": 0.0000016,
85
+ "cacheReadPerToken": 0.0000001
86
+ },
87
+ "gpt-4.1-nano": {
88
+ "inputPerToken": 0.0000001,
89
+ "outputPerToken": 0.0000004,
90
+ "cacheReadPerToken": 0.000000025
91
+ },
92
+ "gpt-4o": {
93
+ "inputPerToken": 0.0000025,
94
+ "outputPerToken": 0.00001,
95
+ "cacheReadPerToken": 0.00000125
96
+ },
97
+ "gpt-4o-mini": {
98
+ "inputPerToken": 0.00000015,
99
+ "outputPerToken": 0.0000006,
100
+ "cacheReadPerToken": 0.000000075
101
+ },
102
+ "o3": {
103
+ "inputPerToken": 0.000002,
104
+ "outputPerToken": 0.000008,
105
+ "cacheReadPerToken": 0.0000005
106
+ },
107
+ "o4-mini": {
108
+ "inputPerToken": 0.0000011,
109
+ "outputPerToken": 0.0000044,
110
+ "cacheReadPerToken": 0.000000275
111
+ },
112
+ "gemini-2.5-pro": {
113
+ "inputPerToken": 0.00000125,
114
+ "outputPerToken": 0.00001,
115
+ "cacheReadPerToken": 0.0000003125
116
+ },
117
+ "gemini-2.5-flash": {
118
+ "inputPerToken": 0.0000003,
119
+ "outputPerToken": 0.0000025,
120
+ "cacheReadPerToken": 0.000000075
121
+ },
122
+ "gemini-2.5-flash-lite": {
123
+ "inputPerToken": 0.0000001,
124
+ "outputPerToken": 0.0000004,
125
+ "cacheReadPerToken": 0.000000025
126
+ },
127
+ "gemini-1.5-pro": {
128
+ "inputPerToken": 0.00000125,
129
+ "outputPerToken": 0.000005,
130
+ "cacheReadPerToken": 0.0000003125
131
+ },
132
+ "gemini-1.5-flash": {
133
+ "inputPerToken": 0.000000075,
134
+ "outputPerToken": 0.0000003,
135
+ "cacheReadPerToken": 0.00000001875
136
+ }
137
+ }
138
+ }
@@ -0,0 +1,17 @@
1
+ /** Per-token USD prices for a single model. Cache fields optional (not all vendors expose them). */
2
+ export interface ModelPricing {
3
+ inputPerToken: number;
4
+ outputPerToken: number;
5
+ cacheReadPerToken?: number;
6
+ cacheWritePerToken?: number;
7
+ }
8
+ /** Date-stamped version of the pricing table (e.g. "2026-06-24"). */
9
+ export declare const PRICING_VERSION: string;
10
+ /**
11
+ * Resolve per-token pricing for a model id. Tolerant of vendor prefixes,
12
+ * version dashes, and date suffixes. Returns null when no canonical key is a
13
+ * substring of the normalized id (i.e. genuinely unknown model).
14
+ */
15
+ export declare function getModelPricing(modelId: string): ModelPricing | null;
16
+ /** List every canonical model id that carries a price. */
17
+ export declare function listPricedModels(): string[];
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Offline, versioned per-model pricing table.
3
+ *
4
+ * The canonical data lives in `prices.json` (LiteLLM-style per-token USD map)
5
+ * and is imported with a `type: json` attribute so it survives `tsc` emit AND
6
+ * Node ESM's import-attribute requirement at runtime (the package is ESM).
7
+ *
8
+ * `getModelPricing` is prefix/suffix-tolerant: real model identifiers carry
9
+ * vendor prefixes (`us.anthropic.`), version dashes (`claude-opus-4-8`), and
10
+ * date suffixes (`-20250514`), none of which appear in the canonical keys. We
11
+ * normalize the input, then match against the LONGEST canonical key the
12
+ * normalized id contains so `claude-opus-4` wins over a hypothetical
13
+ * `claude-opus` when both are present.
14
+ */
15
+ import pricesData from './prices.json' with { type: 'json' };
16
+ const PRICES = pricesData;
17
+ /** Date-stamped version of the pricing table (e.g. "2026-06-24"). */
18
+ export const PRICING_VERSION = PRICES.version;
19
+ const MODELS = PRICES.models;
20
+ // Canonical keys sorted longest-first so containment matching prefers the most
21
+ // specific key (e.g. "gemini-2.5-flash-lite" before "gemini-2.5-flash").
22
+ const KEYS_BY_LENGTH = Object.keys(MODELS).sort((a, b) => b.length - a.length);
23
+ /**
24
+ * Normalize a raw model id into the dash-delimited token space the canonical
25
+ * keys live in. Strips vendor prefixes (`anthropic/`, `us.anthropic.`,
26
+ * `models/`, `openai/`), lowercases, and collapses any non [a-z0-9.] run to a
27
+ * single dash so `claude-opus-4-8`, `Claude Opus 4`, and `claude.opus.4` all
28
+ * normalize to a comparable form.
29
+ */
30
+ function normalizeModelId(modelId) {
31
+ let id = modelId.trim().toLowerCase();
32
+ // Drop a leading vendor segment: "anthropic/claude-..", "us.anthropic.claude-..",
33
+ // "google/gemini-..", "models/gemini-..", "openai/gpt-..".
34
+ id = id.replace(/^[a-z]+\//, ''); // "anthropic/x" -> "x"
35
+ id = id.replace(/^[a-z]+\.[a-z]+\./, ''); // "us.anthropic.x" -> "x"
36
+ id = id.replace(/^models\//, ''); // already handled, defensive
37
+ // Collapse separators to single dashes, keep dots (gpt-5.4) intact.
38
+ id = id.replace(/[\s_]+/g, '-').replace(/-+/g, '-');
39
+ return id;
40
+ }
41
+ /**
42
+ * Resolve per-token pricing for a model id. Tolerant of vendor prefixes,
43
+ * version dashes, and date suffixes. Returns null when no canonical key is a
44
+ * substring of the normalized id (i.e. genuinely unknown model).
45
+ */
46
+ export function getModelPricing(modelId) {
47
+ if (!modelId)
48
+ return null;
49
+ const norm = normalizeModelId(modelId);
50
+ // Exact key first (fast path + unambiguous).
51
+ if (MODELS[norm])
52
+ return MODELS[norm];
53
+ // Containment match, longest canonical key wins. The canonical key must
54
+ // appear as a dash-bounded prefix of the normalized id so "claude-opus-4"
55
+ // matches "claude-opus-4-8" and "claude-opus-4-20250514" but a stray
56
+ // "gpt-4" inside "gpt-40-turbo-experimental" still requires the boundary.
57
+ for (const key of KEYS_BY_LENGTH) {
58
+ if (norm === key || norm.startsWith(key + '-') || norm.startsWith(key + '.')) {
59
+ return MODELS[key];
60
+ }
61
+ }
62
+ // Last resort: canonical key contained anywhere (handles "anthropic-claude-opus-4"
63
+ // style ids the prefix strip missed). Still longest-first.
64
+ for (const key of KEYS_BY_LENGTH) {
65
+ if (norm.includes(key))
66
+ return MODELS[key];
67
+ }
68
+ return null;
69
+ }
70
+ /** List every canonical model id that carries a price. */
71
+ export function listPricedModels() {
72
+ return Object.keys(MODELS);
73
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * The secrets-agent: a local broker that holds resolved bundle env in memory
3
+ * after a single Touch ID unlock, so concurrent agent processes don't each pop
4
+ * their own prompt.
5
+ *
6
+ * Why this exists: every secret item carries a biometry access control, and
7
+ * macOS refuses to cache that across processes — N concurrent `agents run`
8
+ * spawns = N Touch ID prompts (see src/lib/secrets/bundles.ts). The Swift
9
+ * helper's LAContext only deduplicates reads *within one process*. This broker
10
+ * is the ssh-agent answer: `agents secrets unlock <bundle>` decrypts the bundle
11
+ * once (one prompt), ships the resolved env here, and every later read returns
12
+ * from memory over a user-only Unix socket — no prompt.
13
+ *
14
+ * Security model (deliberate): while a bundle is unlocked, any same-user
15
+ * process that can reach the socket reads it silently. That's strictly the same
16
+ * trust boundary the keychain already concedes (docs/secrets.md: the ACL is
17
+ * user-presence, not code-identity — any same-user process can pop the prompt
18
+ * and read), minus the visible prompt. We bound it with: explicit per-bundle
19
+ * opt-in (nothing is held unless you `unlock` it), an absolute TTL, auto-lock
20
+ * on screen-lock / sleep, and `agents secrets lock`. Nothing ever touches disk.
21
+ *
22
+ * macOS only: Linux libsecret has no biometry prompt, so there's nothing to
23
+ * deduplicate — every entry point here no-ops off darwin.
24
+ */
25
+ import type { SecretsBundle } from './bundles.js';
26
+ /** Default lifetime of an unlocked bundle when `--ttl` is not given. */
27
+ export declare const DEFAULT_TTL_MS: number;
28
+ export interface StoredBundle {
29
+ bundle: SecretsBundle;
30
+ env: Record<string, string>;
31
+ /** epoch ms; the entry is gone once Date.now() passes this. */
32
+ expiresAt: number;
33
+ }
34
+ /** One unlocked bundle as reported by `status`. */
35
+ export interface AgentStatusEntry {
36
+ name: string;
37
+ expiresAt: number;
38
+ keyCount: number;
39
+ }
40
+ export type Request = {
41
+ cmd: 'ping';
42
+ } | {
43
+ cmd: 'get';
44
+ name: string;
45
+ } | {
46
+ cmd: 'load';
47
+ name: string;
48
+ bundle: SecretsBundle;
49
+ env: Record<string, string>;
50
+ ttlMs: number;
51
+ } | {
52
+ cmd: 'lock';
53
+ name?: string;
54
+ } | {
55
+ cmd: 'status';
56
+ };
57
+ export type Response = {
58
+ ok: true;
59
+ cmd: 'ping';
60
+ version: number;
61
+ } | {
62
+ ok: true;
63
+ cmd: 'get';
64
+ hit: false;
65
+ } | {
66
+ ok: true;
67
+ cmd: 'get';
68
+ hit: true;
69
+ bundle: SecretsBundle;
70
+ env: Record<string, string>;
71
+ } | {
72
+ ok: true;
73
+ cmd: 'load';
74
+ } | {
75
+ ok: true;
76
+ cmd: 'lock';
77
+ wiped: number;
78
+ } | {
79
+ ok: true;
80
+ cmd: 'status';
81
+ entries: AgentStatusEntry[];
82
+ } | {
83
+ ok: false;
84
+ error: string;
85
+ };
86
+ /**
87
+ * Pure request handler over the in-memory store. Extracted so the store
88
+ * semantics (lazy expiry on get/status, lock-one vs lock-all, load TTL) are
89
+ * unit-testable with a controlled `now`, without a socket or a spawned process.
90
+ * Mutates `store` in place; returns the wire response.
91
+ */
92
+ export declare function handleAgentRequest(store: Map<string, StoredBundle>, req: Request, now?: number): Response;
93
+ /**
94
+ * Run the broker in the foreground. Spawned detached by ensureAgentRunning via
95
+ * `agents secrets _agent-run`. Holds the store in memory, serves the socket,
96
+ * sweeps expired entries, wipes on screen-lock/sleep, and self-exits when idle.
97
+ */
98
+ export declare function runSecretsAgent(): Promise<void>;
99
+ /** True if a broker socket exists at all. Cheap; gates the sync read so the
100
+ * never-unlocked path stays a single stat. */
101
+ export declare function agentSocketExists(): boolean;
102
+ /**
103
+ * Synchronous read for the hot path. Returns the cached resolved bundle, or
104
+ * null if the agent isn't running / doesn't hold this bundle / anything fails
105
+ * (soft — caller falls through to the real keychain). macOS only.
106
+ */
107
+ export declare function agentGetSync(name: string): {
108
+ bundle: SecretsBundle;
109
+ env: Record<string, string>;
110
+ } | null;
111
+ /** True when `secrets.agent.auto` is enabled in agents.yaml. Best-effort; a
112
+ * missing/unreadable meta reads as off. */
113
+ export declare function secretsAgentAutoEnabled(): boolean;
114
+ /**
115
+ * Fire-and-forget: populate the broker with a freshly-resolved bundle so the
116
+ * NEXT process reads it without a prompt. Used by the auto-cache path after a
117
+ * real keychain read of a `session`-tier bundle. Adds no latency to the caller
118
+ * — it spawns the agent (if needed) and a detached loader, both unref'd, then
119
+ * returns immediately. Entirely best-effort; never throws. macOS only.
120
+ */
121
+ export declare function agentAutoLoadSync(name: string, bundle: SecretsBundle, env: Record<string, string>, ttlMs: number): void;
122
+ /** Store a resolved bundle in the broker. Returns false on transport failure. */
123
+ export declare function agentLoad(name: string, bundle: SecretsBundle, env: Record<string, string>, ttlMs: number): Promise<boolean>;
124
+ /** Wipe one bundle (or all if name omitted) from the broker. Returns the count
125
+ * wiped, or 0 when no broker is running. */
126
+ export declare function agentLock(name?: string): Promise<number>;
127
+ /** List currently-unlocked bundles, or [] when no broker is running. */
128
+ export declare function agentStatus(): Promise<AgentStatusEntry[]>;
129
+ /**
130
+ * Ensure a broker is running and reachable, spawning one detached if not.
131
+ * Returns true once the socket answers a ping. On protocol-version skew, kills
132
+ * the stale broker and respawns. macOS only.
133
+ */
134
+ export declare function ensureAgentRunning(timeoutMs?: number): Promise<boolean>;