@phnx-labs/agents-cli 1.20.51 → 1.20.53
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 +71 -0
- package/README.md +12 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/browser.js +215 -7
- package/dist/commands/cloud.d.ts +3 -0
- package/dist/commands/cloud.js +8 -1
- package/dist/commands/events.d.ts +1 -1
- package/dist/commands/events.js +2 -3
- package/dist/commands/exec.js +82 -3
- package/dist/commands/factory.js +8 -0
- package/dist/commands/feed.d.ts +38 -0
- package/dist/commands/feed.js +274 -0
- package/dist/commands/logs.d.ts +5 -1
- package/dist/commands/logs.js +248 -3
- package/dist/commands/mcp.js +7 -0
- package/dist/commands/memory.d.ts +9 -0
- package/dist/commands/memory.js +164 -0
- package/dist/commands/message.d.ts +11 -6
- package/dist/commands/message.js +140 -5
- package/dist/commands/routines.js +12 -0
- package/dist/commands/secrets-migrate.d.ts +2 -1
- package/dist/commands/secrets-migrate.js +88 -13
- package/dist/commands/secrets.d.ts +22 -0
- package/dist/commands/secrets.js +176 -42
- package/dist/commands/sessions.js +10 -1
- package/dist/commands/teams.js +4 -0
- package/dist/commands/worktree.js +4 -2
- package/dist/index.js +21 -22
- package/dist/lib/agents.js +249 -17
- package/dist/lib/answer-router.d.ts +75 -0
- package/dist/lib/answer-router.js +149 -0
- package/dist/lib/ask-classifier.d.ts +71 -0
- package/dist/lib/ask-classifier.js +197 -0
- package/dist/lib/browser/login-detection.d.ts +94 -0
- package/dist/lib/browser/login-detection.js +274 -0
- package/dist/lib/browser/profiles.d.ts +17 -8
- package/dist/lib/browser/profiles.js +27 -8
- package/dist/lib/browser/secret-ref.d.ts +10 -0
- package/dist/lib/browser/secret-ref.js +14 -0
- package/dist/lib/browser/service.js +14 -12
- package/dist/lib/cloud/antigravity.d.ts +0 -2
- package/dist/lib/cloud/antigravity.js +2 -17
- package/dist/lib/cloud/codex.js +3 -18
- package/dist/lib/cloud/rush.d.ts +15 -0
- package/dist/lib/cloud/rush.js +10 -16
- package/dist/lib/cloud/stream.js +2 -0
- package/dist/lib/cloud/types.d.ts +21 -0
- package/dist/lib/cloud/types.js +81 -0
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +12 -2
- package/dist/lib/crabbox/lease.d.ts +19 -0
- package/dist/lib/crabbox/lease.js +22 -11
- package/dist/lib/crabbox/progress.d.ts +62 -0
- package/dist/lib/crabbox/progress.js +129 -0
- package/dist/lib/crabbox/runtimes.d.ts +38 -1
- package/dist/lib/crabbox/runtimes.js +98 -5
- package/dist/lib/daemon.d.ts +12 -9
- package/dist/lib/daemon.js +32 -17
- package/dist/lib/events.d.ts +31 -5
- package/dist/lib/events.js +291 -101
- package/dist/lib/exec.js +20 -1
- package/dist/lib/feed-outcome.d.ts +101 -0
- package/dist/lib/feed-outcome.js +244 -0
- package/dist/lib/feed-policy.d.ts +30 -0
- package/dist/lib/feed-policy.js +133 -0
- package/dist/lib/feed.d.ts +180 -0
- package/dist/lib/feed.js +627 -0
- package/dist/lib/git.d.ts +17 -1
- package/dist/lib/git.js +20 -1
- package/dist/lib/hooks.js +529 -14
- package/dist/lib/hosts/passthrough.d.ts +3 -3
- package/dist/lib/hosts/passthrough.js +3 -3
- package/dist/lib/mailbox-gc.d.ts +22 -0
- package/dist/lib/mailbox-gc.js +161 -0
- package/dist/lib/mailbox.d.ts +26 -2
- package/dist/lib/mailbox.js +80 -5
- package/dist/lib/mcp.js +82 -0
- package/dist/lib/memory.d.ts +55 -0
- package/dist/lib/memory.js +274 -0
- package/dist/lib/notify.d.ts +16 -0
- package/dist/lib/notify.js +61 -0
- package/dist/lib/operator.d.ts +26 -0
- package/dist/lib/operator.js +107 -0
- package/dist/lib/plugins.d.ts +35 -0
- package/dist/lib/plugins.js +217 -0
- package/dist/lib/remote-agents-json.d.ts +14 -0
- package/dist/lib/remote-agents-json.js +94 -0
- package/dist/lib/resources/mcp.js +44 -0
- package/dist/lib/resources/memory.d.ts +15 -0
- package/dist/lib/resources/memory.js +46 -0
- package/dist/lib/resources/types.d.ts +2 -2
- package/dist/lib/rotate.js +2 -0
- package/dist/lib/runner.d.ts +43 -0
- package/dist/lib/runner.js +323 -74
- package/dist/lib/sandbox.js +6 -0
- 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/secrets/agent.d.ts +21 -0
- package/dist/lib/secrets/agent.js +63 -1
- package/dist/lib/secrets/bundles.d.ts +33 -1
- package/dist/lib/secrets/bundles.js +76 -21
- package/dist/lib/secrets/icloud-import.d.ts +79 -0
- package/dist/lib/secrets/icloud-import.js +203 -0
- package/dist/lib/secrets/index.d.ts +142 -2
- package/dist/lib/secrets/index.js +695 -30
- package/dist/lib/secrets/remote.js +1 -1
- package/dist/lib/secrets/sync.js +1 -1
- package/dist/lib/session/active.d.ts +18 -0
- package/dist/lib/session/active.js +47 -17
- package/dist/lib/session/db.d.ts +9 -1
- package/dist/lib/session/db.js +18 -3
- package/dist/lib/session/discover.d.ts +13 -0
- package/dist/lib/session/discover.js +32 -2
- package/dist/lib/session/parse.d.ts +8 -0
- package/dist/lib/session/parse.js +42 -21
- package/dist/lib/session/remote-active.js +8 -89
- package/dist/lib/session/state.d.ts +11 -0
- package/dist/lib/session/state.js +50 -1
- package/dist/lib/session/tail.d.ts +23 -4
- package/dist/lib/session/tail.js +34 -16
- package/dist/lib/session/throughput.d.ts +30 -0
- package/dist/lib/session/throughput.js +86 -0
- package/dist/lib/shim-heal.d.ts +12 -3
- package/dist/lib/shim-heal.js +12 -6
- package/dist/lib/staleness/detectors/subagents.js +57 -3
- package/dist/lib/staleness/writers/hooks.js +7 -3
- package/dist/lib/staleness/writers/subagents.js +37 -6
- package/dist/lib/startup/command-registry.d.ts +2 -0
- package/dist/lib/startup/command-registry.js +4 -0
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +25 -8
- package/dist/lib/subagents.d.ts +52 -0
- package/dist/lib/subagents.js +315 -12
- package/dist/lib/teams/agents.js +6 -3
- package/dist/lib/teams/worktree.d.ts +8 -0
- package/dist/lib/teams/worktree.js +8 -0
- package/dist/lib/types.d.ts +20 -2
- package/dist/lib/versions.js +38 -48
- package/dist/lib/whats-new.d.ts +5 -3
- package/dist/lib/whats-new.js +25 -5
- package/package.json +4 -3
- package/scripts/postinstall.js +61 -1
package/dist/commands/logs.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `agents logs` — unified, discoverable run-log viewer.
|
|
2
|
+
* `agents logs` — unified, discoverable run-log viewer + audit trail.
|
|
3
3
|
*
|
|
4
4
|
* Resolves a run across two substrates and shows (or `-f` follows) its log:
|
|
5
5
|
* - host-dispatch tasks (`agents run --host`) → combined-stdout log, offset-tailed
|
|
6
6
|
* - sessions (the local index) → transcript, tailed via the sessions tailer
|
|
7
7
|
*
|
|
8
|
+
* Subcommands:
|
|
9
|
+
* - `agents logs audit` — read the structured audit/event log
|
|
10
|
+
* - `agents logs stats` — show aggregate audit statistics
|
|
11
|
+
*
|
|
8
12
|
* Concise by default: a bare `agents logs <id>` prints the same summary digest as
|
|
9
13
|
* `agents sessions <id>` — cheap for an agent to glance at. The token-heavy full
|
|
10
14
|
* transcript / raw stdout is opt-in behind `--full` (alias `-m/--markdown`).
|
|
@@ -17,12 +21,14 @@
|
|
|
17
21
|
* the same underlying helpers (showHostTaskLog / streamSessionTail).
|
|
18
22
|
*/
|
|
19
23
|
import chalk from 'chalk';
|
|
24
|
+
import * as fs from 'fs';
|
|
20
25
|
import { discoverSessions, resolveSessionById } from '../lib/session/discover.js';
|
|
21
26
|
import { parseAgentFilter, renderSessionLog } from './sessions.js';
|
|
22
27
|
import { streamSessionTail, isTailable } from './sessions-tail.js';
|
|
23
28
|
import { showHostTaskLog } from '../lib/hosts/logs.js';
|
|
24
29
|
import { listTasks } from '../lib/hosts/tasks.js';
|
|
25
30
|
import { itemPicker } from '../lib/picker.js';
|
|
31
|
+
import { query, stats, getLogsPath, rotate, levelFor, } from '../lib/events.js';
|
|
26
32
|
/** Compact one-line label used by both the picker and the non-TTY list. */
|
|
27
33
|
function candidateLabel(c) {
|
|
28
34
|
if (c.kind === 'task') {
|
|
@@ -130,11 +136,207 @@ async function runLogs(id, opts) {
|
|
|
130
136
|
return;
|
|
131
137
|
await showCandidate(picked.item, follow, full);
|
|
132
138
|
}
|
|
139
|
+
function parseSince(s) {
|
|
140
|
+
const m = s.match(/^(\d+)([smhdw])$/);
|
|
141
|
+
if (m) {
|
|
142
|
+
const n = parseInt(m[1], 10);
|
|
143
|
+
const unitMs = {
|
|
144
|
+
s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000, w: 604_800_000,
|
|
145
|
+
};
|
|
146
|
+
return new Date(Date.now() - n * unitMs[m[2]]);
|
|
147
|
+
}
|
|
148
|
+
const ms = Date.parse(s);
|
|
149
|
+
if (isNaN(ms))
|
|
150
|
+
throw new Error(`Invalid --since value: ${s} (use e.g. 2h, 7d, or an ISO date)`);
|
|
151
|
+
return new Date(ms);
|
|
152
|
+
}
|
|
153
|
+
function originLabel(r) {
|
|
154
|
+
if (r.transport === 'ssh') {
|
|
155
|
+
return chalk.yellow(`ssh${r.sshClientIp ? ' ' + r.sshClientIp : ''}`);
|
|
156
|
+
}
|
|
157
|
+
return chalk.gray('local');
|
|
158
|
+
}
|
|
159
|
+
function auditDetailFor(r) {
|
|
160
|
+
if (r.command)
|
|
161
|
+
return r.command;
|
|
162
|
+
const bits = [];
|
|
163
|
+
if (typeof r.team === 'string')
|
|
164
|
+
bits.push(`team=${r.team}`);
|
|
165
|
+
if (typeof r.bundle === 'string')
|
|
166
|
+
bits.push(`bundle=${r.bundle}`);
|
|
167
|
+
if (typeof r.skill === 'string')
|
|
168
|
+
bits.push(`skill=${r.skill}`);
|
|
169
|
+
if (typeof r.version === 'string')
|
|
170
|
+
bits.push(`v=${r.version}`);
|
|
171
|
+
if (typeof r.profile === 'string')
|
|
172
|
+
bits.push(`profile=${r.profile}`);
|
|
173
|
+
if (typeof r.server === 'string')
|
|
174
|
+
bits.push(`server=${r.server}`);
|
|
175
|
+
if (typeof r.error === 'string')
|
|
176
|
+
bits.push(chalk.red(r.error));
|
|
177
|
+
return bits.join(' ');
|
|
178
|
+
}
|
|
179
|
+
function levelColor(level) {
|
|
180
|
+
if (level === 'audit')
|
|
181
|
+
return chalk.magenta(level);
|
|
182
|
+
if (level === 'warn')
|
|
183
|
+
return chalk.yellow(level);
|
|
184
|
+
if (level === 'debug')
|
|
185
|
+
return chalk.gray(level);
|
|
186
|
+
return chalk.blue(level);
|
|
187
|
+
}
|
|
188
|
+
function renderAuditRow(r) {
|
|
189
|
+
const time = chalk.gray(r.ts.slice(0, 19).replace('T', ' '));
|
|
190
|
+
const user = `${r.osUser ?? '?'}@${r.hostname}`;
|
|
191
|
+
const ev = r.event.startsWith('error') ? chalk.red(r.event) : chalk.cyan(r.event);
|
|
192
|
+
const lvl = levelColor(r.level ?? levelFor(r.event));
|
|
193
|
+
const agent = r.agent ? chalk.gray(` ${r.agent}`) : '';
|
|
194
|
+
const caller = chalk.gray(`via ${r.caller ?? 'unknown'}${r.session ? ` ${r.session}` : ''}`);
|
|
195
|
+
return `${time} ${lvl.padEnd(14)} ${originLabel(r).padEnd(24)} ${user.padEnd(22)} ${caller.padEnd(28)} ${ev.padEnd(26)}${agent} ${auditDetailFor(r)}`;
|
|
196
|
+
}
|
|
197
|
+
function collect(value, previous) {
|
|
198
|
+
return previous.concat([value]);
|
|
199
|
+
}
|
|
200
|
+
async function runAudit(opts) {
|
|
201
|
+
if (opts.follow) {
|
|
202
|
+
await followAuditLog();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const limit = Math.max(1, parseInt(opts.limit ?? '50', 10) || 50);
|
|
206
|
+
let startDate;
|
|
207
|
+
try {
|
|
208
|
+
startDate = opts.since ? parseSince(opts.since) : undefined;
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
console.error(chalk.red(err.message));
|
|
212
|
+
process.exit(2);
|
|
213
|
+
}
|
|
214
|
+
const records = query({
|
|
215
|
+
startDate,
|
|
216
|
+
eventTypes: opts.event?.length ? opts.event : undefined,
|
|
217
|
+
level: opts.level,
|
|
218
|
+
agent: opts.agent,
|
|
219
|
+
caller: opts.caller,
|
|
220
|
+
command: opts.command,
|
|
221
|
+
module: opts.module,
|
|
222
|
+
limit,
|
|
223
|
+
});
|
|
224
|
+
if (opts.json) {
|
|
225
|
+
console.log(JSON.stringify(records, null, 2));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (records.length === 0) {
|
|
229
|
+
console.log(chalk.gray('No matching events.'));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
for (const r of records.slice().reverse())
|
|
233
|
+
console.log(renderAuditRow(r));
|
|
234
|
+
console.log(chalk.gray(`\n${records.length} event(s). Log: ${getLogsPath()}`));
|
|
235
|
+
}
|
|
236
|
+
async function followAuditLog() {
|
|
237
|
+
const file = getLogsPath();
|
|
238
|
+
let offset = 0;
|
|
239
|
+
try {
|
|
240
|
+
offset = fs.statSync(file).size;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// File may not exist yet — start at 0.
|
|
244
|
+
}
|
|
245
|
+
console.log(chalk.gray(`Tailing ${file} — Ctrl-C to stop`));
|
|
246
|
+
const drain = () => {
|
|
247
|
+
let size = 0;
|
|
248
|
+
try {
|
|
249
|
+
size = fs.statSync(file).size;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (size <= offset) {
|
|
255
|
+
if (size < offset)
|
|
256
|
+
offset = 0;
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const fd = fs.openSync(file, 'r');
|
|
260
|
+
try {
|
|
261
|
+
const buf = Buffer.alloc(size - offset);
|
|
262
|
+
fs.readSync(fd, buf, 0, buf.length, offset);
|
|
263
|
+
offset = size;
|
|
264
|
+
for (const line of buf.toString('utf-8').split('\n').filter(Boolean)) {
|
|
265
|
+
try {
|
|
266
|
+
console.log(renderAuditRow(JSON.parse(line)));
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
// Skip malformed lines.
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
fs.closeSync(fd);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
await new Promise(() => {
|
|
278
|
+
setInterval(drain, 500);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function humanBytes(bytes) {
|
|
282
|
+
if (bytes < 1024)
|
|
283
|
+
return `${bytes} B`;
|
|
284
|
+
if (bytes < 1024 * 1024)
|
|
285
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
286
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
287
|
+
}
|
|
288
|
+
async function runStats(opts) {
|
|
289
|
+
let days = 7;
|
|
290
|
+
if (opts.since) {
|
|
291
|
+
try {
|
|
292
|
+
const d = parseSince(opts.since);
|
|
293
|
+
days = Math.max(1, Math.ceil((Date.now() - d.getTime()) / 86_400_000));
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
console.error(chalk.red(err.message));
|
|
297
|
+
process.exit(2);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const s = stats({ days });
|
|
301
|
+
if (opts.json) {
|
|
302
|
+
console.log(JSON.stringify(s, null, 2));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
console.log(chalk.bold(`Audit statistics (last ${days} day${days === 1 ? '' : 's'})\n`));
|
|
306
|
+
console.log(` Total events: ${s.totalEvents}`);
|
|
307
|
+
console.log(` Log files: ${s.fileCount} (${humanBytes(s.totalBytes)})`);
|
|
308
|
+
console.log(` Log path: ${chalk.gray(getLogsPath())}`);
|
|
309
|
+
if (Object.keys(s.byLevel).length) {
|
|
310
|
+
console.log(chalk.bold('\n By level:'));
|
|
311
|
+
for (const [k, v] of Object.entries(s.byLevel).sort((a, b) => b[1] - a[1])) {
|
|
312
|
+
console.log(` ${levelColor(k).padEnd(20)} ${v}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (Object.keys(s.byEvent).length) {
|
|
316
|
+
console.log(chalk.bold('\n By event (top 15):'));
|
|
317
|
+
for (const [k, v] of Object.entries(s.byEvent).sort((a, b) => b[1] - a[1]).slice(0, 15)) {
|
|
318
|
+
console.log(` ${chalk.cyan(k).padEnd(30)} ${v}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (Object.keys(s.byModule).length) {
|
|
322
|
+
console.log(chalk.bold('\n By module:'));
|
|
323
|
+
for (const [k, v] of Object.entries(s.byModule).sort((a, b) => b[1] - a[1])) {
|
|
324
|
+
console.log(` ${k.padEnd(20)} ${v}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (Object.keys(s.byUser).length) {
|
|
328
|
+
console.log(chalk.bold('\n By user:'));
|
|
329
|
+
for (const [k, v] of Object.entries(s.byUser).sort((a, b) => b[1] - a[1])) {
|
|
330
|
+
console.log(` ${k.padEnd(30)} ${v}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
console.log();
|
|
334
|
+
}
|
|
133
335
|
/** Register the top-level `agents logs` command. */
|
|
134
336
|
export function registerLogsCommand(program) {
|
|
135
|
-
program
|
|
337
|
+
const logsCmd = program
|
|
136
338
|
.command('logs [id]')
|
|
137
|
-
.description('Show a run
|
|
339
|
+
.description('Show a run log, audit trail, or stats. Subcommands: audit, stats, rotate.')
|
|
138
340
|
.option('--host <name>', 'Scope to runs dispatched to a host')
|
|
139
341
|
.option('-a, --agent <agent>', 'Filter by agent (e.g. claude, codex@0.116.0)')
|
|
140
342
|
.option('--version <version>', 'Filter by agent version')
|
|
@@ -142,4 +344,47 @@ export function registerLogsCommand(program) {
|
|
|
142
344
|
.option('-f, --follow', 'Follow live output')
|
|
143
345
|
.option('-m, --full', 'Show the full raw transcript / stdout instead of the concise summary')
|
|
144
346
|
.action((id, opts) => runLogs(id, opts));
|
|
347
|
+
logsCmd
|
|
348
|
+
.command('audit')
|
|
349
|
+
.description('Read the structured audit/event log (who ran what, from where)')
|
|
350
|
+
.option('--module <name>', 'Only events from this command group (e.g. teams, secrets)')
|
|
351
|
+
.option('--command <path>', 'Only this command path — prefix match (e.g. "teams create")')
|
|
352
|
+
.option('--event <type>', 'Only this typed event (repeatable)', collect, [])
|
|
353
|
+
.option('--agent <name>', 'Only events tagged with this agent')
|
|
354
|
+
.option('--caller <kind>', 'Only this caller kind (claude-code, codex, gemini, cursor, terminal, script)')
|
|
355
|
+
.option('--level <level>', 'Only this level: audit, warn, info, debug')
|
|
356
|
+
.option('--since <time>', 'Only events newer than this (e.g. 2h, 7d, or ISO date)')
|
|
357
|
+
.option('--limit <n>', 'Max records to show (default 50)', '50')
|
|
358
|
+
.option('--json', 'Output raw records as JSON')
|
|
359
|
+
.option('-f, --follow', "Tail today's log live")
|
|
360
|
+
.addHelpText('after', `
|
|
361
|
+
Examples:
|
|
362
|
+
agents logs audit Recent activity across everything
|
|
363
|
+
agents logs audit --module teams Team lifecycle (create / add / disband)
|
|
364
|
+
agents logs audit --module secrets Every secret accessed or revealed
|
|
365
|
+
agents logs audit --level audit Only audit-level events
|
|
366
|
+
agents logs audit --command "teams create" Just team creations
|
|
367
|
+
agents logs audit --event secrets.get --since 7d --json
|
|
368
|
+
agents logs audit -f Live tail`)
|
|
369
|
+
.action(async (options) => runAudit(options));
|
|
370
|
+
logsCmd
|
|
371
|
+
.command('stats')
|
|
372
|
+
.description('Show aggregate audit statistics')
|
|
373
|
+
.option('--since <time>', 'Window size (e.g. 7d, 30d; default 7d)')
|
|
374
|
+
.option('--json', 'Output stats as JSON')
|
|
375
|
+
.action(async (opts) => runStats(opts));
|
|
376
|
+
logsCmd
|
|
377
|
+
.command('rotate')
|
|
378
|
+
.description('Force log rotation — remove files older than the retention period')
|
|
379
|
+
.option('--days <n>', 'Retention period in days (default 7)', '7')
|
|
380
|
+
.action((opts) => {
|
|
381
|
+
const days = Math.max(1, parseInt(opts.days ?? '7', 10) || 7);
|
|
382
|
+
const removed = rotate(days);
|
|
383
|
+
if (removed > 0) {
|
|
384
|
+
console.log(`Removed ${removed} log file${removed === 1 ? '' : 's'} older than ${days} day${days === 1 ? '' : 's'}.`);
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
console.log(chalk.gray('No log files to remove.'));
|
|
388
|
+
}
|
|
389
|
+
});
|
|
145
390
|
}
|
package/dist/commands/mcp.js
CHANGED
|
@@ -3,6 +3,7 @@ import { truncate } from '../lib/format.js';
|
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { checkbox } from '@inquirer/prompts';
|
|
5
5
|
import { capableAgents, isCapable } from '../lib/capabilities.js';
|
|
6
|
+
import { emit } from '../lib/events.js';
|
|
6
7
|
import { AGENTS, getAllCliStates, resolveAgentName, formatAgentError, registerMcpToTargets, unregisterMcpFromTargets, listInstalledMcpsWithScope, parseMcpConfig, getMcpConfigPathForHome, agentLabel, } from '../lib/agents.js';
|
|
7
8
|
import { readManifest, writeManifest, createDefaultManifest } from '../lib/manifest.js';
|
|
8
9
|
import { listMcpServerConfigs, discoverMcpConfigsFromRepo, installMcpConfigCentrally, } from '../lib/mcp.js';
|
|
@@ -293,6 +294,7 @@ Examples:
|
|
|
293
294
|
};
|
|
294
295
|
}
|
|
295
296
|
writeManifest(localPath, manifest);
|
|
297
|
+
emit('mcp.add', { module: 'mcp', server: name });
|
|
296
298
|
console.log(chalk.green(`Added MCP server '${name}' to manifest`));
|
|
297
299
|
console.log(chalk.gray('Run: agents mcp register to apply'));
|
|
298
300
|
});
|
|
@@ -441,6 +443,8 @@ Examples:
|
|
|
441
443
|
console.log(chalk.yellow('No MCP servers removed.'));
|
|
442
444
|
}
|
|
443
445
|
else {
|
|
446
|
+
for (const n of mcpsToRemove)
|
|
447
|
+
emit('mcp.remove', { module: 'mcp', server: n });
|
|
444
448
|
console.log(chalk.green(`\nRemoved ${removed} MCP server(s).`));
|
|
445
449
|
}
|
|
446
450
|
});
|
|
@@ -575,6 +579,7 @@ Examples:
|
|
|
575
579
|
targets = resolveConfiguredAgentTargets(config.agents, config.agentVersions, capableAgents('mcp'));
|
|
576
580
|
}
|
|
577
581
|
const results = await registerMcpToTargets(targets, mcpName, commandOrUrl, config.scope || 'user', transport, { headers: config.headers });
|
|
582
|
+
const applied = results.filter(r => r.success).length;
|
|
578
583
|
for (const result of results) {
|
|
579
584
|
if (result.success) {
|
|
580
585
|
console.log(` ${chalk.green('+')} ${formatTargetLabel(result.agentId, result.version)}`);
|
|
@@ -586,6 +591,8 @@ Examples:
|
|
|
586
591
|
console.log(` ${chalk.red('x')} ${formatTargetLabel(result.agentId, result.version)}: ${result.error}`);
|
|
587
592
|
}
|
|
588
593
|
}
|
|
594
|
+
if (applied > 0)
|
|
595
|
+
emit('mcp.register', { module: 'mcp', server: mcpName, applied });
|
|
589
596
|
}
|
|
590
597
|
});
|
|
591
598
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents memory` — first-class knowledge/facts resource (not rules).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the skills surface at list / add / remove / view / sync scale.
|
|
5
|
+
* Canonical storage: ~/.agents/memory/ (project > user > system layering).
|
|
6
|
+
*/
|
|
7
|
+
import type { Command } from 'commander';
|
|
8
|
+
/** Register the `agents memory` command tree. */
|
|
9
|
+
export declare function registerMemoryCommands(program: Command): void;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents memory` — first-class knowledge/facts resource (not rules).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the skills surface at list / add / remove / view / sync scale.
|
|
5
|
+
* Canonical storage: ~/.agents/memory/ (project > user > system layering).
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import { listMemoryFacts, addMemoryFact, removeMemoryFact, readMemoryFact, ensureUserMemoryDir, getUserMemoryDir, syncMemoryToVersionHome, } from '../lib/memory.js';
|
|
10
|
+
import { capableAgents } from '../lib/capabilities.js';
|
|
11
|
+
import { resolveAgentName, formatAgentError, agentLabel, ALL_AGENT_IDS } from '../lib/agents.js';
|
|
12
|
+
import { listInstalledVersions, getVersionHomePath, } from '../lib/versions.js';
|
|
13
|
+
import { supports } from '../lib/capabilities.js';
|
|
14
|
+
import { setHelpSections } from '../lib/help.js';
|
|
15
|
+
/** Register the `agents memory` command tree. */
|
|
16
|
+
export function registerMemoryCommands(program) {
|
|
17
|
+
const memoryCmd = program
|
|
18
|
+
.command('memory')
|
|
19
|
+
.description('Manage portable agent memory (facts, preferences, project knowledge)');
|
|
20
|
+
setHelpSections(memoryCmd, {
|
|
21
|
+
examples: `
|
|
22
|
+
# List all memory facts (project > user > system)
|
|
23
|
+
agents memory list
|
|
24
|
+
|
|
25
|
+
# Add a fact
|
|
26
|
+
agents memory add preferred-editor --body "User prefers vim keybindings"
|
|
27
|
+
|
|
28
|
+
# View a fact
|
|
29
|
+
agents memory view preferred-editor
|
|
30
|
+
|
|
31
|
+
# Remove a fact from the user layer
|
|
32
|
+
agents memory remove preferred-editor
|
|
33
|
+
|
|
34
|
+
# Fan out to all capable installed agent versions
|
|
35
|
+
agents memory sync
|
|
36
|
+
`,
|
|
37
|
+
notes: `
|
|
38
|
+
Memory is the knowledge store (learned facts), distinct from rules
|
|
39
|
+
(instructions / AGENTS.md persona). Stored under ~/.agents/memory/ and
|
|
40
|
+
synced into capable agents' version homes (claude/codex/openclaw/grok).
|
|
41
|
+
|
|
42
|
+
Format: MEMORY.md index + one <slug>.md file per fact.
|
|
43
|
+
`,
|
|
44
|
+
});
|
|
45
|
+
memoryCmd
|
|
46
|
+
.command('list')
|
|
47
|
+
.description('List memory facts from project, user, and system layers')
|
|
48
|
+
.option('--json', 'Emit machine-readable JSON')
|
|
49
|
+
.action((options) => {
|
|
50
|
+
const facts = listMemoryFacts(process.cwd());
|
|
51
|
+
if (options.json) {
|
|
52
|
+
process.stdout.write(JSON.stringify(facts, null, 2) + '\n');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (facts.length === 0) {
|
|
56
|
+
console.log(chalk.gray('No memory facts yet.'));
|
|
57
|
+
console.log(chalk.gray(` Add one: agents memory add <name> --body "..."`));
|
|
58
|
+
console.log(chalk.gray(` Dir: ${getUserMemoryDir()}`));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.log(chalk.bold(`Memory (${facts.length})`));
|
|
62
|
+
for (const f of facts) {
|
|
63
|
+
const layer = chalk.dim(`[${f.layer}]`);
|
|
64
|
+
console.log(` ${chalk.cyan(f.name)} ${layer} ${f.summary}`);
|
|
65
|
+
}
|
|
66
|
+
const capable = capableAgents('memory');
|
|
67
|
+
if (capable.length > 0) {
|
|
68
|
+
console.log(chalk.gray(`\nCapable agents: ${capable.join(', ')}`));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
memoryCmd
|
|
72
|
+
.command('add <name>')
|
|
73
|
+
.description('Add or overwrite a user-layer memory fact')
|
|
74
|
+
.requiredOption('--body <text>', 'Fact body (markdown)')
|
|
75
|
+
.option('--sync', 'Immediately sync to all capable installed versions')
|
|
76
|
+
.action((name, options) => {
|
|
77
|
+
const filePath = addMemoryFact(name, options.body);
|
|
78
|
+
console.log(chalk.green(`Added memory fact: ${name}`));
|
|
79
|
+
console.log(chalk.gray(` ${filePath}`));
|
|
80
|
+
if (options.sync) {
|
|
81
|
+
for (const r of syncAllMemory(process.cwd())) {
|
|
82
|
+
console.log(chalk.gray(` synced ${r.agent}@${r.version} (${r.facts.length} facts)`));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.log(chalk.gray(' Run: agents memory sync (or agents sync) to fan out'));
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
memoryCmd
|
|
90
|
+
.command('remove <name>')
|
|
91
|
+
.alias('rm')
|
|
92
|
+
.description('Remove a user-layer memory fact')
|
|
93
|
+
.action((name) => {
|
|
94
|
+
if (removeMemoryFact(name)) {
|
|
95
|
+
console.log(chalk.green(`Removed memory fact: ${name}`));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.error(chalk.red(`No user-layer fact named "${name}"`));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
memoryCmd
|
|
103
|
+
.command('view <name>')
|
|
104
|
+
.description('Print a memory fact (winning layer)')
|
|
105
|
+
.action((name) => {
|
|
106
|
+
const fact = readMemoryFact(name, process.cwd());
|
|
107
|
+
if (!fact) {
|
|
108
|
+
console.error(chalk.red(`No memory fact named "${name}"`));
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
console.log(chalk.dim(`# ${fact.name} [${fact.layer}] ${fact.path}`));
|
|
112
|
+
console.log(fs.readFileSync(fact.path, 'utf-8'));
|
|
113
|
+
});
|
|
114
|
+
memoryCmd
|
|
115
|
+
.command('sync [agent]')
|
|
116
|
+
.description('Copy canonical memory into capable agent version homes')
|
|
117
|
+
.option('-a, --agent <agent>', 'Limit to one agent (or agent@version)')
|
|
118
|
+
.action(async (agentArg, options) => {
|
|
119
|
+
ensureUserMemoryDir();
|
|
120
|
+
const input = agentArg || options.agent;
|
|
121
|
+
if (input) {
|
|
122
|
+
const parts = input.split('@');
|
|
123
|
+
const resolved = resolveAgentName(parts[0]);
|
|
124
|
+
if (!resolved) {
|
|
125
|
+
console.error(chalk.red(formatAgentError(parts[0], capableAgents('memory'))));
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
const agent = resolved;
|
|
129
|
+
const versions = parts[1]
|
|
130
|
+
? [parts[1]]
|
|
131
|
+
: listInstalledVersions(agent);
|
|
132
|
+
if (versions.length === 0) {
|
|
133
|
+
console.log(chalk.yellow(`No installed versions of ${agentLabel(agent)}`));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
for (const version of versions) {
|
|
137
|
+
const home = getVersionHomePath(agent, version);
|
|
138
|
+
const facts = syncMemoryToVersionHome(agent, home, process.cwd());
|
|
139
|
+
console.log(chalk.green(`Synced ${facts.length} fact(s) → ${agent}@${version}`));
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const results = syncAllMemory(process.cwd());
|
|
144
|
+
if (results.length === 0) {
|
|
145
|
+
console.log(chalk.gray('No memory-capable agent versions installed.'));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
for (const r of results) {
|
|
149
|
+
console.log(chalk.green(`Synced ${r.facts.length} fact(s) → ${r.agent}@${r.version}`));
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function syncAllMemory(cwd) {
|
|
154
|
+
const out = [];
|
|
155
|
+
for (const agent of ALL_AGENT_IDS) {
|
|
156
|
+
if (!supports(agent, 'memory').ok)
|
|
157
|
+
continue;
|
|
158
|
+
for (const version of listInstalledVersions(agent)) {
|
|
159
|
+
const home = getVersionHomePath(agent, version);
|
|
160
|
+
out.push({ agent, version, facts: syncMemoryToVersionHome(agent, home, cwd) });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `agents message <target> <text>` — send a message to a running agent.
|
|
2
|
+
* `agents message <target> <text>` — send a message to a running or parked agent.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
4
|
+
* Delivery is routed by agent state (RUSH-1474):
|
|
5
|
+
* - running, between tool calls → mailbox spool (PreToolUse inject)
|
|
6
|
+
* - parked on AskUserQuestion with a tmux/iterm/pty rail → keystroke inject
|
|
7
|
+
* - parked headless (no rail) → `agents run --resume <id> -- <answer>`
|
|
8
|
+
* - cloud task → provider.message()
|
|
9
9
|
*
|
|
10
10
|
* Cross-host is handled one layer up: `--host <h>` routes the whole command over
|
|
11
11
|
* ssh via `REMOTE_PASSTHROUGH` (see src/lib/hosts/passthrough.ts), so the box is
|
|
12
12
|
* written on the host that actually owns the agent.
|
|
13
|
+
*
|
|
14
|
+
* For local agents, the message is tied to the agent's current open feed block
|
|
15
|
+
* (if any). The first answer to a block wins: a second concurrent answer is
|
|
16
|
+
* rejected with the surface that already answered. Delivery receipts
|
|
17
|
+
* (queued → consumed → continued) are surfaced in the feed store.
|
|
13
18
|
*/
|
|
14
19
|
import type { Command } from 'commander';
|
|
15
20
|
export declare function registerMessageCommand(program: Command): void;
|