@phnx-labs/agents-cli 1.22.43 → 1.22.45

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 (164) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +14 -12
  3. package/dist/bootstrap.js +3 -6
  4. package/dist/cli/command-registry.d.ts +0 -7
  5. package/dist/cli/command-registry.js +1 -21
  6. package/dist/commands/accounts.d.ts +1 -9
  7. package/dist/commands/accounts.js +12 -90
  8. package/dist/commands/apply.d.ts +7 -10
  9. package/dist/commands/apply.js +15 -35
  10. package/dist/commands/artifacts.js +4 -5
  11. package/dist/commands/audit.d.ts +6 -6
  12. package/dist/commands/audit.js +12 -12
  13. package/dist/commands/beta.d.ts +7 -1
  14. package/dist/commands/beta.js +16 -9
  15. package/dist/commands/commands.js +1 -1
  16. package/dist/commands/doctor.js +1 -4
  17. package/dist/commands/events.d.ts +2 -2
  18. package/dist/commands/events.js +6 -5
  19. package/dist/commands/exec.d.ts +1 -1
  20. package/dist/commands/exec.js +8 -4
  21. package/dist/commands/factory.d.ts +1 -1
  22. package/dist/commands/fleet-capture.d.ts +1 -1
  23. package/dist/commands/fleet-capture.js +4 -5
  24. package/dist/commands/harness.js +1 -49
  25. package/dist/commands/hooks.js +1 -1
  26. package/dist/commands/insights.d.ts +1 -1
  27. package/dist/commands/insights.js +85 -156
  28. package/dist/commands/menubar.js +58 -1
  29. package/dist/commands/output.js +2 -2
  30. package/dist/commands/prune.js +5 -4
  31. package/dist/commands/routines.test-fixture.d.ts +86 -0
  32. package/dist/commands/routines.test-fixture.js +345 -0
  33. package/dist/commands/sessions.test-fixture.d.ts +24 -0
  34. package/dist/commands/sessions.test-fixture.js +224 -0
  35. package/dist/commands/setup.js +5 -1
  36. package/dist/commands/share.d.ts +9 -10
  37. package/dist/commands/share.js +68 -50
  38. package/dist/commands/skills.js +1 -1
  39. package/dist/commands/ssh.js +15 -62
  40. package/dist/commands/subagents.js +1 -1
  41. package/dist/commands/view.js +5 -2
  42. package/dist/lib/analytics/mix-commands.d.ts +6 -28
  43. package/dist/lib/analytics/mix-commands.js +14 -40
  44. package/dist/lib/audit/log.d.ts +2 -2
  45. package/dist/lib/audit/log.js +2 -2
  46. package/dist/lib/beta.js +1 -1
  47. package/dist/lib/browser/domain-skills.d.ts +26 -6
  48. package/dist/lib/browser/domain-skills.js +81 -43
  49. package/dist/lib/daemon/daemon.js +11 -0
  50. package/dist/lib/daemon/daemon.test-fixture.d.ts +24 -0
  51. package/dist/lib/daemon/daemon.test-fixture.js +71 -0
  52. package/dist/lib/daemon-ticks.d.ts +38 -1
  53. package/dist/lib/daemon-ticks.js +52 -5
  54. package/dist/lib/device-config.js +1 -2
  55. package/dist/lib/devices/discovery-policy.d.ts +3 -3
  56. package/dist/lib/devices/discovery-policy.js +3 -3
  57. package/dist/lib/devices/fleet.d.ts +4 -8
  58. package/dist/lib/devices/fleet.js +3 -15
  59. package/dist/lib/devices/health-report.js +3 -3
  60. package/dist/lib/devices/pool.d.ts +0 -6
  61. package/dist/lib/devices/pool.js +0 -6
  62. package/dist/lib/devices/registry.d.ts +0 -15
  63. package/dist/lib/devices/registry.js +0 -9
  64. package/dist/lib/devices/stats-cache.d.ts +12 -0
  65. package/dist/lib/devices/stats-cache.js +24 -5
  66. package/dist/lib/fleet/capture.d.ts +1 -1
  67. package/dist/lib/fleet/manifest.js +1 -1
  68. package/dist/lib/fleet/remote-login.d.ts +2 -2
  69. package/dist/lib/fleet/remote-login.js +3 -4
  70. package/dist/lib/hosts/dispatch.d.ts +2 -0
  71. package/dist/lib/hosts/dispatch.js +8 -1
  72. package/dist/lib/hosts/passthrough.js +0 -1
  73. package/dist/lib/hosts/providers/devices.js +1 -13
  74. package/dist/lib/hosts/ready.d.ts +12 -3
  75. package/dist/lib/hosts/ready.js +27 -12
  76. package/dist/lib/hosts/registry.d.ts +4 -5
  77. package/dist/lib/hosts/registry.js +3 -7
  78. package/dist/lib/installations/versions.js +21 -2
  79. package/dist/lib/menubar/install-menubar.d.ts +109 -0
  80. package/dist/lib/menubar/install-menubar.js +199 -4
  81. package/dist/lib/remote-agents-json.js +1 -7
  82. package/dist/lib/routines-placement.d.ts +1 -1
  83. package/dist/lib/routines-placement.js +1 -1
  84. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  85. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
  86. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  87. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
  88. package/dist/lib/session/active.d.ts +4 -16
  89. package/dist/lib/session/active.js +2 -6
  90. package/dist/lib/session/db.d.ts +13 -98
  91. package/dist/lib/session/db.js +4 -11
  92. package/dist/lib/session/discover.d.ts +5 -81
  93. package/dist/lib/session/discover.js +5 -5
  94. package/dist/lib/session/remote/remote-bundle.d.ts +12 -0
  95. package/dist/lib/session/remote/remote-bundle.js +61 -0
  96. package/dist/lib/session/remote/remote-list.d.ts +148 -0
  97. package/dist/lib/session/remote/remote-list.js +607 -0
  98. package/dist/lib/session/remote/remote.d.ts +132 -0
  99. package/dist/lib/session/remote/remote.js +314 -0
  100. package/dist/lib/session/remote/watch.d.ts +101 -0
  101. package/dist/lib/session/remote/watch.js +241 -0
  102. package/dist/lib/session/remote-bundle.d.ts +1 -12
  103. package/dist/lib/session/remote-bundle.js +3 -61
  104. package/dist/lib/session/remote-list.d.ts +1 -148
  105. package/dist/lib/session/remote-list.js +3 -607
  106. package/dist/lib/session/remote.d.ts +1 -132
  107. package/dist/lib/session/remote.js +3 -314
  108. package/dist/lib/session/watch.d.ts +1 -101
  109. package/dist/lib/session/watch.js +3 -242
  110. package/dist/lib/share/config.d.ts +12 -2
  111. package/dist/lib/share/config.js +47 -11
  112. package/dist/lib/share/delete.js +1 -1
  113. package/dist/lib/share/publish.js +1 -1
  114. package/dist/lib/share/worker-template.js +2 -2
  115. package/dist/lib/smart-launch.d.ts +4 -4
  116. package/dist/lib/smart-launch.js +8 -18
  117. package/dist/lib/staleness/index.d.ts +3 -1
  118. package/dist/lib/staleness/index.js +21 -1
  119. package/dist/lib/staleness/types.d.ts +11 -0
  120. package/dist/lib/staleness/writers/commands.js +13 -4
  121. package/dist/lib/staleness/writers/hooks.js +3 -1
  122. package/dist/lib/staleness/writers/mcp.d.ts +0 -7
  123. package/dist/lib/staleness/writers/mcp.js +16 -1
  124. package/dist/lib/staleness/writers/rules.js +1 -1
  125. package/dist/lib/staleness/writers/skills.js +3 -1
  126. package/dist/lib/staleness/writers/subagents.d.ts +0 -9
  127. package/dist/lib/staleness/writers/subagents.js +18 -1
  128. package/dist/lib/staleness/writers/types.d.ts +10 -0
  129. package/dist/lib/startup/command-registry.d.ts +8 -1
  130. package/dist/lib/startup/command-registry.js +21 -6
  131. package/dist/lib/startup/root-command.d.ts +18 -1
  132. package/dist/lib/startup/root-command.js +18 -1
  133. package/dist/lib/state.js +7 -0
  134. package/dist/lib/teams/placement-probe.js +27 -12
  135. package/dist/lib/teams/scheduler.d.ts +3 -1
  136. package/dist/lib/types.d.ts +1 -1
  137. package/dist/lib/view-types.d.ts +3 -0
  138. package/package.json +2 -3
  139. package/dist/bin/agents +0 -0
  140. package/dist/commands/auth.d.ts +0 -9
  141. package/dist/commands/auth.js +0 -108
  142. package/dist/commands/org.d.ts +0 -6
  143. package/dist/commands/org.js +0 -175
  144. package/dist/commands/serve.d.ts +0 -10
  145. package/dist/commands/serve.js +0 -68
  146. package/dist/commands/trends.d.ts +0 -10
  147. package/dist/commands/trends.js +0 -12
  148. package/dist/lib/entitlement.d.ts +0 -31
  149. package/dist/lib/entitlement.js +0 -137
  150. package/dist/lib/prix-account.d.ts +0 -158
  151. package/dist/lib/prix-account.js +0 -214
  152. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
  153. package/dist/lib/serve/control.d.ts +0 -95
  154. package/dist/lib/serve/control.js +0 -260
  155. package/dist/lib/serve/data.d.ts +0 -81
  156. package/dist/lib/serve/data.js +0 -91
  157. package/dist/lib/serve/page.d.ts +0 -7
  158. package/dist/lib/serve/page.js +0 -140
  159. package/dist/lib/serve/server.d.ts +0 -80
  160. package/dist/lib/serve/server.js +0 -145
  161. package/dist/lib/serve/stream.d.ts +0 -43
  162. package/dist/lib/serve/stream.js +0 -116
  163. package/dist/lib/serve/token.d.ts +0 -35
  164. package/dist/lib/serve/token.js +0 -85
