@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.
- package/CHANGELOG.md +85 -0
- package/README.md +14 -12
- package/dist/bootstrap.js +3 -6
- package/dist/cli/command-registry.d.ts +0 -7
- package/dist/cli/command-registry.js +1 -21
- package/dist/commands/accounts.d.ts +1 -9
- package/dist/commands/accounts.js +12 -90
- package/dist/commands/apply.d.ts +7 -10
- package/dist/commands/apply.js +15 -35
- package/dist/commands/artifacts.js +4 -5
- package/dist/commands/audit.d.ts +6 -6
- package/dist/commands/audit.js +12 -12
- package/dist/commands/beta.d.ts +7 -1
- package/dist/commands/beta.js +16 -9
- package/dist/commands/commands.js +1 -1
- package/dist/commands/doctor.js +1 -4
- package/dist/commands/events.d.ts +2 -2
- package/dist/commands/events.js +6 -5
- package/dist/commands/exec.d.ts +1 -1
- package/dist/commands/exec.js +8 -4
- package/dist/commands/factory.d.ts +1 -1
- package/dist/commands/fleet-capture.d.ts +1 -1
- package/dist/commands/fleet-capture.js +4 -5
- package/dist/commands/harness.js +1 -49
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/insights.d.ts +1 -1
- package/dist/commands/insights.js +85 -156
- package/dist/commands/menubar.js +58 -1
- package/dist/commands/output.js +2 -2
- package/dist/commands/prune.js +5 -4
- package/dist/commands/routines.test-fixture.d.ts +86 -0
- package/dist/commands/routines.test-fixture.js +345 -0
- package/dist/commands/sessions.test-fixture.d.ts +24 -0
- package/dist/commands/sessions.test-fixture.js +224 -0
- package/dist/commands/setup.js +5 -1
- package/dist/commands/share.d.ts +9 -10
- package/dist/commands/share.js +68 -50
- package/dist/commands/skills.js +1 -1
- package/dist/commands/ssh.js +15 -62
- package/dist/commands/subagents.js +1 -1
- package/dist/commands/view.js +5 -2
- package/dist/lib/analytics/mix-commands.d.ts +6 -28
- package/dist/lib/analytics/mix-commands.js +14 -40
- package/dist/lib/audit/log.d.ts +2 -2
- package/dist/lib/audit/log.js +2 -2
- package/dist/lib/beta.js +1 -1
- package/dist/lib/browser/domain-skills.d.ts +26 -6
- package/dist/lib/browser/domain-skills.js +81 -43
- package/dist/lib/daemon/daemon.js +11 -0
- package/dist/lib/daemon/daemon.test-fixture.d.ts +24 -0
- package/dist/lib/daemon/daemon.test-fixture.js +71 -0
- package/dist/lib/daemon-ticks.d.ts +38 -1
- package/dist/lib/daemon-ticks.js +52 -5
- package/dist/lib/device-config.js +1 -2
- package/dist/lib/devices/discovery-policy.d.ts +3 -3
- package/dist/lib/devices/discovery-policy.js +3 -3
- package/dist/lib/devices/fleet.d.ts +4 -8
- package/dist/lib/devices/fleet.js +3 -15
- package/dist/lib/devices/health-report.js +3 -3
- package/dist/lib/devices/pool.d.ts +0 -6
- package/dist/lib/devices/pool.js +0 -6
- package/dist/lib/devices/registry.d.ts +0 -15
- package/dist/lib/devices/registry.js +0 -9
- package/dist/lib/devices/stats-cache.d.ts +12 -0
- package/dist/lib/devices/stats-cache.js +24 -5
- package/dist/lib/fleet/capture.d.ts +1 -1
- package/dist/lib/fleet/manifest.js +1 -1
- package/dist/lib/fleet/remote-login.d.ts +2 -2
- package/dist/lib/fleet/remote-login.js +3 -4
- package/dist/lib/hosts/dispatch.d.ts +2 -0
- package/dist/lib/hosts/dispatch.js +8 -1
- package/dist/lib/hosts/passthrough.js +0 -1
- package/dist/lib/hosts/providers/devices.js +1 -13
- package/dist/lib/hosts/ready.d.ts +12 -3
- package/dist/lib/hosts/ready.js +27 -12
- package/dist/lib/hosts/registry.d.ts +4 -5
- package/dist/lib/hosts/registry.js +3 -7
- package/dist/lib/installations/versions.js +21 -2
- package/dist/lib/menubar/install-menubar.d.ts +109 -0
- package/dist/lib/menubar/install-menubar.js +199 -4
- package/dist/lib/remote-agents-json.js +1 -7
- package/dist/lib/routines-placement.d.ts +1 -1
- package/dist/lib/routines-placement.js +1 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
- package/dist/lib/session/active.d.ts +4 -16
- package/dist/lib/session/active.js +2 -6
- package/dist/lib/session/db.d.ts +13 -98
- package/dist/lib/session/db.js +4 -11
- package/dist/lib/session/discover.d.ts +5 -81
- package/dist/lib/session/discover.js +5 -5
- package/dist/lib/session/remote/remote-bundle.d.ts +12 -0
- package/dist/lib/session/remote/remote-bundle.js +61 -0
- package/dist/lib/session/remote/remote-list.d.ts +148 -0
- package/dist/lib/session/remote/remote-list.js +607 -0
- package/dist/lib/session/remote/remote.d.ts +132 -0
- package/dist/lib/session/remote/remote.js +314 -0
- package/dist/lib/session/remote/watch.d.ts +101 -0
- package/dist/lib/session/remote/watch.js +241 -0
- package/dist/lib/session/remote-bundle.d.ts +1 -12
- package/dist/lib/session/remote-bundle.js +3 -61
- package/dist/lib/session/remote-list.d.ts +1 -148
- package/dist/lib/session/remote-list.js +3 -607
- package/dist/lib/session/remote.d.ts +1 -132
- package/dist/lib/session/remote.js +3 -314
- package/dist/lib/session/watch.d.ts +1 -101
- package/dist/lib/session/watch.js +3 -242
- package/dist/lib/share/config.d.ts +12 -2
- package/dist/lib/share/config.js +47 -11
- package/dist/lib/share/delete.js +1 -1
- package/dist/lib/share/publish.js +1 -1
- package/dist/lib/share/worker-template.js +2 -2
- package/dist/lib/smart-launch.d.ts +4 -4
- package/dist/lib/smart-launch.js +8 -18
- package/dist/lib/staleness/index.d.ts +3 -1
- package/dist/lib/staleness/index.js +21 -1
- package/dist/lib/staleness/types.d.ts +11 -0
- package/dist/lib/staleness/writers/commands.js +13 -4
- package/dist/lib/staleness/writers/hooks.js +3 -1
- package/dist/lib/staleness/writers/mcp.d.ts +0 -7
- package/dist/lib/staleness/writers/mcp.js +16 -1
- package/dist/lib/staleness/writers/rules.js +1 -1
- package/dist/lib/staleness/writers/skills.js +3 -1
- package/dist/lib/staleness/writers/subagents.d.ts +0 -9
- package/dist/lib/staleness/writers/subagents.js +18 -1
- package/dist/lib/staleness/writers/types.d.ts +10 -0
- package/dist/lib/startup/command-registry.d.ts +8 -1
- package/dist/lib/startup/command-registry.js +21 -6
- package/dist/lib/startup/root-command.d.ts +18 -1
- package/dist/lib/startup/root-command.js +18 -1
- package/dist/lib/state.js +7 -0
- package/dist/lib/teams/placement-probe.js +27 -12
- package/dist/lib/teams/scheduler.d.ts +3 -1
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/view-types.d.ts +3 -0
- package/package.json +2 -3
- package/dist/bin/agents +0 -0
- package/dist/commands/auth.d.ts +0 -9
- package/dist/commands/auth.js +0 -108
- package/dist/commands/org.d.ts +0 -6
- package/dist/commands/org.js +0 -175
- package/dist/commands/serve.d.ts +0 -10
- package/dist/commands/serve.js +0 -68
- package/dist/commands/trends.d.ts +0 -10
- package/dist/commands/trends.js +0 -12
- package/dist/lib/entitlement.d.ts +0 -31
- package/dist/lib/entitlement.js +0 -137
- package/dist/lib/prix-account.d.ts +0 -158
- package/dist/lib/prix-account.js +0 -214
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/serve/control.d.ts +0 -95
- package/dist/lib/serve/control.js +0 -260
- package/dist/lib/serve/data.d.ts +0 -81
- package/dist/lib/serve/data.js +0 -91
- package/dist/lib/serve/page.d.ts +0 -7
- package/dist/lib/serve/page.js +0 -140
- package/dist/lib/serve/server.d.ts +0 -80
- package/dist/lib/serve/server.js +0 -145
- package/dist/lib/serve/stream.d.ts +0 -43
- package/dist/lib/serve/stream.js +0 -116
- package/dist/lib/serve/token.d.ts +0 -35
- package/dist/lib/serve/token.js +0 -85
package/dist/commands/org.js
DELETED
|
@@ -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
|
-
}
|
package/dist/commands/serve.d.ts
DELETED
|
@@ -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;
|
package/dist/commands/serve.js
DELETED
|
@@ -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;
|
package/dist/commands/trends.js
DELETED
|
@@ -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;
|
package/dist/lib/entitlement.js
DELETED
|
@@ -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;
|