@phnx-labs/agents-cli 1.20.50 → 1.20.52
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 +40 -0
- package/dist/commands/browser-picker.js +1 -18
- package/dist/commands/browser.js +215 -7
- package/dist/commands/cloud.js +7 -25
- package/dist/commands/computer.d.ts +1 -0
- package/dist/commands/computer.js +129 -8
- package/dist/commands/events.d.ts +1 -1
- package/dist/commands/events.js +2 -3
- package/dist/commands/exec.js +66 -8
- package/dist/commands/factory.js +9 -4
- package/dist/commands/feed.d.ts +9 -0
- package/dist/commands/feed.js +69 -0
- package/dist/commands/inspect.js +1 -11
- package/dist/commands/logs.d.ts +5 -1
- package/dist/commands/logs.js +248 -3
- package/dist/commands/mcp.js +9 -6
- package/dist/commands/message.js +1 -4
- package/dist/commands/profiles.js +1 -18
- package/dist/commands/repo.js +33 -14
- package/dist/commands/resource-view.d.ts +1 -0
- package/dist/commands/resource-view.js +5 -17
- package/dist/commands/secrets.d.ts +23 -0
- package/dist/commands/secrets.js +174 -70
- package/dist/commands/sessions-picker.js +1 -18
- package/dist/commands/sessions.js +6 -8
- package/dist/commands/teams-picker.js +1 -32
- package/dist/commands/teams.js +5 -27
- package/dist/commands/tmux.js +1 -3
- package/dist/commands/view.js +1 -9
- package/dist/commands/worktree.js +1 -4
- package/dist/index.js +6 -2
- package/dist/lib/agents.d.ts +0 -4
- package/dist/lib/agents.js +20 -33
- package/dist/lib/auto-dispatch-linear.d.ts +18 -0
- package/dist/lib/auto-dispatch-linear.js +107 -0
- package/dist/lib/auto-dispatch-provider.d.ts +10 -0
- package/dist/lib/auto-dispatch-provider.js +25 -0
- package/dist/lib/auto-dispatch.d.ts +87 -0
- package/dist/lib/auto-dispatch.js +142 -0
- package/dist/lib/browser/cdp.js +11 -2
- package/dist/lib/browser/drivers/ssh.d.ts +28 -10
- package/dist/lib/browser/drivers/ssh.js +57 -18
- 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/refs.js +1 -5
- 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/cli-resources.d.ts +0 -2
- package/dist/lib/cli-resources.js +30 -13
- package/dist/lib/cloud/rush.d.ts +15 -24
- package/dist/lib/cloud/rush.js +7 -32
- package/dist/lib/crabbox/cli.js +4 -1
- package/dist/lib/crabbox/lease.d.ts +6 -0
- package/dist/lib/crabbox/lease.js +40 -10
- 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 +73 -17
- package/dist/lib/events.d.ts +31 -5
- package/dist/lib/events.js +288 -101
- package/dist/lib/exec.js +32 -14
- package/dist/lib/feed.d.ts +56 -0
- package/dist/lib/feed.js +251 -0
- package/dist/lib/format.d.ts +38 -0
- package/dist/lib/format.js +108 -0
- package/dist/lib/git.d.ts +21 -0
- package/dist/lib/git.js +92 -0
- package/dist/lib/hooks/cache.d.ts +9 -2
- package/dist/lib/hooks/cache.js +220 -8
- package/dist/lib/hooks.js +24 -10
- package/dist/lib/hosts/passthrough.js +1 -0
- package/dist/lib/platform/exec.d.ts +4 -1
- package/dist/lib/platform/exec.js +8 -2
- package/dist/lib/resources.d.ts +0 -8
- package/dist/lib/resources.js +0 -10
- package/dist/lib/rotate.js +2 -0
- package/dist/lib/runner.js +10 -2
- 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 +38 -8
- package/dist/lib/secrets/icloud-import.d.ts +70 -0
- package/dist/lib/secrets/icloud-import.js +173 -0
- package/dist/lib/secrets/index.d.ts +36 -0
- package/dist/lib/secrets/index.js +99 -9
- package/dist/lib/secrets/remote.js +1 -1
- package/dist/lib/secrets/sync.js +1 -1
- package/dist/lib/session/active.d.ts +11 -1
- package/dist/lib/session/active.js +3 -0
- package/dist/lib/session/db.d.ts +1 -4
- package/dist/lib/session/db.js +20 -25
- package/dist/lib/session/discover.d.ts +2 -2
- package/dist/lib/session/discover.js +60 -48
- package/dist/lib/session/parse.js +35 -34
- package/dist/lib/session/render.d.ts +7 -3
- package/dist/lib/session/render.js +15 -9
- package/dist/lib/session/state.d.ts +55 -0
- package/dist/lib/session/state.js +100 -11
- package/dist/lib/session/types.d.ts +9 -0
- package/dist/lib/shims.d.ts +9 -3
- package/dist/lib/shims.js +44 -8
- package/dist/lib/ssh-tunnel.d.ts +33 -2
- package/dist/lib/ssh-tunnel.js +94 -7
- package/dist/lib/staleness/types.d.ts +0 -1
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +25 -8
- package/dist/lib/teams/agents.js +6 -3
- package/dist/lib/types.d.ts +24 -1
- package/dist/lib/versions.d.ts +0 -26
- package/dist/lib/versions.js +2 -145
- package/dist/lib/warn-unpushed.d.ts +40 -0
- package/dist/lib/warn-unpushed.js +128 -0
- package/dist/lib/whats-new.d.ts +5 -3
- package/dist/lib/whats-new.js +25 -5
- package/package.json +3 -1
- package/dist/lib/resources/index.d.ts +0 -53
- package/dist/lib/resources/index.js +0 -76
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
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { truncate } from '../lib/format.js';
|
|
2
3
|
import ora from 'ora';
|
|
3
4
|
import { checkbox } from '@inquirer/prompts';
|
|
4
5
|
import { capableAgents, isCapable } from '../lib/capabilities.js';
|
|
6
|
+
import { emit } from '../lib/events.js';
|
|
5
7
|
import { AGENTS, getAllCliStates, resolveAgentName, formatAgentError, registerMcpToTargets, unregisterMcpFromTargets, listInstalledMcpsWithScope, parseMcpConfig, getMcpConfigPathForHome, agentLabel, } from '../lib/agents.js';
|
|
6
8
|
import { readManifest, writeManifest, createDefaultManifest } from '../lib/manifest.js';
|
|
7
9
|
import { listMcpServerConfigs, discoverMcpConfigsFromRepo, installMcpConfigCentrally, } from '../lib/mcp.js';
|
|
@@ -292,6 +294,7 @@ Examples:
|
|
|
292
294
|
};
|
|
293
295
|
}
|
|
294
296
|
writeManifest(localPath, manifest);
|
|
297
|
+
emit('mcp.add', { module: 'mcp', server: name });
|
|
295
298
|
console.log(chalk.green(`Added MCP server '${name}' to manifest`));
|
|
296
299
|
console.log(chalk.gray('Run: agents mcp register to apply'));
|
|
297
300
|
});
|
|
@@ -440,6 +443,8 @@ Examples:
|
|
|
440
443
|
console.log(chalk.yellow('No MCP servers removed.'));
|
|
441
444
|
}
|
|
442
445
|
else {
|
|
446
|
+
for (const n of mcpsToRemove)
|
|
447
|
+
emit('mcp.remove', { module: 'mcp', server: n });
|
|
443
448
|
console.log(chalk.green(`\nRemoved ${removed} MCP server(s).`));
|
|
444
449
|
}
|
|
445
450
|
});
|
|
@@ -574,6 +579,7 @@ Examples:
|
|
|
574
579
|
targets = resolveConfiguredAgentTargets(config.agents, config.agentVersions, capableAgents('mcp'));
|
|
575
580
|
}
|
|
576
581
|
const results = await registerMcpToTargets(targets, mcpName, commandOrUrl, config.scope || 'user', transport, { headers: config.headers });
|
|
582
|
+
const applied = results.filter(r => r.success).length;
|
|
577
583
|
for (const result of results) {
|
|
578
584
|
if (result.success) {
|
|
579
585
|
console.log(` ${chalk.green('+')} ${formatTargetLabel(result.agentId, result.version)}`);
|
|
@@ -585,6 +591,8 @@ Examples:
|
|
|
585
591
|
console.log(` ${chalk.red('x')} ${formatTargetLabel(result.agentId, result.version)}: ${result.error}`);
|
|
586
592
|
}
|
|
587
593
|
}
|
|
594
|
+
if (applied > 0)
|
|
595
|
+
emit('mcp.register', { module: 'mcp', server: mcpName, applied });
|
|
588
596
|
}
|
|
589
597
|
});
|
|
590
598
|
}
|
|
@@ -764,7 +772,7 @@ function buildMcpRows(opts) {
|
|
|
764
772
|
const displayCommand = declaredCommand || firstCommand;
|
|
765
773
|
rows.push({
|
|
766
774
|
name,
|
|
767
|
-
description: displayCommand ?
|
|
775
|
+
description: displayCommand ? truncate(displayCommand, 60) : '',
|
|
768
776
|
extra: source,
|
|
769
777
|
targets,
|
|
770
778
|
buildDetail: () => formatMcpDetail(name, source, centralConfig, manifestConfig, displayCommand, targets),
|
|
@@ -811,8 +819,3 @@ function formatMcpDetail(name, source, centralConfig, manifestConfig, command, t
|
|
|
811
819
|
lines.push(buildTargetsSection(targets));
|
|
812
820
|
return lines.join('\n');
|
|
813
821
|
}
|
|
814
|
-
function truncateString(s, max) {
|
|
815
|
-
if (s.length <= max)
|
|
816
|
-
return s;
|
|
817
|
-
return s.slice(0, max - 1) + '…';
|
|
818
|
-
}
|
package/dist/commands/message.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { die } from '../lib/format.js';
|
|
2
3
|
import { getActiveSessions } from '../lib/session/active.js';
|
|
3
4
|
import { getTaskById, updateTaskStatus } from '../lib/cloud/store.js';
|
|
4
5
|
import { resolveProvider } from '../lib/cloud/registry.js';
|
|
5
6
|
import { mailboxDir, enqueue } from '../lib/mailbox.js';
|
|
6
7
|
import { resolveMessageTarget } from '../lib/mailbox-target.js';
|
|
7
|
-
function die(msg, code = 1) {
|
|
8
|
-
console.error(chalk.red(msg));
|
|
9
|
-
process.exit(code);
|
|
10
|
-
}
|
|
11
8
|
export function registerMessageCommand(program) {
|
|
12
9
|
program
|
|
13
10
|
.command('message <target> <text>')
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* through a standard agent CLI with no local proxy.
|
|
8
8
|
*/
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
-
import
|
|
10
|
+
import { readStdinSync } from '../lib/format.js';
|
|
11
11
|
import { spawn } from 'child_process';
|
|
12
12
|
import { listProfiles, readProfile, writeProfile, deleteProfile, profileExists, profileFromPreset, validateProfileName, getPresetForProfile, } from '../lib/profiles.js';
|
|
13
13
|
import { getPreset, listPresets, expandPreset } from '../lib/profiles-presets.js';
|
|
@@ -41,23 +41,6 @@ async function promptForSecret(message) {
|
|
|
41
41
|
return await password({ message, mask: true });
|
|
42
42
|
}
|
|
43
43
|
/** Read all available data from stdin synchronously, trimmed. */
|
|
44
|
-
function readStdinSync() {
|
|
45
|
-
const chunks = [];
|
|
46
|
-
const buf = Buffer.alloc(65536);
|
|
47
|
-
while (true) {
|
|
48
|
-
let bytesRead;
|
|
49
|
-
try {
|
|
50
|
-
bytesRead = fs.readSync(0, buf, 0, buf.length, null);
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
if (bytesRead === 0)
|
|
56
|
-
break;
|
|
57
|
-
chunks.push(Buffer.from(buf.subarray(0, bytesRead)));
|
|
58
|
-
}
|
|
59
|
-
return Buffer.concat(chunks).toString('utf-8').trim();
|
|
60
|
-
}
|
|
61
44
|
/** Ensure a provider API key exists in keychain, prompting or reading stdin if missing. */
|
|
62
45
|
async function ensureProviderToken(provider, signupUrl, fromStdin) {
|
|
63
46
|
const item = keychainItemName(provider);
|
package/dist/commands/repo.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { visibleWidth, padVisible } from '../lib/format.js';
|
|
2
3
|
import ora from 'ora';
|
|
3
4
|
import * as fs from 'fs';
|
|
4
5
|
import * as path from 'path';
|
|
@@ -26,7 +27,7 @@ function resolveRepoPath(target) {
|
|
|
26
27
|
return path.join(HOME, `.agents-${trimmed}`);
|
|
27
28
|
}
|
|
28
29
|
import { applyExtraAliasToVersions, ensureAgentsDir, getExtraRepoDir, getSystemAgentsDir, getUserAgentsDir, readMeta, resolveExtraRepoDir, updateMeta, } from '../lib/state.js';
|
|
29
|
-
import { parseSource, pullRepo, commitAndPush, isGitRepo, isSystemRepoOrigin } from '../lib/git.js';
|
|
30
|
+
import { parseSource, pullRepo, commitAndPush, isGitRepo, isSystemRepoOrigin, adoptRepo } from '../lib/git.js';
|
|
30
31
|
import { DEFAULT_SYSTEM_REPO } from '../lib/types.js';
|
|
31
32
|
import { ALL_AGENT_IDS, isAgentName, resolveAgentName } from '../lib/agents.js';
|
|
32
33
|
import { refresh } from '../lib/refresh.js';
|
|
@@ -218,14 +219,6 @@ async function diffResourceEntries(git, range) {
|
|
|
218
219
|
}
|
|
219
220
|
return out;
|
|
220
221
|
}
|
|
221
|
-
/** Visible character width of a string with embedded ANSI color codes. */
|
|
222
|
-
function visibleWidth(s) {
|
|
223
|
-
return s.replace(/\[[0-9;]*m/g, '').length;
|
|
224
|
-
}
|
|
225
|
-
/** Pad string with trailing spaces to a target visible column width. */
|
|
226
|
-
function padVisible(s, width) {
|
|
227
|
-
return s + ' '.repeat(Math.max(0, width - visibleWidth(s)));
|
|
228
|
-
}
|
|
229
222
|
/**
|
|
230
223
|
* Read one repo's status into structured data: branch, resource-level sync (what
|
|
231
224
|
* a pull/push would move), resource-level local edits, and remote URL + commit.
|
|
@@ -752,9 +745,9 @@ export function registerRepoCommands(program) {
|
|
|
752
745
|
await toggle(alias, false);
|
|
753
746
|
});
|
|
754
747
|
repoCmd
|
|
755
|
-
.command('pull [alias]')
|
|
756
|
-
.description('Pull updates. Aliases: "system" (~/.agents/.system/), "user" (~/.agents/), or any registered extra. No arg pulls all.')
|
|
757
|
-
.action(async (alias) => {
|
|
748
|
+
.command('pull [alias] [url]')
|
|
749
|
+
.description('Pull updates. Aliases: "system" (~/.agents/.system/), "user" (~/.agents/), or any registered extra. No arg pulls all. Pass a git URL to git-back a not-yet-cloned user repo: "agents repo pull user <url>".')
|
|
750
|
+
.action(async (alias, url) => {
|
|
758
751
|
const targets = collectRepoTargets(alias);
|
|
759
752
|
if (!targets) {
|
|
760
753
|
process.exitCode = 1;
|
|
@@ -766,8 +759,34 @@ export function registerRepoCommands(program) {
|
|
|
766
759
|
}
|
|
767
760
|
for (const t of targets) {
|
|
768
761
|
if (!fs.existsSync(t.dir) || !isGitRepo(t.dir)) {
|
|
769
|
-
|
|
770
|
-
|
|
762
|
+
// A plain (never-cloned) user repo — setup makes ~/.agents a bare dir and
|
|
763
|
+
// never git-clones it (state.ts ensureAgentsDir), so a fresh/Windows box
|
|
764
|
+
// silently falls out of config sync. If a URL was passed, git-back it in
|
|
765
|
+
// place (keeping local files, backing up any that differ); else guide the
|
|
766
|
+
// user. Only the USER repo gets this — system is cloned by setup, extras
|
|
767
|
+
// by `repo add`.
|
|
768
|
+
if (t.alias === 'user' && url) {
|
|
769
|
+
const spinner = ora(`Git-backing ${t.dir} from ${url}...`).start();
|
|
770
|
+
const res = await adoptRepo(url, t.dir);
|
|
771
|
+
if (!res.success) {
|
|
772
|
+
spinner.fail(`user: could not git-back — ${res.error}`);
|
|
773
|
+
process.exitCode = 1;
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
spinner.succeed(`user: git-backed -> ${res.commit}`);
|
|
777
|
+
if (res.backedUp.length > 0) {
|
|
778
|
+
console.log(chalk.yellow(` backed up ${res.backedUp.length} locally-modified file(s) to ${res.backupDir}`));
|
|
779
|
+
console.log(chalk.gray(` (${res.backedUp.slice(0, 5).join(', ')}${res.backedUp.length > 5 ? ', …' : ''})`));
|
|
780
|
+
}
|
|
781
|
+
// Now a git repo — fall through to the normal pull below.
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
console.log(chalk.yellow(` ${t.alias}: not a git repo, skipping`));
|
|
785
|
+
if (t.alias === 'user') {
|
|
786
|
+
console.log(chalk.gray(' git-back it from your config remote: agents repo pull user <git-url>'));
|
|
787
|
+
}
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
771
790
|
}
|
|
772
791
|
if (t.alias === 'system') {
|
|
773
792
|
// Skip system repo unless explicitly requested
|
|
@@ -58,3 +58,4 @@ export declare function resourceLayout(cols: number, o: {
|
|
|
58
58
|
}): ResourceLayout;
|
|
59
59
|
/** Build the sync targets section showing version pills grouped by agent. */
|
|
60
60
|
export declare function buildTargetsSection(targets: SyncTarget[]): string;
|
|
61
|
+
/** Pad a string to a fixed width, accounting for ANSI escape codes in length calculation. */
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* filtering, and paging.
|
|
8
8
|
*/
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
+
import { truncate, padVisible } from '../lib/format.js';
|
|
10
11
|
import { agentLabel } from '../lib/agents.js';
|
|
11
12
|
import { itemPicker } from '../lib/picker.js';
|
|
12
13
|
import { isInteractiveTerminal, isPromptCancelled, printWithPager } from './utils.js';
|
|
@@ -65,15 +66,15 @@ function filterRows(rows, query) {
|
|
|
65
66
|
}
|
|
66
67
|
/** Row label rendered inside the picker list. */
|
|
67
68
|
function formatPickerRow(row, opts) {
|
|
68
|
-
const name = chalk.cyan(
|
|
69
|
+
const name = chalk.cyan(padVisible(row.name, 22));
|
|
69
70
|
const extra = opts.extraLabel
|
|
70
|
-
? chalk.gray(
|
|
71
|
+
? chalk.gray(padVisible(row.extra ?? '-', 10))
|
|
71
72
|
: '';
|
|
72
73
|
const extra2 = opts.extra2Label
|
|
73
|
-
? chalk.gray(
|
|
74
|
+
? chalk.gray(padVisible(row.extra2 ?? '-', 16))
|
|
74
75
|
: '';
|
|
75
76
|
const descRaw = row.description ? truncate(row.description, 40) : '';
|
|
76
|
-
const desc =
|
|
77
|
+
const desc = padVisible(chalk.gray(descRaw), 42);
|
|
77
78
|
const sync = formatSyncSummary(row.targets, opts);
|
|
78
79
|
return `${name} ${extra}${extra2}${desc} ${sync}`;
|
|
79
80
|
}
|
|
@@ -252,16 +253,3 @@ function formatVersionPill(t) {
|
|
|
252
253
|
}
|
|
253
254
|
}
|
|
254
255
|
/** Pad a string to a fixed width, accounting for ANSI escape codes in length calculation. */
|
|
255
|
-
function padRight(s, width) {
|
|
256
|
-
// Strip ANSI for length calc
|
|
257
|
-
const raw = s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
258
|
-
if (raw.length >= width)
|
|
259
|
-
return s;
|
|
260
|
-
return s + ' '.repeat(width - raw.length);
|
|
261
|
-
}
|
|
262
|
-
/** Truncate a string to a maximum length, appending an ellipsis if needed. */
|
|
263
|
-
function truncate(s, max) {
|
|
264
|
-
if (s.length <= max)
|
|
265
|
-
return s;
|
|
266
|
-
return s.slice(0, max - 1) + '…';
|
|
267
|
-
}
|
|
@@ -9,6 +9,7 @@ import { type Command } from 'commander';
|
|
|
9
9
|
import { SSH_TARGET_RE, assertValidSshTarget } from '../lib/ssh-exec.js';
|
|
10
10
|
import { quoteWin32ExecArg } from '../lib/platform/index.js';
|
|
11
11
|
import { type SecretsBundle, type SecretsPolicy } from '../lib/secrets/bundles.js';
|
|
12
|
+
/** Read all available data from stdin synchronously, trimmed. */
|
|
12
13
|
/**
|
|
13
14
|
* Read the raw `.env` text for `import --from <path|->`. A `-` reads the .env
|
|
14
15
|
* from stdin (the SSH push path: `export --host` pipes the resolved dotenv over
|
|
@@ -16,6 +17,28 @@ import { type SecretsBundle, type SecretsPolicy } from '../lib/secrets/bundles.j
|
|
|
16
17
|
* a filesystem path.
|
|
17
18
|
*/
|
|
18
19
|
export declare function readImportDotenv(from: string): string;
|
|
20
|
+
/** Where `secrets import` pulls keys from, parsed off the unified `--from`. */
|
|
21
|
+
export type ImportSource = {
|
|
22
|
+
kind: 'dotenv';
|
|
23
|
+
path: string;
|
|
24
|
+
} | {
|
|
25
|
+
kind: '1password';
|
|
26
|
+
vault?: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'icloud';
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Parse the unified `--from <source>` value: a .env path (`-` reads stdin),
|
|
32
|
+
* `1password:<vault>` (bare `1password` prompts for the vault), or `icloud`
|
|
33
|
+
* (legacy iCloud Keychain bundles). The deprecated `--from-1password --vault`
|
|
34
|
+
* pair maps onto the 1password source. A file literally named `icloud` or
|
|
35
|
+
* `1password` can still be imported via an explicit path (`./icloud`).
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseImportSource(opts: {
|
|
38
|
+
from?: string;
|
|
39
|
+
from1password?: boolean;
|
|
40
|
+
vault?: string;
|
|
41
|
+
}): ImportSource;
|
|
19
42
|
/**
|
|
20
43
|
* Build the remote `agents secrets unlock` argv for `unlock --host`. `--all`
|
|
21
44
|
* forwards verbatim; otherwise the explicit bundle names. A `--ttl` is passed
|