@phnx-labs/agents-cli 1.14.1 → 1.14.3
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/README.md +31 -3
- package/dist/commands/browser.d.ts +2 -0
- package/dist/commands/browser.js +388 -0
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/doctor.d.ts +16 -9
- package/dist/commands/doctor.js +248 -12
- package/dist/commands/exec.js +17 -17
- package/dist/commands/prune.js +9 -3
- package/dist/commands/refresh-rules.d.ts +15 -0
- package/dist/commands/{refresh-memory.js → refresh-rules.js} +14 -14
- package/dist/commands/routines.js +1 -1
- package/dist/commands/rules.js +100 -4
- package/dist/commands/secrets.js +206 -12
- package/dist/commands/sync.js +19 -0
- package/dist/commands/teams.js +162 -22
- package/dist/commands/trash.d.ts +10 -0
- package/dist/commands/trash.js +187 -0
- package/dist/commands/view.js +46 -13
- package/dist/index.js +62 -4
- package/dist/lib/agents.js +2 -2
- package/dist/lib/browser/cdp.d.ts +24 -0
- package/dist/lib/browser/cdp.js +94 -0
- package/dist/lib/browser/chrome.d.ts +16 -0
- package/dist/lib/browser/chrome.js +157 -0
- package/dist/lib/browser/drivers/local.d.ts +8 -0
- package/dist/lib/browser/drivers/local.js +22 -0
- package/dist/lib/browser/drivers/ssh.d.ts +9 -0
- package/dist/lib/browser/drivers/ssh.js +129 -0
- package/dist/lib/browser/index.d.ts +5 -0
- package/dist/lib/browser/index.js +5 -0
- package/dist/lib/browser/input.d.ts +6 -0
- package/dist/lib/browser/input.js +52 -0
- package/dist/lib/browser/ipc.d.ts +12 -0
- package/dist/lib/browser/ipc.js +223 -0
- package/dist/lib/browser/profiles.d.ts +11 -0
- package/dist/lib/browser/profiles.js +61 -0
- package/dist/lib/browser/refs.d.ts +21 -0
- package/dist/lib/browser/refs.js +88 -0
- package/dist/lib/browser/service.d.ts +45 -0
- package/dist/lib/browser/service.js +404 -0
- package/dist/lib/browser/types.d.ts +73 -0
- package/dist/lib/browser/types.js +7 -0
- package/dist/lib/cloud/codex.js +1 -1
- package/dist/lib/cloud/registry.js +2 -2
- package/dist/lib/cloud/rush.js +2 -2
- package/dist/lib/cloud/store.js +2 -2
- package/dist/lib/daemon.d.ts +1 -1
- package/dist/lib/daemon.js +47 -11
- package/dist/lib/diff-text.d.ts +25 -0
- package/dist/lib/diff-text.js +47 -0
- package/dist/lib/doctor-diff.d.ts +64 -0
- package/dist/lib/doctor-diff.js +497 -0
- package/dist/lib/git.js +3 -3
- package/dist/lib/hooks.d.ts +6 -0
- package/dist/lib/hooks.js +6 -1
- package/dist/lib/migrate.js +77 -0
- package/dist/lib/pty-client.js +3 -3
- package/dist/lib/pty-server.js +36 -7
- package/dist/lib/resources.js +1 -1
- package/dist/lib/rotate.d.ts +43 -26
- package/dist/lib/rotate.js +99 -44
- package/dist/lib/rules/compile.d.ts +104 -0
- package/dist/lib/{memory-compile.js → rules/compile.js} +160 -21
- package/dist/lib/rules/compose.d.ts +78 -0
- package/dist/lib/rules/compose.js +170 -0
- package/dist/lib/{memory.d.ts → rules/rules.d.ts} +5 -5
- package/dist/lib/{memory.js → rules/rules.js} +10 -10
- package/dist/lib/secrets/AgentsKeychain.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/AgentsKeychain.app/Contents/MacOS/AgentsKeychain +0 -0
- package/dist/lib/secrets/bundles.d.ts +61 -4
- package/dist/lib/secrets/bundles.js +222 -54
- package/dist/lib/secrets/index.d.ts +24 -5
- package/dist/lib/secrets/index.js +70 -41
- package/dist/lib/session/active.js +5 -5
- package/dist/lib/session/db.js +4 -4
- package/dist/lib/session/discover.js +2 -2
- package/dist/lib/session/render.js +21 -7
- package/dist/lib/shims.d.ts +28 -4
- package/dist/lib/shims.js +72 -14
- package/dist/lib/state.d.ts +22 -28
- package/dist/lib/state.js +83 -76
- package/dist/lib/sync-manifest.d.ts +2 -2
- package/dist/lib/sync-manifest.js +5 -5
- package/dist/lib/teams/agents.d.ts +4 -2
- package/dist/lib/teams/agents.js +11 -4
- package/dist/lib/teams/api.d.ts +1 -1
- package/dist/lib/teams/api.js +2 -2
- package/dist/lib/teams/index.d.ts +1 -0
- package/dist/lib/teams/index.js +1 -0
- package/dist/lib/teams/persistence.js +3 -3
- package/dist/lib/teams/registry.d.ts +8 -1
- package/dist/lib/teams/registry.js +8 -2
- package/dist/lib/teams/worktree.d.ts +30 -0
- package/dist/lib/teams/worktree.js +96 -0
- package/dist/lib/types.d.ts +13 -7
- package/dist/lib/types.js +3 -3
- package/dist/lib/versions.d.ts +30 -2
- package/dist/lib/versions.js +127 -105
- package/package.json +1 -1
- package/scripts/postinstall.js +29 -0
- package/dist/commands/refresh-memory.d.ts +0 -15
- package/dist/lib/memory-compile.d.ts +0 -66
package/README.md
CHANGED
|
@@ -250,16 +250,44 @@ agents secrets create prod-stripe
|
|
|
250
250
|
agents secrets add prod-stripe STRIPE_SECRET_KEY # Prompts, stores in Keychain
|
|
251
251
|
agents secrets add prod-stripe TEST_CARD --value "4242..."
|
|
252
252
|
|
|
253
|
-
# Injected at run time.
|
|
253
|
+
# Injected at run time. Bundle definitions live in the Keychain, not on disk.
|
|
254
254
|
agents run claude "charge a test card" --secrets prod-stripe
|
|
255
255
|
```
|
|
256
256
|
|
|
257
257
|
<p align="center">
|
|
258
|
-
<img src="assets/secrets.svg" alt="How agents-cli secrets work:
|
|
258
|
+
<img src="assets/secrets.svg" alt="How agents-cli secrets work: bundle definitions live in the macOS Keychain alongside their values, agents-cli resolves at runtime and injects the env into the child process" width="100%" />
|
|
259
259
|
</p>
|
|
260
260
|
|
|
261
261
|
Merge order: profile env < `--secrets` < `--env K=V`. A missing keychain item aborts before the child starts.
|
|
262
262
|
|
|
263
|
+
### Cross-machine sync via iCloud Keychain
|
|
264
|
+
|
|
265
|
+
Pass `--icloud-sync` when creating a bundle and both the bundle definition and its values are written to the iCloud-synced keychain. Sign into the same iCloud account on another Mac (with iCloud Keychain enabled) and the bundle appears there within seconds — no copy-paste, no `.env` files emailed to yourself, no shared secret stores.
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
# On laptop:
|
|
269
|
+
agents secrets create npm-tokens --icloud-sync
|
|
270
|
+
agents secrets add npm-tokens NPM_TOKEN # value lives in iCloud Keychain
|
|
271
|
+
|
|
272
|
+
# On another Mac (same iCloud account):
|
|
273
|
+
agents secrets list # npm-tokens is already there;
|
|
274
|
+
agents run claude "..." --secrets npm-tokens # injects NPM_TOKEN automatically
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Under the hood, `--icloud-sync` routes writes through a notarized helper app (`AgentsKeychain.app`) that holds the entitlement macOS requires for `kSecAttrSynchronizable`. Bundles without `--icloud-sync` stay device-local.
|
|
278
|
+
|
|
279
|
+
Bundle definitions sync via iCloud Keychain too — no `agents repo push` needed for secrets, no recreate step on each Mac. Nothing about secrets ever lives in plaintext on disk.
|
|
280
|
+
|
|
281
|
+
### Per-secret metadata and rotation
|
|
282
|
+
|
|
283
|
+
Tag each secret with `--type`, `--expires`, and `--note` so the bundle is self-documenting. `--expires` is always future-dated (`YYYY-MM-DD`); past or same-day values are rejected. Use `agents secrets rotate <bundle> <key>` to refresh a credential — `add` only creates new keys, `rotate` replaces the value and preserves metadata unless overridden.
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
agents secrets add prod STRIPE_API_KEY --type api-key --expires 2027-01-15 --note "Live key, owner: payments-team"
|
|
287
|
+
agents secrets rotate prod STRIPE_API_KEY --note "rotated after suspected leak"
|
|
288
|
+
agents secrets list # EXPIRING column flags secrets due in the next 30 days
|
|
289
|
+
```
|
|
290
|
+
|
|
263
291
|
---
|
|
264
292
|
|
|
265
293
|
## Routines
|
|
@@ -403,7 +431,7 @@ No. API keys come from your shell environment or each agent CLI's existing auth.
|
|
|
403
431
|
|
|
404
432
|
macOS and Linux. Windows via WSL works but isn't first-class yet.
|
|
405
433
|
|
|
406
|
-
**macOS-only features:** Keychain-based secrets (`agents secrets`, `agents profiles login`) require macOS. On Linux, use environment variables or `.env` files for API keys. Native Linux credential store support is planned.
|
|
434
|
+
**macOS-only features:** Keychain-based secrets (`agents secrets`, `agents profiles login`) require macOS. The `--icloud-sync` flag on bundles requires macOS + iCloud Keychain enabled. On Linux, use environment variables or `.env` files for API keys. Native Linux credential store support is planned.
|
|
407
435
|
|
|
408
436
|
### Do I need Node.js?
|
|
409
437
|
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { listProfiles, getProfile, createProfile, deleteProfile, } from '../lib/browser/profiles.js';
|
|
2
|
+
import { sendIPCRequest } from '../lib/browser/ipc.js';
|
|
3
|
+
import { isValidTaskId } from '../lib/browser/types.js';
|
|
4
|
+
export function registerBrowserCommand(program) {
|
|
5
|
+
const browser = program
|
|
6
|
+
.command('browser')
|
|
7
|
+
.description('Browser automation via CDP');
|
|
8
|
+
registerProfilesCommands(browser);
|
|
9
|
+
registerTaskCommands(browser);
|
|
10
|
+
}
|
|
11
|
+
function registerProfilesCommands(browser) {
|
|
12
|
+
const profiles = browser
|
|
13
|
+
.command('profiles')
|
|
14
|
+
.description('Manage browser profiles');
|
|
15
|
+
profiles
|
|
16
|
+
.command('list')
|
|
17
|
+
.alias('ls')
|
|
18
|
+
.description('List all browser profiles')
|
|
19
|
+
.action(async () => {
|
|
20
|
+
const allProfiles = await listProfiles();
|
|
21
|
+
if (allProfiles.length === 0) {
|
|
22
|
+
console.log('No browser profiles configured.');
|
|
23
|
+
console.log('Create one with: agents browser profiles create <name> --endpoint <url>');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
console.log('NAME'.padEnd(20) + 'BROWSER'.padEnd(12) + 'ENDPOINTS');
|
|
27
|
+
console.log('-'.repeat(72));
|
|
28
|
+
for (const p of allProfiles) {
|
|
29
|
+
const endpoints = p.endpoints.join(', ');
|
|
30
|
+
console.log(p.name.padEnd(20) + (p.browser || '-').padEnd(12) + endpoints);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const VALID_BROWSERS = ['chrome', 'comet', 'chromium', 'brave', 'edge'];
|
|
34
|
+
profiles
|
|
35
|
+
.command('create <name>')
|
|
36
|
+
.description('Create a new browser profile')
|
|
37
|
+
.requiredOption('-b, --browser <type>', `Browser type: ${VALID_BROWSERS.join(', ')}`)
|
|
38
|
+
.requiredOption('-e, --endpoint <url>', 'CDP endpoint URL (repeatable)', collect, [])
|
|
39
|
+
.option('-s, --secrets <bundle>', 'Secrets bundle to inject')
|
|
40
|
+
.option('-d, --description <text>', 'Profile description')
|
|
41
|
+
.option('--headless', 'Run in headless mode')
|
|
42
|
+
.action(async (name, opts) => {
|
|
43
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
44
|
+
console.error('Profile name must be lowercase alphanumeric with hyphens');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
if (!VALID_BROWSERS.includes(opts.browser)) {
|
|
48
|
+
console.error(`Invalid browser type. Must be one of: ${VALID_BROWSERS.join(', ')}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const profile = {
|
|
52
|
+
name,
|
|
53
|
+
description: opts.description,
|
|
54
|
+
browser: opts.browser,
|
|
55
|
+
endpoints: opts.endpoint,
|
|
56
|
+
secrets: opts.secrets,
|
|
57
|
+
chrome: opts.headless ? { headless: true } : undefined,
|
|
58
|
+
};
|
|
59
|
+
await createProfile(profile);
|
|
60
|
+
console.log(`Created profile: ${name}`);
|
|
61
|
+
});
|
|
62
|
+
profiles
|
|
63
|
+
.command('show <name>')
|
|
64
|
+
.description('Show profile details')
|
|
65
|
+
.action(async (name) => {
|
|
66
|
+
const profile = await getProfile(name);
|
|
67
|
+
if (!profile) {
|
|
68
|
+
console.error(`Profile "${name}" not found`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
console.log(`Name: ${profile.name}`);
|
|
72
|
+
console.log(`Browser: ${profile.browser}`);
|
|
73
|
+
if (profile.description)
|
|
74
|
+
console.log(`Description: ${profile.description}`);
|
|
75
|
+
console.log(`Endpoints:`);
|
|
76
|
+
for (const e of profile.endpoints) {
|
|
77
|
+
console.log(` - ${e}`);
|
|
78
|
+
}
|
|
79
|
+
if (profile.secrets)
|
|
80
|
+
console.log(`Secrets: ${profile.secrets}`);
|
|
81
|
+
if (profile.chrome?.headless)
|
|
82
|
+
console.log(`Headless: true`);
|
|
83
|
+
});
|
|
84
|
+
profiles
|
|
85
|
+
.command('delete <name>')
|
|
86
|
+
.description('Delete a browser profile')
|
|
87
|
+
.action(async (name) => {
|
|
88
|
+
await deleteProfile(name);
|
|
89
|
+
console.log(`Deleted profile: ${name}`);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function registerTaskCommands(browser) {
|
|
93
|
+
browser
|
|
94
|
+
.command('start [task]')
|
|
95
|
+
.description('Start a browser task')
|
|
96
|
+
.requiredOption('-p, --profile <name>', 'Browser profile to use')
|
|
97
|
+
.action(async (task, opts) => {
|
|
98
|
+
if (task && !isValidTaskId(task)) {
|
|
99
|
+
console.error('Task ID must be lowercase alphanumeric with hyphens');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
const response = await sendIPCRequest({
|
|
103
|
+
action: 'start',
|
|
104
|
+
profile: opts.profile,
|
|
105
|
+
task,
|
|
106
|
+
});
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
console.error(response.error);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
console.log(response.task);
|
|
112
|
+
});
|
|
113
|
+
browser
|
|
114
|
+
.command('stop <task>')
|
|
115
|
+
.description('Stop a browser task and close its tabs')
|
|
116
|
+
.action(async (task) => {
|
|
117
|
+
const response = await sendIPCRequest({
|
|
118
|
+
action: 'stop',
|
|
119
|
+
task,
|
|
120
|
+
});
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
console.error(response.error);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
console.log(`Stopped task: ${task}`);
|
|
126
|
+
});
|
|
127
|
+
browser
|
|
128
|
+
.command('navigate <task> <url>')
|
|
129
|
+
.description('Open a URL in the task window')
|
|
130
|
+
.option('-p, --profile <name>', 'Browser profile (optional if task is unique)')
|
|
131
|
+
.action(async (task, url, opts) => {
|
|
132
|
+
const response = await sendIPCRequest({
|
|
133
|
+
action: 'navigate',
|
|
134
|
+
task,
|
|
135
|
+
url,
|
|
136
|
+
profile: opts.profile,
|
|
137
|
+
});
|
|
138
|
+
if (!response.ok) {
|
|
139
|
+
console.error(response.error);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
console.log(`Opened tab ${response.tabId}: ${url}`);
|
|
143
|
+
});
|
|
144
|
+
browser
|
|
145
|
+
.command('tabs [task]')
|
|
146
|
+
.description('List open tabs')
|
|
147
|
+
.option('-p, --profile <name>', 'Filter by profile')
|
|
148
|
+
.action(async (task, opts) => {
|
|
149
|
+
const response = await sendIPCRequest({
|
|
150
|
+
action: 'tabs',
|
|
151
|
+
task,
|
|
152
|
+
profile: opts.profile,
|
|
153
|
+
});
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
console.error(response.error);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
if (!response.tabs || response.tabs.length === 0) {
|
|
159
|
+
console.log('No tabs open');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
console.log('TASK'.padEnd(15) + 'TAB'.padEnd(12) + 'URL');
|
|
163
|
+
console.log('-'.repeat(80));
|
|
164
|
+
for (const tab of response.tabs) {
|
|
165
|
+
const shortId = tab.id.slice(0, 8);
|
|
166
|
+
console.log(tab.task.padEnd(15) +
|
|
167
|
+
shortId.padEnd(12) +
|
|
168
|
+
tab.url.slice(0, 55));
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
browser
|
|
172
|
+
.command('close <task> [tabId]')
|
|
173
|
+
.description('Close tabs for a task')
|
|
174
|
+
.action(async (task, tabId) => {
|
|
175
|
+
const response = await sendIPCRequest({
|
|
176
|
+
action: 'close',
|
|
177
|
+
task,
|
|
178
|
+
tabId,
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
console.error(response.error);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
console.log(tabId ? `Closed tab ${tabId}` : `Closed all tabs for task ${task}`);
|
|
185
|
+
});
|
|
186
|
+
browser
|
|
187
|
+
.command('screenshot <task> [tabId]')
|
|
188
|
+
.description('Take a screenshot')
|
|
189
|
+
.option('-o, --output <path>', 'Output path')
|
|
190
|
+
.action(async (task, tabId, opts) => {
|
|
191
|
+
const response = await sendIPCRequest({
|
|
192
|
+
action: 'screenshot',
|
|
193
|
+
task,
|
|
194
|
+
tabId,
|
|
195
|
+
path: opts.output,
|
|
196
|
+
});
|
|
197
|
+
if (!response.ok) {
|
|
198
|
+
console.error(response.error);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
console.log(response.path);
|
|
202
|
+
});
|
|
203
|
+
browser
|
|
204
|
+
.command('evaluate <task> <tabId> <expression>')
|
|
205
|
+
.description('Evaluate JavaScript in a tab')
|
|
206
|
+
.action(async (task, tabId, expression) => {
|
|
207
|
+
const response = await sendIPCRequest({
|
|
208
|
+
action: 'evaluate',
|
|
209
|
+
task,
|
|
210
|
+
tabId,
|
|
211
|
+
expr: expression,
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
console.error(response.error);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
console.log(JSON.stringify(response.result, null, 2));
|
|
218
|
+
});
|
|
219
|
+
browser
|
|
220
|
+
.command('status')
|
|
221
|
+
.description('Show running browser tasks')
|
|
222
|
+
.option('-p, --profile <name>', 'Filter by profile')
|
|
223
|
+
.action(async (opts) => {
|
|
224
|
+
const response = await sendIPCRequest({
|
|
225
|
+
action: 'status',
|
|
226
|
+
profile: opts.profile,
|
|
227
|
+
});
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
console.error(response.error);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
if (!response.profiles || response.profiles.length === 0) {
|
|
233
|
+
console.log('No browser profiles running');
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
for (const profile of response.profiles) {
|
|
237
|
+
console.log(`\n${profile.name} (port ${profile.port}, pid ${profile.pid})`);
|
|
238
|
+
if (profile.tasks.length === 0) {
|
|
239
|
+
console.log(' No active tasks');
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
console.log(' TASK'.padEnd(17) + 'TABS'.padEnd(8) + 'CREATED');
|
|
243
|
+
for (const task of profile.tasks) {
|
|
244
|
+
const age = formatAge(task.createdAt);
|
|
245
|
+
console.log(' ' +
|
|
246
|
+
task.id.padEnd(15) +
|
|
247
|
+
String(task.tabCount).padEnd(8) +
|
|
248
|
+
age);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
browser
|
|
254
|
+
.command('tasks')
|
|
255
|
+
.description('List all browser tasks')
|
|
256
|
+
.option('-p, --profile <name>', 'Filter by profile')
|
|
257
|
+
.action(async (opts) => {
|
|
258
|
+
const response = await sendIPCRequest({
|
|
259
|
+
action: 'status',
|
|
260
|
+
profile: opts.profile,
|
|
261
|
+
});
|
|
262
|
+
if (!response.ok) {
|
|
263
|
+
console.error(response.error);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
const allTasks = [];
|
|
267
|
+
for (const profile of response.profiles || []) {
|
|
268
|
+
for (const task of profile.tasks) {
|
|
269
|
+
allTasks.push({
|
|
270
|
+
profile: profile.name,
|
|
271
|
+
id: task.id,
|
|
272
|
+
tabs: task.tabCount,
|
|
273
|
+
created: task.createdAt,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (allTasks.length === 0) {
|
|
278
|
+
console.log('No active tasks');
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
console.log('PROFILE'.padEnd(18) + 'TASK'.padEnd(15) + 'TABS'.padEnd(8) + 'CREATED');
|
|
282
|
+
console.log('-'.repeat(55));
|
|
283
|
+
for (const t of allTasks) {
|
|
284
|
+
console.log(t.profile.padEnd(18) +
|
|
285
|
+
t.id.padEnd(15) +
|
|
286
|
+
String(t.tabs).padEnd(8) +
|
|
287
|
+
formatAge(t.created));
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
browser
|
|
291
|
+
.command('refs <task> [tabId]')
|
|
292
|
+
.description('Get DOM refs for interactive elements')
|
|
293
|
+
.option('--all', 'Include non-interactive elements')
|
|
294
|
+
.option('-l, --limit <n>', 'Max elements (default 500)', '500')
|
|
295
|
+
.action(async (task, tabId, opts) => {
|
|
296
|
+
const response = await sendIPCRequest({
|
|
297
|
+
action: 'refs',
|
|
298
|
+
task,
|
|
299
|
+
tabId,
|
|
300
|
+
interactive: !opts.all,
|
|
301
|
+
limit: parseInt(opts.limit, 10),
|
|
302
|
+
});
|
|
303
|
+
if (!response.ok) {
|
|
304
|
+
console.error(response.error);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
console.log(response.refs);
|
|
308
|
+
});
|
|
309
|
+
browser
|
|
310
|
+
.command('click <task> <tabId> <ref>')
|
|
311
|
+
.description('Click an element by ref')
|
|
312
|
+
.action(async (task, tabId, ref) => {
|
|
313
|
+
const response = await sendIPCRequest({
|
|
314
|
+
action: 'click',
|
|
315
|
+
task,
|
|
316
|
+
tabId,
|
|
317
|
+
ref: parseInt(ref, 10),
|
|
318
|
+
});
|
|
319
|
+
if (!response.ok) {
|
|
320
|
+
console.error(response.error);
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
console.log('Clicked');
|
|
324
|
+
});
|
|
325
|
+
browser
|
|
326
|
+
.command('type <task> <tabId> <ref> <text>')
|
|
327
|
+
.description('Type text into an element by ref')
|
|
328
|
+
.action(async (task, tabId, ref, text) => {
|
|
329
|
+
const response = await sendIPCRequest({
|
|
330
|
+
action: 'type',
|
|
331
|
+
task,
|
|
332
|
+
tabId,
|
|
333
|
+
ref: parseInt(ref, 10),
|
|
334
|
+
text,
|
|
335
|
+
});
|
|
336
|
+
if (!response.ok) {
|
|
337
|
+
console.error(response.error);
|
|
338
|
+
process.exit(1);
|
|
339
|
+
}
|
|
340
|
+
console.log('Typed');
|
|
341
|
+
});
|
|
342
|
+
browser
|
|
343
|
+
.command('press <task> <tabId> <key>')
|
|
344
|
+
.description('Press a key (Enter, Tab, Escape, etc)')
|
|
345
|
+
.action(async (task, tabId, key) => {
|
|
346
|
+
const response = await sendIPCRequest({
|
|
347
|
+
action: 'press',
|
|
348
|
+
task,
|
|
349
|
+
tabId,
|
|
350
|
+
key,
|
|
351
|
+
});
|
|
352
|
+
if (!response.ok) {
|
|
353
|
+
console.error(response.error);
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
console.log('Pressed');
|
|
357
|
+
});
|
|
358
|
+
browser
|
|
359
|
+
.command('hover <task> <tabId> <ref>')
|
|
360
|
+
.description('Hover over an element by ref')
|
|
361
|
+
.action(async (task, tabId, ref) => {
|
|
362
|
+
const response = await sendIPCRequest({
|
|
363
|
+
action: 'hover',
|
|
364
|
+
task,
|
|
365
|
+
tabId,
|
|
366
|
+
ref: parseInt(ref, 10),
|
|
367
|
+
});
|
|
368
|
+
if (!response.ok) {
|
|
369
|
+
console.error(response.error);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
console.log('Hovered');
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function collect(val, memo) {
|
|
376
|
+
memo.push(val);
|
|
377
|
+
return memo;
|
|
378
|
+
}
|
|
379
|
+
function formatAge(timestamp) {
|
|
380
|
+
const seconds = Math.floor((Date.now() - timestamp) / 1000);
|
|
381
|
+
if (seconds < 60)
|
|
382
|
+
return `${seconds}s ago`;
|
|
383
|
+
const minutes = Math.floor(seconds / 60);
|
|
384
|
+
if (minutes < 60)
|
|
385
|
+
return `${minutes}m ago`;
|
|
386
|
+
const hours = Math.floor(minutes / 60);
|
|
387
|
+
return `${hours}h ago`;
|
|
388
|
+
}
|
package/dist/commands/daemon.js
CHANGED
|
@@ -92,7 +92,7 @@ you never need to start it manually.
|
|
|
92
92
|
warnDeprecated('logs', 'agents routines scheduler-logs');
|
|
93
93
|
if (options.follow) {
|
|
94
94
|
const { getAgentsDir } = await import('../lib/state.js');
|
|
95
|
-
const logPath = path.join(getAgentsDir(), 'daemon.
|
|
95
|
+
const logPath = path.join(getAgentsDir(), 'helpers/daemon/logs.jsonl');
|
|
96
96
|
const child = spawn('tail', ['-f', logPath], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
97
97
|
child.stdout.pipe(process.stdout);
|
|
98
98
|
child.stderr.pipe(process.stderr);
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `agents doctor` — diagnostic readout across the install.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
*
|
|
6
|
+
* 1. Overview (no target): three sections —
|
|
7
|
+
* - CLI availability (which agent binaries can be invoked).
|
|
8
|
+
* - Sync status per default version (fresh / stale / never-synced).
|
|
9
|
+
* - Orphans per default version per resource type.
|
|
10
|
+
*
|
|
11
|
+
* 2. Target mode: `agents doctor <agent>[@version]` — full per-resource
|
|
12
|
+
* diff for a single (agent, version) against the current cwd's resolved
|
|
13
|
+
* sources. Reports ok / DIFF / MISS / EXTRA per resource with the source
|
|
14
|
+
* layer (project, user, system, extra repo). With `--diff`, renders a
|
|
15
|
+
* unified diff body for each divergent file. Mirrors the resolution that
|
|
16
|
+
* the shim drives at runtime: project > user > system > extras.
|
|
17
|
+
*
|
|
18
|
+
* Read-only: doctor never mutates state. Run `agents prune` to act on orphan
|
|
19
|
+
* readouts, or just launch the agent to apply pending sync.
|
|
13
20
|
*/
|
|
14
21
|
import type { Command } from 'commander';
|
|
15
22
|
export declare function registerDoctorCommand(program: Command): void;
|