@phnx-labs/agents-cli 1.21.3 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/README.md +32 -3
- package/dist/bin/agents +0 -0
- package/dist/commands/computer-actions.js +1 -0
- package/dist/commands/exec.d.ts +27 -0
- package/dist/commands/exec.js +123 -6
- package/dist/commands/models.js +36 -1
- package/dist/commands/projects.js +22 -2
- package/dist/commands/sessions-backfill.d.ts +32 -0
- package/dist/commands/sessions-backfill.js +186 -0
- package/dist/commands/sessions.d.ts +17 -1
- package/dist/commands/sessions.js +317 -18
- package/dist/commands/teams.js +1 -1
- package/dist/commands/worktree.d.ts +3 -3
- package/dist/commands/worktree.js +35 -4
- package/dist/lib/daemon.d.ts +5 -1
- package/dist/lib/daemon.js +63 -14
- package/dist/lib/devices/resolve-target.d.ts +6 -0
- package/dist/lib/devices/resolve-target.js +9 -3
- package/dist/lib/exec.js +39 -8
- package/dist/lib/hosts/dispatch.d.ts +12 -0
- package/dist/lib/hosts/dispatch.js +23 -6
- package/dist/lib/hosts/reconnect.d.ts +38 -0
- package/dist/lib/hosts/reconnect.js +85 -4
- package/dist/lib/hosts/run-target.js +14 -2
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/model-tiers.d.ts +54 -0
- package/dist/lib/model-tiers.js +229 -0
- package/dist/lib/models.d.ts +3 -0
- package/dist/lib/models.js +44 -7
- package/dist/lib/pricing/prices.json +16 -1
- package/dist/lib/project-focus.d.ts +42 -0
- package/dist/lib/project-focus.js +80 -0
- package/dist/lib/project-schedule.d.ts +75 -0
- package/dist/lib/project-schedule.js +110 -0
- package/dist/lib/redact.d.ts +2 -0
- package/dist/lib/redact.js +22 -0
- package/dist/lib/remote-agents-json.d.ts +2 -0
- package/dist/lib/remote-agents-json.js +3 -3
- package/dist/lib/rotate.d.ts +84 -1
- package/dist/lib/rotate.js +155 -5
- package/dist/lib/runner.d.ts +4 -2
- package/dist/lib/runner.js +13 -4
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/session/bash-command.js +60 -9
- package/dist/lib/session/db.d.ts +7 -1
- package/dist/lib/session/db.js +301 -32
- package/dist/lib/session/discover.d.ts +40 -7
- package/dist/lib/session/discover.js +144 -83
- package/dist/lib/session/parse.d.ts +8 -1
- package/dist/lib/session/parse.js +83 -32
- package/dist/lib/session/remote-list.d.ts +71 -0
- package/dist/lib/session/remote-list.js +410 -2
- package/dist/lib/session/shell-programs.d.ts +15 -0
- package/dist/lib/session/shell-programs.js +359 -0
- package/dist/lib/session/tool-calls.d.ts +88 -0
- package/dist/lib/session/tool-calls.js +612 -0
- package/dist/lib/session/tool-index.d.ts +100 -0
- package/dist/lib/session/tool-index.js +773 -0
- package/dist/lib/session/tool-store.d.ts +15 -0
- package/dist/lib/session/tool-store.js +198 -0
- package/dist/lib/session/types.d.ts +7 -0
- package/dist/lib/state.d.ts +10 -1
- package/dist/lib/state.js +11 -2
- package/dist/lib/teams/remoteWorktree.d.ts +3 -4
- package/dist/lib/teams/remoteWorktree.js +3 -4
- package/dist/lib/teams/worktree.d.ts +11 -1
- package/dist/lib/teams/worktree.js +42 -4
- package/dist/lib/types.d.ts +17 -0
- package/dist/lib/types.js +17 -0
- package/package.json +3 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { setHelpSections } from '../lib/help.js';
|
|
3
|
+
import { gatherRemoteAgentsJson } from '../lib/remote-agents-json.js';
|
|
4
|
+
import { discoverSessions } from '../lib/session/discover.js';
|
|
5
|
+
import { machineId } from '../lib/session/sync/config.js';
|
|
6
|
+
import { SESSION_AGENTS } from '../lib/session/types.js';
|
|
7
|
+
import { ensureToolIndex, readToolIndexCoverage } from '../lib/session/tool-index.js';
|
|
8
|
+
import { NO_FANOUT_ENV } from '../lib/session/remote-active.js';
|
|
9
|
+
const BACKFILL_REMOTE_TIMEOUT_MS = 10 * 60_000;
|
|
10
|
+
function parseAgent(value) {
|
|
11
|
+
if (!value)
|
|
12
|
+
return {};
|
|
13
|
+
const separator = value.indexOf('@');
|
|
14
|
+
const name = (separator < 0 ? value : value.slice(0, separator));
|
|
15
|
+
if (!SESSION_AGENTS.includes(name))
|
|
16
|
+
throw new Error(`Unknown session agent: ${name}`);
|
|
17
|
+
return { agent: name, version: separator < 0 ? undefined : value.slice(separator + 1) || undefined };
|
|
18
|
+
}
|
|
19
|
+
function mergeHosts(options) {
|
|
20
|
+
const values = [options.host, options.device].flatMap((value) => value === undefined ? [] : Array.isArray(value) ? value : [value]);
|
|
21
|
+
return [...new Set(values)];
|
|
22
|
+
}
|
|
23
|
+
export async function backfillToolsLocal(options, oneBatch = false) {
|
|
24
|
+
const { agent, version } = parseAgent(options.agent);
|
|
25
|
+
const sessions = await discoverSessions({
|
|
26
|
+
agent,
|
|
27
|
+
version,
|
|
28
|
+
project: options.project,
|
|
29
|
+
since: options.since,
|
|
30
|
+
until: options.until,
|
|
31
|
+
all: true,
|
|
32
|
+
unbounded: true,
|
|
33
|
+
skipExistenceCheck: true,
|
|
34
|
+
includeUnmanaged: options.unmanaged,
|
|
35
|
+
excludeTeamOrigin: !options.teams,
|
|
36
|
+
});
|
|
37
|
+
let indexedFiles = 0;
|
|
38
|
+
let indexedCalls = 0;
|
|
39
|
+
for (;;) {
|
|
40
|
+
const batch = await ensureToolIndex(sessions);
|
|
41
|
+
indexedFiles += batch.indexedFiles;
|
|
42
|
+
indexedCalls += batch.indexedCalls;
|
|
43
|
+
if (oneBatch || batch.remainingFiles === 0 || batch.indexedFiles === 0)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
machine: machineId(),
|
|
48
|
+
indexedFiles,
|
|
49
|
+
indexedCalls,
|
|
50
|
+
coverage: readToolIndexCoverage(sessions),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function parseRemoteBackfill(stdout, machine) {
|
|
54
|
+
try {
|
|
55
|
+
const envelope = JSON.parse(stdout);
|
|
56
|
+
if (envelope.schemaVersion !== 1 || envelope.kind !== 'tools-backfill'
|
|
57
|
+
|| !Array.isArray(envelope.machines) || envelope.machines.length !== 1) {
|
|
58
|
+
return { items: [], valid: false };
|
|
59
|
+
}
|
|
60
|
+
const item = envelope.machines[0];
|
|
61
|
+
const coverage = item.coverage;
|
|
62
|
+
const coverageKeys = ['indexedFiles', 'indexedCalls', 'skippedFiles', 'limitedFiles', 'remainingFiles'];
|
|
63
|
+
if (!coverage || typeof coverage.complete !== 'boolean'
|
|
64
|
+
|| !Number.isSafeInteger(item.indexedFiles) || item.indexedFiles < 0
|
|
65
|
+
|| !Number.isSafeInteger(item.indexedCalls) || item.indexedCalls < 0
|
|
66
|
+
|| coverageKeys.some((key) => !Number.isSafeInteger(coverage[key]) || coverage[key] < 0)) {
|
|
67
|
+
return { items: [], valid: false };
|
|
68
|
+
}
|
|
69
|
+
return { items: [{ ...item, machine }], valid: true };
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return { items: [], valid: false };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function peerArgs(options) {
|
|
76
|
+
const args = ['sessions', 'backfill', 'tools', '--json', '--local'];
|
|
77
|
+
if (options.agent)
|
|
78
|
+
args.push('--agent', options.agent);
|
|
79
|
+
if (options.project)
|
|
80
|
+
args.push('--project', options.project);
|
|
81
|
+
if (options.since)
|
|
82
|
+
args.push('--since', options.since);
|
|
83
|
+
if (options.until)
|
|
84
|
+
args.push('--until', options.until);
|
|
85
|
+
if (options.unmanaged)
|
|
86
|
+
args.push('--unmanaged');
|
|
87
|
+
if (options.teams)
|
|
88
|
+
args.push('--teams');
|
|
89
|
+
return args;
|
|
90
|
+
}
|
|
91
|
+
export async function runToolsBackfill(options) {
|
|
92
|
+
const hosts = mergeHosts(options);
|
|
93
|
+
const includeLocal = options.local === true || hosts.length === 0;
|
|
94
|
+
const fanOut = options.local !== true && (options.fleet === true || hosts.length > 0);
|
|
95
|
+
if (!fanOut) {
|
|
96
|
+
const machines = includeLocal
|
|
97
|
+
? [await backfillToolsLocal(options, process.env[NO_FANOUT_ENV] === '1')]
|
|
98
|
+
: [];
|
|
99
|
+
return {
|
|
100
|
+
schemaVersion: 1,
|
|
101
|
+
kind: 'tools-backfill',
|
|
102
|
+
generatedAt: new Date().toISOString(),
|
|
103
|
+
complete: machines.every((machine) => machine.coverage.complete),
|
|
104
|
+
machines,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const machineTotals = new Map();
|
|
108
|
+
let unreachable = false;
|
|
109
|
+
for (;;) {
|
|
110
|
+
const localPromise = includeLocal ? backfillToolsLocal(options, true) : Promise.resolve(undefined);
|
|
111
|
+
const [local, remote] = await Promise.all([localPromise, gatherRemoteAgentsJson({
|
|
112
|
+
args: peerArgs(options),
|
|
113
|
+
noFanoutEnv: NO_FANOUT_ENV,
|
|
114
|
+
hosts: hosts.length > 0 ? hosts : undefined,
|
|
115
|
+
timeoutMs: BACKFILL_REMOTE_TIMEOUT_MS,
|
|
116
|
+
parse: parseRemoteBackfill,
|
|
117
|
+
})]);
|
|
118
|
+
const round = [local, ...remote.items].filter((item) => item !== undefined);
|
|
119
|
+
for (const item of round) {
|
|
120
|
+
const prior = machineTotals.get(item.machine);
|
|
121
|
+
machineTotals.set(item.machine, {
|
|
122
|
+
...item,
|
|
123
|
+
indexedFiles: (prior?.indexedFiles ?? 0) + item.indexedFiles,
|
|
124
|
+
indexedCalls: (prior?.indexedCalls ?? 0) + item.indexedCalls,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
unreachable = remote.discoveryFailed || remote.skipped.length > 0 || remote.parseFailed.length > 0;
|
|
128
|
+
const progressed = round.reduce((sum, item) => sum + item.indexedFiles, 0);
|
|
129
|
+
if (unreachable || progressed === 0 || (round.length > 0 && round.every((item) => item.coverage.complete)))
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
const machines = [...machineTotals.values()];
|
|
133
|
+
return {
|
|
134
|
+
schemaVersion: 1,
|
|
135
|
+
kind: 'tools-backfill',
|
|
136
|
+
generatedAt: new Date().toISOString(),
|
|
137
|
+
complete: !unreachable && machines.every((machine) => machine.coverage.complete),
|
|
138
|
+
machines,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
export function registerSessionsBackfillCommand(sessionsCmd) {
|
|
142
|
+
const backfill = sessionsCmd.command('backfill').description('Populate derived session data explicitly.');
|
|
143
|
+
const tools = backfill.command('tools').description('Parse historical tool calls once into the local SQLite index.');
|
|
144
|
+
setHelpSections(tools, {
|
|
145
|
+
examples: `
|
|
146
|
+
# Backfill this machine once; reruns resume and skip completed transcripts
|
|
147
|
+
agents sessions backfill tools
|
|
148
|
+
|
|
149
|
+
# Backfill every online compute device, keeping each index local
|
|
150
|
+
agents sessions backfill tools --fleet --json
|
|
151
|
+
|
|
152
|
+
# Narrow the historical work
|
|
153
|
+
agents sessions backfill tools --agent codex --since 7d
|
|
154
|
+
`,
|
|
155
|
+
notes: `
|
|
156
|
+
- This command is the only historical transcript parse for tool indexing. Tool queries never trigger it.
|
|
157
|
+
- New and changed sessions are indexed during their normal incremental scan.
|
|
158
|
+
- No embeddings, vector database, network model, or semantic processing is used.
|
|
159
|
+
`,
|
|
160
|
+
});
|
|
161
|
+
tools.action(async (_options, command) => {
|
|
162
|
+
const options = command.optsWithGlobals();
|
|
163
|
+
if (options.local && (options.fleet || mergeHosts(options).length > 0)) {
|
|
164
|
+
console.error(chalk.red('--local cannot be combined with --fleet, --host, or --device.'));
|
|
165
|
+
process.exitCode = 1;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const envelope = await runToolsBackfill(options);
|
|
170
|
+
if (options.json)
|
|
171
|
+
process.stdout.write(JSON.stringify(envelope, null, 2) + '\n');
|
|
172
|
+
else {
|
|
173
|
+
for (const machine of envelope.machines) {
|
|
174
|
+
console.log(`${machine.machine}: indexed ${machine.indexedFiles.toLocaleString()} transcript${machine.indexedFiles === 1 ? '' : 's'} and ${machine.indexedCalls.toLocaleString()} tool call${machine.indexedCalls === 1 ? '' : 's'}.`);
|
|
175
|
+
}
|
|
176
|
+
if (!envelope.complete) {
|
|
177
|
+
console.log(chalk.yellow('Backfill is partial; rerun the command after resolving skipped or limited transcripts.'));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
183
|
+
process.exitCode = 1;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
@@ -4,6 +4,7 @@ import type { SessionAgentId, SessionMeta, ViewMode } from '../lib/session/types
|
|
|
4
4
|
import { type ActiveSession } from '../lib/session/active.js';
|
|
5
5
|
import { gatherRemoteList, runOnPeer } from '../lib/session/remote-list.js';
|
|
6
6
|
import { type PickedSession } from './sessions-picker.js';
|
|
7
|
+
import { type ToolSearchEnvelope, type ToolProgramCountEnvelope } from '../lib/session/tool-index.js';
|
|
7
8
|
interface SessionFilterOptions {
|
|
8
9
|
agent?: string;
|
|
9
10
|
project?: string;
|
|
@@ -17,7 +18,7 @@ interface SessionFilterOptions {
|
|
|
17
18
|
interface SessionsOptions extends SessionFilterOptions {
|
|
18
19
|
/** Also list sessions from the user's own unmanaged ~/.<agent> installs. */
|
|
19
20
|
unmanaged?: boolean;
|
|
20
|
-
query?: string;
|
|
21
|
+
query?: string[];
|
|
21
22
|
/** Resolve one historical selector to metadata only (requires --json). */
|
|
22
23
|
resolve?: string;
|
|
23
24
|
/** Versioned internal peer protocol; old/unsafe peers must reject it. */
|
|
@@ -55,6 +56,10 @@ interface SessionsOptions extends SessionFilterOptions {
|
|
|
55
56
|
local?: boolean;
|
|
56
57
|
/** --device <target...> — alias for --host; resolves against the device registry. */
|
|
57
58
|
device?: string[];
|
|
59
|
+
/** Query every registered online compute device and merge tool evidence. */
|
|
60
|
+
fleet?: boolean;
|
|
61
|
+
/** Aggregate static program sites instead of returning matching call evidence. */
|
|
62
|
+
count?: boolean;
|
|
58
63
|
/** Per-agent shorthands: aliases for `--agent <name>` (prioritized harnesses). */
|
|
59
64
|
claude?: boolean;
|
|
60
65
|
codex?: boolean;
|
|
@@ -338,6 +343,15 @@ export declare function renderSessionPreview(query: string, scope: {
|
|
|
338
343
|
* attached", so those two spell it out.
|
|
339
344
|
*/
|
|
340
345
|
export declare function formatLiveStatusHeadline(live: ActiveSession | undefined, favorite?: boolean): string;
|
|
346
|
+
/** Merge local and peer envelopes without changing the versioned JSON shape. */
|
|
347
|
+
export declare function mergeToolSearchEnvelopes(local: ToolSearchEnvelope, remotes: ToolSearchEnvelope[]): ToolSearchEnvelope;
|
|
348
|
+
export declare function mergeToolProgramCountEnvelopes(local: ToolProgramCountEnvelope, remotes: ToolProgramCountEnvelope[]): ToolProgramCountEnvelope;
|
|
349
|
+
/** Partition a fleet query by transcript origin so synced mirrors cannot duplicate results. */
|
|
350
|
+
export declare function toolOriginSessions(sessions: SessionMeta[], machine: string, originOnly: boolean): SessionMeta[];
|
|
351
|
+
export declare function printToolProgramCount(envelope: ToolProgramCountEnvelope): void;
|
|
352
|
+
export declare function toolSearchFleetSortError(sort: string | undefined, spansDevices: boolean): string | undefined;
|
|
353
|
+
/** Compact grouped evidence for humans; JSON retains every bounded field. */
|
|
354
|
+
export declare function printToolSearch(envelope: ToolSearchEnvelope): void;
|
|
341
355
|
/**
|
|
342
356
|
* Whether a session belongs to `team`, from either end: it spawned the team, or
|
|
343
357
|
* it is one of the team's teammates. Case-insensitive, matching the SQL
|
|
@@ -605,6 +619,8 @@ export declare function fleetCandidatesByQuery(rows: SessionMeta[], query: strin
|
|
|
605
619
|
/** Fixed peer argv for the metadata resolver. Scope flags compose identically on
|
|
606
620
|
* every host; `--all` removes the SSH login cwd/time window, not agent/project filters. */
|
|
607
621
|
export declare function metadataResolveForwardedArgs(selector: string, scope: Pick<SessionFilterOptions, 'agent' | 'project'>): string[];
|
|
622
|
+
/** Build the local-only peer argv for a distributed tool search. */
|
|
623
|
+
export declare function toolSearchForwardedArgs(argv: string[], hosts: string[]): string[];
|
|
608
624
|
/** Resolution must fail closed when any selected peer did not answer. Choosing
|
|
609
625
|
* from a partial fleet can turn an unseen candidate into a false unique match. */
|
|
610
626
|
export declare function metadataResolveOutcome(localMatches: SessionMeta[], remote: {
|