@phnx-labs/agents-cli 1.14.2 → 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 +17 -7
- 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/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 +198 -11
- 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 +8 -1
- package/dist/lib/rotate.js +17 -4
- 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 +12 -6
- 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,19 +250,19 @@ 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
263
|
### Cross-machine sync via iCloud Keychain
|
|
264
264
|
|
|
265
|
-
Pass `--icloud-sync` when creating a bundle and the values are written to the iCloud-synced keychain. Sign into the same iCloud account on another Mac (with iCloud Keychain enabled) and the
|
|
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
266
|
|
|
267
267
|
```bash
|
|
268
268
|
# On laptop:
|
|
@@ -270,13 +270,23 @@ agents secrets create npm-tokens --icloud-sync
|
|
|
270
270
|
agents secrets add npm-tokens NPM_TOKEN # value lives in iCloud Keychain
|
|
271
271
|
|
|
272
272
|
# On another Mac (same iCloud account):
|
|
273
|
-
agents secrets
|
|
274
|
-
|
|
273
|
+
agents secrets list # npm-tokens is already there;
|
|
274
|
+
agents run claude "..." --secrets npm-tokens # injects NPM_TOKEN automatically
|
|
275
275
|
```
|
|
276
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`
|
|
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
278
|
|
|
279
|
-
Bundle
|
|
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
|
+
```
|
|
280
290
|
|
|
281
291
|
---
|
|
282
292
|
|
|
@@ -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;
|