@@ -1,175 +0,0 @@
1
- import chalk from 'chalk';
2
- import { setHelpSections } from '../lib/help.js';
3
- import { runOrDie } from '../lib/format.js';
4
- import { createSpace, createSpaceInvite, fetchWhoAmI, listSpaceMembers, listSpaces, removeSpaceMember, resolveMemberFromList, resolvePrixToken, resolveSpaceFromList, slugify, updateSpaceMemberRole, } from '../lib/prix-account.js';
5
- /** Every `agents org` subcommand needs a token up front — one place to say so. */
6
- function requireToken() {
7
- if (!resolvePrixToken())
8
- throw new Error("Not signed in. Run 'agents auth login' first.");
9
- }
10
- async function resolveSpace(explicit) {
11
- const spaces = await listSpaces();
12
- return resolveSpaceFromList(spaces, explicit);
13
- }
14
- function printSpace(space) {
15
- console.log(`${chalk.bold(space.name)} ${chalk.gray(space.slug)} ${chalk.gray(space.id)}`);
16
- console.log(` role: ${space.user_role}${space.organization_id ? ` org: ${space.organization_id}` : ''}`);
17
- }
18
- function printMembers(members) {
19
- if (!members.length) {
20
- console.log(chalk.gray(' No members.'));
21
- return;
22
- }
23
- for (const m of members)
24
- console.log(` ${chalk.cyan(m.email)} ${m.role} ${chalk.gray(m.user_id)}`);
25
- }
26
- async function runCreate(name, o) {
27
- requireToken();
28
- const space = await createSpace({ name, slug: o.slug ?? slugify(name), description: o.description });
29
- if (o.json) {
30
- console.log(JSON.stringify(space, null, 2));
31
- return;
32
- }
33
- console.log(chalk.green(`Created space '${space.name}' (${space.slug}).`));
34
- printSpace(space);
35
- }
36
- async function runList(o) {
37
- requireToken();
38
- const spaces = await listSpaces();
39
- if (o.json) {
40
- console.log(JSON.stringify(spaces, null, 2));
41
- return;
42
- }
43
- if (!spaces.length) {
44
- console.log(chalk.gray("No spaces. Create one with 'agents org create <name>'."));
45
- return;
46
- }
47
- for (const space of spaces)
48
- printSpace(space);
49
- }
50
- async function runView(spaceArg, o) {
51
- requireToken();
52
- const space = await resolveSpace(spaceArg);
53
- if (o.json) {
54
- console.log(JSON.stringify(space, null, 2));
55
- return;
56
- }
57
- printSpace(space);
58
- }
59
- /** `--role` defaults to member when omitted; anything present must be a real role — never silently coerced. */
60
- export function resolveInviteRole(raw) {
61
- return raw === undefined ? 'member' : parseRole(raw);
62
- }
63
- async function runInvite(email, o) {
64
- requireToken();
65
- const role = resolveInviteRole(o.role);
66
- const space = await resolveSpace(o.space);
67
- const result = await createSpaceInvite(space.id, email, role);
68
- if (o.json) {
69
- console.log(JSON.stringify(result, null, 2));
70
- return;
71
- }
72
- if (result.member_added)
73
- console.log(chalk.green(`Added ${email} to '${space.name}' as ${role}.`));
74
- else
75
- console.log(chalk.green(`Invited ${email} to '${space.name}' as ${role}. They'll get an email.`));
76
- }
77
- async function runMembers(spaceArg, o) {
78
- requireToken();
79
- const space = await resolveSpace(spaceArg);
80
- const members = await listSpaceMembers(space.id);
81
- if (o.json) {
82
- console.log(JSON.stringify(members, null, 2));
83
- return;
84
- }
85
- console.log(chalk.bold(`${space.name} (${members.length} member${members.length === 1 ? '' : 's'})`));
86
- printMembers(members);
87
- }
88
- /** Validate a role argument before any network call — same rule the backend enforces server-side. */
89
- export function parseRole(raw) {
90
- if (raw === 'admin' || raw === 'member')
91
- return raw;
92
- throw new Error(`role must be 'admin' or 'member', got '${raw}'.`);
93
- }
94
- async function runRole(email, roleRaw, o) {
95
- requireToken();
96
- const role = parseRole(roleRaw);
97
- const space = await resolveSpace(o.space);
98
- const members = await listSpaceMembers(space.id);
99
- const member = resolveMemberFromList(members, email);
100
- const result = await updateSpaceMemberRole(space.id, member.user_id, role);
101
- if (o.json) {
102
- console.log(JSON.stringify(result, null, 2));
103
- return;
104
- }
105
- console.log(chalk.green(`${email} is now ${role} in '${space.name}'.`));
106
- }
107
- async function runRemove(email, o) {
108
- requireToken();
109
- const space = await resolveSpace(o.space);
110
- const members = await listSpaceMembers(space.id);
111
- const member = resolveMemberFromList(members, email);
112
- await removeSpaceMember(space.id, member.user_id);
113
- if (o.json) {
114
- console.log(JSON.stringify({ removed: email, space: space.slug }, null, 2));
115
- return;
116
- }
117
- console.log(chalk.green(`Removed ${email} from '${space.name}'.`));
118
- }
119
- async function runLeave(spaceArg, o) {
120
- requireToken();
121
- const resolved = resolvePrixToken();
122
- if (!resolved)
123
- throw new Error("Not signed in. Run 'agents auth login' first.");
124
- const who = await fetchWhoAmI(resolved.token);
125
- const space = await resolveSpace(spaceArg);
126
- if (space.owner_user_id === who.userId) {
127
- throw new Error(`You own '${space.name}'. Transfer ownership or delete the space instead of leaving it.`);
128
- }
129
- await removeSpaceMember(space.id, who.userId);
130
- if (o.json) {
131
- console.log(JSON.stringify({ left: space.slug }, null, 2));
132
- return;
133
- }
134
- console.log(chalk.green(`Left '${space.name}'.`));
135
- }
136
- export function registerOrgCommand(program) {
137
- const org = program.command('org').description('Create and manage a team (a Rush "space") you can share with `agents auth login` collaborators');
138
- org.command('create <name>').description('Create a space (free tier: 1 owned space)')
139
- .option('--slug <slug>', 'Override the derived slug')
140
- .option('--description <text>', 'Optional description')
141
- .option('--json', 'Machine-readable output')
142
- .action((name, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runCreate(name, { ...o, json }), { json }); });
143
- org.command('list').description('List spaces you own or belong to').option('--json', 'Machine-readable output')
144
- .action((o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runList({ json }), { json }); });
145
- org.command('view [space]').description('Show one space (defaults to your only space)').option('--json', 'Machine-readable output')
146
- .action((space, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runView(space, { json }), { json }); });
147
- org.command('invite <email>').description('Invite (or directly add) a member')
148
- .option('--role <role>', 'admin | member', 'member')
149
- .option('--space <id-or-slug>', 'Space to invite into (defaults to your only space)')
150
- .option('--json', 'Machine-readable output')
151
- .action((email, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runInvite(email, { ...o, json }), { json }); });
152
- org.command('members [space]').description('List a space\'s members').option('--json', 'Machine-readable output')
153
- .action((space, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runMembers(space, { json }), { json }); });
154
- org.command('role <email> <role>').description('Change a member\'s role (owner-only for admin)')
155
- .option('--space <id-or-slug>', 'Space to change (defaults to your only space)')
156
- .option('--json', 'Machine-readable output')
157
- .action((email, role, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runRole(email, role, { ...o, json }), { json }); });
158
- org.command('remove <email>').description('Remove a member from a space')
159
- .option('--space <id-or-slug>', 'Space to remove from (defaults to your only space)')
160
- .option('--json', 'Machine-readable output')
161
- .action((email, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runRemove(email, { ...o, json }), { json }); });
162
- org.command('leave [space]').description('Leave a space you do not own').option('--json', 'Machine-readable output')
163
- .action((space, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runLeave(space, { json }), { json }); });
164
- setHelpSections(org, {
165
- examples: `agents org create acme-team
166
- agents org list
167
- agents org view acme-team
168
- agents org invite dev@example.com --role admin
169
- agents org members
170
- agents org role dev@example.com admin
171
- agents org remove dev@example.com
172
- agents org leave acme-team`,
173
- notes: 'Maps to the Rush backend\'s /api/v1/spaces (the free-tier team primitive), not /api/v1/orgs. Free tier: 1 owned space, 3 members per space. Every command needs `agents auth login` (or a `rush login` session) first. `--space` is only needed once you belong to more than one space.',
174
- });
175
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * `agents serve` — read-only local web companion.
3
- *
4
- * Boots a localhost-only (127.0.0.1) HTTP + SSE server that renders a live
5
- * dashboard of team status + per-worktree diffs, scheduled routines, and cloud
6
- * tasks. Everything is a viewer over data other commands already own — there
7
- * are no mutation endpoints. See src/lib/serve/.
8
- */
9
- import type { Command } from 'commander';
10
- export declare function registerServeCommand(program: Command): void;
@@ -1,68 +0,0 @@
1
- import chalk from 'chalk';
2
- import { startServeServer, DEFAULT_SERVE_PORT, SERVE_HOST } from '../lib/serve/server.js';
3
- import { startControlServer } from '../lib/serve/control.js';
4
- import { ensureControlToken } from '../lib/serve/token.js';
5
- export function registerServeCommand(program) {
6
- program
7
- .command('serve')
8
- .description('Read-only local web companion: team diffs, routines, and cloud status (binds 127.0.0.1 only).')
9
- .option('-p, --port <n>', `Port to bind on ${SERVE_HOST}`, String(DEFAULT_SERVE_PORT))
10
- .option('--interval <ms>', 'SSE refresh cadence in milliseconds', '3000')
11
- .option('--control', 'Authenticated control mode (RUSH-1731): adds bearer-gated POST /api/run and /api/session/:id/message so the iOS cockpit can dispatch + steer. Enables binding beyond loopback — pair a device with `agents devices pair-ios`.', false)
12
- .option('--bind <addr>', 'Address to bind in --control mode (e.g. your tailnet IP, or 0.0.0.0). Ignored without --control; read-only serve is always loopback.', SERVE_HOST)
13
- .action(async (opts) => {
14
- const port = parseInt(opts.port ?? '', 10) || DEFAULT_SERVE_PORT;
15
- const intervalMs = parseInt(opts.interval ?? '', 10) || 3000;
16
- try {
17
- if (opts.control) {
18
- const bind = opts.bind || SERVE_HOST;
19
- const minted = ensureControlToken('default');
20
- const { server, port: bound } = await startControlServer(port, bind, {
21
- cwd: process.cwd(),
22
- intervalMs,
23
- });
24
- const url = `http://${bind}:${bound}`;
25
- console.log(`${chalk.green('agents serve --control')} ${chalk.dim('→')} ${chalk.cyan(url)}`);
26
- if (minted.created) {
27
- console.log(chalk.yellow('New control token (shown once — store it in the cockpit now):'));
28
- console.log(` ${chalk.bold(minted.token)} ${chalk.dim(`(id ${minted.id})`)}`);
29
- }
30
- else {
31
- console.log(chalk.dim('Using existing control token(s). Issue another with `agents devices pair-ios`.'));
32
- }
33
- if (bind !== SERVE_HOST) {
34
- console.log(chalk.dim('bearer-gated · bind beyond loopback — keep this on the tailnet, never public Funnel · Ctrl-C to stop'));
35
- }
36
- else {
37
- console.log(chalk.dim('bearer-gated · loopback (pass --bind <tailnet-ip> to reach it from the phone) · Ctrl-C to stop'));
38
- }
39
- const shutdown = () => server.close(() => process.exit(0));
40
- process.on('SIGINT', shutdown);
41
- process.on('SIGTERM', shutdown);
42
- return;
43
- }
44
- const { server, port: bound } = await startServeServer(port, {
45
- cwd: process.cwd(),
46
- intervalMs,
47
- });
48
- const url = `http://${SERVE_HOST}:${bound}`;
49
- console.log(`${chalk.green('agents serve')} ${chalk.dim('→')} ${chalk.cyan(url)}`);
50
- console.log(chalk.dim('read-only · localhost only · Ctrl-C to stop'));
51
- const shutdown = () => {
52
- server.close(() => process.exit(0));
53
- };
54
- process.on('SIGINT', shutdown);
55
- process.on('SIGTERM', shutdown);
56
- }
57
- catch (err) {
58
- const msg = String(err?.message ?? err);
59
- if (msg.includes('EADDRINUSE')) {
60
- console.error(chalk.red(`Port ${port} is already in use. Pass --port <n> to pick another.`));
61
- }
62
- else {
63
- console.error(chalk.red(`Could not start serve: ${msg}`));
64
- }
65
- process.exit(1);
66
- }
67
- });
68
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Deprecated top-level spelling of the counter-mix tree.
3
- *
4
- * Implementation lives under `agents insights mix` (see
5
- * `lib/analytics/mix-commands.ts`). This file only registers the thin alias so
6
- * old scripts and agent briefs keep working while printing one deprecation line
7
- * — no second recipe implementation.
8
- */
9
- import type { Command } from 'commander';
10
- export declare function registerTrendsCommand(program: Command): void;
@@ -1,12 +0,0 @@
1
- /**
2
- * Deprecated top-level spelling of the counter-mix tree.
3
- *
4
- * Implementation lives under `agents insights mix` (see
5
- * `lib/analytics/mix-commands.ts`). This file only registers the thin alias so
6
- * old scripts and agent briefs keep working while printing one deprecation line
7
- * — no second recipe implementation.
8
- */
9
- import { registerDeprecatedTrendsAlias } from '../lib/analytics/mix-commands.js';
10
- export function registerTrendsCommand(program) {
11
- registerDeprecatedTrendsAlias(program);
12
- }
@@ -1,31 +0,0 @@
1
- /** How long a cached tier is trusted before the next call re-fetches. */
2
- export declare const ENTITLEMENT_CACHE_TTL_MS: number;
3
- export type EntitlementSource = 'live' | 'cache' | 'offline' | 'no-session';
4
- export interface EntitlementTier {
5
- /** The raw tier name from the backend ('free', 'admin', or a paid tier name). */
6
- tierName: string;
7
- /** true for 'admin' or any tier name other than 'free'. */
8
- isPaid: boolean;
9
- /** Where this value came from — mainly for diagnostics/tests. */
10
- source: EntitlementSource;
11
- }
12
- export declare function setEntitlementUserYamlPathForTest(value: string | null): void;
13
- export declare function setEntitlementCachePathForTest(value: string | null): void;
14
- export declare function setEntitlementFetchForTest(fn: typeof globalThis.fetch): void;
15
- export declare function entitlementCachePath(): string;
16
- /**
17
- * The live plan tier, cache-first with a TTL.
18
- *
19
- * - No `~/.rush/user.yaml` at all → free, no network call.
20
- * - A fresh cache entry (within {@link ENTITLEMENT_CACHE_TTL_MS}) is returned
21
- * with no network call.
22
- * - A stale or missing cache triggers a live fetch; on success the result is
23
- * cached and returned.
24
- * - A failed fetch (offline, timeout, non-2xx) falls back to a stale cache if
25
- * one exists — never silently drops a known-paid account to free just
26
- * because the network hiccuped — and only falls back to free when there is
27
- * no cache at all to fall back to.
28
- */
29
- export declare function getTier(): Promise<EntitlementTier>;
30
- /** Per-harness account cap for a tier: 3 on free, 10 on paid/admin. */
31
- export declare function accountCapForTier(tier: Pick<EntitlementTier, 'isPaid'>): number;
@@ -1,137 +0,0 @@
1
- /**
2
- * Plan-tier entitlement — the one place agents-cli reads a user's live billing
3
- * tier for plan gates (the `agents accounts` per-harness cap, the `agents
4
- * insights` paid split). Fetches `GET /api/v1/billing/subscription?agent=agi-cli`
5
- * using the Rush session token from `~/.rush/user.yaml` (same read pattern as
6
- * `lib/cloud/rush.ts` / `lib/secrets/drivers/rush.ts` — this module does not
7
- * import either, since neither exports its token reader), caches the result on
8
- * disk with a TTL, and stays offline-tolerant: a stale cache is honored over a
9
- * failed network call rather than silently dropping a paid account to free, and
10
- * no session file at all resolves straight to the free tier with no request.
11
- */
12
- import * as fs from 'node:fs';
13
- import * as os from 'node:os';
14
- import * as path from 'node:path';
15
- import * as yaml from 'yaml';
16
- import { getCacheDir } from './state.js';
17
- import { atomicWriteFileSync, ensureLockTarget, withFileLock } from './fs-atomic.js';
18
- const PROXY_BASE = process.env.RUSH_PROXY_BASE ?? 'https://api.prix.dev';
19
- const SUBSCRIPTION_PATH = '/api/v1/billing/subscription?agent=agi-cli';
20
- /** How long a cached tier is trusted before the next call re-fetches. */
21
- export const ENTITLEMENT_CACHE_TTL_MS = 15 * 60_000;
22
- /** Same shape as bootstrap.ts's fetchNpmPackageMetadata — a hung connection
23
- * (dead peer, a firewall dropping packets silently) must not block every
24
- * `accounts add`/`insights` call indefinitely once the cache goes stale. */
25
- const FETCH_TIMEOUT_MS = 5000;
26
- const FREE_TIER = { tierName: 'free', isPaid: false };
27
- // ─── Injectable seams (tests only — the lib's own real read/write paths) ────
28
- let userYamlPathOverride = null;
29
- let cachePathOverride = null;
30
- let fetchImpl = globalThis.fetch;
31
- export function setEntitlementUserYamlPathForTest(value) {
32
- userYamlPathOverride = value;
33
- }
34
- export function setEntitlementCachePathForTest(value) {
35
- cachePathOverride = value;
36
- }
37
- export function setEntitlementFetchForTest(fn) {
38
- fetchImpl = fn;
39
- }
40
- function userYamlPath() {
41
- return userYamlPathOverride ?? path.join(os.homedir(), '.rush', 'user.yaml');
42
- }
43
- export function entitlementCachePath() {
44
- return cachePathOverride ?? path.join(getCacheDir(), '.entitlement-cache.json');
45
- }
46
- function readRushToken() {
47
- const file = userYamlPath();
48
- if (!fs.existsSync(file))
49
- return null;
50
- try {
51
- const data = yaml.parse(fs.readFileSync(file, 'utf-8'));
52
- return data?.session?.access_token || null;
53
- }
54
- catch {
55
- return null;
56
- }
57
- }
58
- function readCache() {
59
- try {
60
- const parsed = JSON.parse(fs.readFileSync(entitlementCachePath(), 'utf-8'));
61
- if (parsed?.version === 1 && typeof parsed.tierName === 'string' && typeof parsed.fetchedAt === 'number')
62
- return parsed;
63
- }
64
- catch {
65
- // missing or corrupt — treat as no cache
66
- }
67
- return null;
68
- }
69
- /** Best-effort write; a failed cache write just means the next call re-fetches. */
70
- function writeCache(tierName, isPaid) {
71
- try {
72
- const target = entitlementCachePath();
73
- ensureLockTarget(target);
74
- withFileLock(target, () => {
75
- const entry = { version: 1, tierName, isPaid, fetchedAt: Date.now() };
76
- atomicWriteFileSync(target, JSON.stringify(entry, null, 2));
77
- });
78
- }
79
- catch {
80
- // best-effort
81
- }
82
- }
83
- /**
84
- * Only `tierName: "free"` (or an absent tier name) reads as free — every other
85
- * name (`admin`, or a real paid-tier name once agi-cli has a pricing manifest,
86
- * see the integration spec §4.4 G3) is treated as paid. Backend tier config for
87
- * agi-cli doesn't exist yet, so this deliberately does not lean on
88
- * `hasSubscription`/`needsUpgrade` nuance the backend isn't populating meaningfully
89
- * for this agent today.
90
- */
91
- function classifySubscription(sub) {
92
- const tierName = typeof sub.tierName === 'string' && sub.tierName ? sub.tierName : 'free';
93
- return { tierName, isPaid: tierName !== 'free' };
94
- }
95
- /**
96
- * The live plan tier, cache-first with a TTL.
97
- *
98
- * - No `~/.rush/user.yaml` at all → free, no network call.
99
- * - A fresh cache entry (within {@link ENTITLEMENT_CACHE_TTL_MS}) is returned
100
- * with no network call.
101
- * - A stale or missing cache triggers a live fetch; on success the result is
102
- * cached and returned.
103
- * - A failed fetch (offline, timeout, non-2xx) falls back to a stale cache if
104
- * one exists — never silently drops a known-paid account to free just
105
- * because the network hiccuped — and only falls back to free when there is
106
- * no cache at all to fall back to.
107
- */
108
- export async function getTier() {
109
- const token = readRushToken();
110
- if (!token)
111
- return { ...FREE_TIER, source: 'no-session' };
112
- const cached = readCache();
113
- if (cached && Date.now() - cached.fetchedAt < ENTITLEMENT_CACHE_TTL_MS) {
114
- return { tierName: cached.tierName, isPaid: cached.isPaid, source: 'cache' };
115
- }
116
- try {
117
- const res = await fetchImpl(`${PROXY_BASE}${SUBSCRIPTION_PATH}`, {
118
- headers: { Authorization: `Bearer ${token}` },
119
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
120
- });
121
- if (!res.ok)
122
- throw new Error(`billing/subscription responded ${res.status}`);
123
- const sub = (await res.json());
124
- const { tierName, isPaid } = classifySubscription(sub);
125
- writeCache(tierName, isPaid);
126
- return { tierName, isPaid, source: 'live' };
127
- }
128
- catch {
129
- if (cached)
130
- return { tierName: cached.tierName, isPaid: cached.isPaid, source: 'offline' };
131
- return { ...FREE_TIER, source: 'offline' };
132
- }
133
- }
134
- /** Per-harness account cap for a tier: 3 on free, 10 on paid/admin. */
135
- export function accountCapForTier(tier) {
136
- return tier.isPaid ? 10 : 3;
137
- }
@@ -1,158 +0,0 @@
1
- /**
2
- * Client for the Rush account layer (`api.prix.dev`) — backs `agents auth` and
3
- * `agents org`. `agents org` maps to `/api/v1/spaces`, not `/api/v1/orgs`: spaces
4
- * already carry the free-tier caps (1 owned space, 3 members) and can exist
5
- * standalone with no parent organization, matching a "just want a team" CLI flow
6
- * better than the heavier enterprise-tenancy `orgs` routes (domain, SSO). See
7
- * `.agents/artifacts/2026-08-20/agi-cli-client-integration-spec.md` §4.2 in the
8
- * agi-cli-web repo for the full reasoning and the live-confirmed route/shape audit.
9
- *
10
- * Token custody: `agents auth login` writes its own session file
11
- * (`getPrixSessionFile()`), separate from `~/.rush/user.yaml`, so `agents auth
12
- * logout` never signs the user out of `rush` (the two CLIs share a backend
13
- * account, not a credential store). Reads fall back to `~/.rush/user.yaml`
14
- * (the pattern `readRushToken` in `lib/secrets/drivers/rush.ts` already uses)
15
- * so a user who is only signed in via `rush login` still gets a working
16
- * `agents auth whoami` / `agents org` with zero extra login step.
17
- */
18
- export declare const PRIX_API_BASE = "https://api.prix.dev";
19
- /** The session `agents auth login` owns. */
20
- export interface PrixSession {
21
- access_token: string;
22
- refresh_token?: string;
23
- /** Unix ms. */
24
- expires_at?: number;
25
- email?: string;
26
- userId?: string;
27
- }
28
- export declare function getPrixSessionFile(): string;
29
- export declare function readPrixSession(): PrixSession | null;
30
- export declare function writePrixSession(session: PrixSession): void;
31
- export declare function clearPrixSession(): boolean;
32
- /** Where the caller's Bearer token came from — surfaced by `whoami` so the user knows which login is live. */
33
- export type PrixTokenSource = 'agents' | 'rush';
34
- export declare function resolvePrixToken(): {
35
- token: string;
36
- source: PrixTokenSource;
37
- } | null;
38
- export declare class PrixApiError extends Error {
39
- status: number;
40
- constructor(status: number, message: string);
41
- }
42
- export interface WhoAmI {
43
- userId: string;
44
- email: string;
45
- valid: true;
46
- }
47
- /** `GET /api/v1/auth/me` — live-confirmed shape: `{email, userId, valid}`. */
48
- export declare function fetchWhoAmI(token?: string): Promise<WhoAmI>;
49
- export interface DeviceAuthorization {
50
- device_code: string;
51
- user_code: string;
52
- verification_uri: string;
53
- verification_uri_complete: string;
54
- expires_in: number;
55
- interval: number;
56
- }
57
- /** `POST /api/v1/auth/device/authorization` — public, no token. */
58
- export declare function startDeviceAuthorization(): Promise<DeviceAuthorization>;
59
- export type DeviceTokenPoll = {
60
- status: 'authorized';
61
- access_token: string;
62
- refresh_token?: string;
63
- expires_in?: number;
64
- user: {
65
- email: string;
66
- id: string;
67
- };
68
- } | {
69
- status: 'pending';
70
- } | {
71
- status: 'slow_down';
72
- } | {
73
- status: 'expired';
74
- } | {
75
- status: 'denied';
76
- };
77
- /** `POST /api/v1/auth/device/token` — one poll attempt. Callers own the interval loop. */
78
- export declare function pollDeviceToken(deviceCode: string): Promise<DeviceTokenPoll>;
79
- export interface SpaceSummary {
80
- id: string;
81
- slug: string;
82
- name: string;
83
- organization_id: string | null;
84
- owner_user_id: string;
85
- invite_code?: string;
86
- user_role: 'owner' | 'admin' | 'member';
87
- created_at: string;
88
- }
89
- export interface SpaceMember {
90
- user_id: string;
91
- email: string;
92
- name?: string;
93
- avatar_url?: string;
94
- role: 'owner' | 'admin' | 'member';
95
- joined_at: string;
96
- }
97
- /** `GET /api/v1/spaces` — live-confirmed: array of `SpaceSummary`. */
98
- export declare function listSpaces(): Promise<SpaceSummary[]>;
99
- /** `POST /api/v1/spaces` — 403 if the caller already owns a space (free tier: 1). */
100
- export declare function createSpace(input: {
101
- name: string;
102
- slug: string;
103
- description?: string;
104
- }): Promise<SpaceSummary>;
105
- /** `GET /api/v1/spaces/:id` — requires membership. */
106
- export declare function getSpace(spaceId: string): Promise<SpaceSummary>;
107
- /** `GET /api/v1/spaces/:id/members`. */
108
- export declare function listSpaceMembers(spaceId: string): Promise<SpaceMember[]>;
109
- export type CreateSpaceInviteResult = {
110
- invited: true;
111
- email: string;
112
- role: string;
113
- member_added: true;
114
- } | {
115
- invited: true;
116
- email: string;
117
- role: string;
118
- invite_code: string;
119
- member_added?: false;
120
- };
121
- /** `POST /api/v1/spaces/:id/invites` — sends a real email for the pending-invite path. */
122
- export declare function createSpaceInvite(spaceId: string, email: string, role?: 'admin' | 'member'): Promise<CreateSpaceInviteResult>;
123
- export interface SpaceInvite {
124
- id: string;
125
- space_id: string;
126
- email: string;
127
- role: 'admin' | 'member';
128
- invite_code: string;
129
- created_at: string;
130
- }
131
- /** `GET /api/v1/spaces/:id/invites`. */
132
- export declare function listSpaceInvites(spaceId: string): Promise<SpaceInvite[]>;
133
- /** `DELETE /api/v1/spaces/:id/invites/:inviteId`. */
134
- export declare function revokeSpaceInvite(spaceId: string, inviteId: string): Promise<{
135
- revoked: true;
136
- }>;
137
- /** `PATCH /api/v1/spaces/:id/members/:userId` — owner-only for admin changes. Route takes userId, not email. */
138
- export declare function updateSpaceMemberRole(spaceId: string, userId: string, role: 'admin' | 'member'): Promise<{
139
- user_id: string;
140
- role: string;
141
- updated: true;
142
- }>;
143
- /** `DELETE /api/v1/spaces/:id/members/:userId` — owner, admin, or the member themself (leave). */
144
- export declare function removeSpaceMember(spaceId: string, userId: string): Promise<void>;
145
- /** `DELETE /api/v1/spaces/:id` — soft delete, 30-day restore window. */
146
- export declare function deleteSpace(spaceId: string): Promise<void>;
147
- /** `agents-cli-space-name` -> `agi-cli-space-name`; lowercase, hyphenated, matches the backend's `^[a-z0-9-]+$` slug rule. */
148
- export declare function slugify(name: string): string;
149
- /**
150
- * Resolve the `--space` a command should act on: an explicit id/slug match
151
- * against the caller's own space list, or — with nothing passed — the
152
- * caller's sole space (free tier caps ownership at one, so this is almost
153
- * always unambiguous). Pure over an already-fetched list so it's cheaply
154
- * unit-testable with a fixture.
155
- */
156
- export declare function resolveSpaceFromList(spaces: SpaceSummary[], explicit?: string): SpaceSummary;
157
- /** Resolve a member's email to their `user_id` from an already-fetched member list. */
158
- export declare function resolveMemberFromList(members: SpaceMember[], email: string): SpaceMember;