@phnx-labs/agents-cli 1.20.0 → 1.20.4
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 +81 -0
- package/README.md +4 -4
- package/dist/commands/cli.js +3 -3
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +24 -7
- package/dist/commands/exec.js +36 -16
- package/dist/commands/feedback.d.ts +7 -0
- package/dist/commands/feedback.js +89 -0
- package/dist/commands/helper.d.ts +12 -0
- package/dist/commands/helper.js +87 -0
- package/dist/commands/hooks.js +86 -7
- package/dist/commands/import.js +90 -37
- package/dist/commands/mcp.js +166 -10
- package/dist/commands/packages.js +196 -27
- package/dist/commands/permissions.js +21 -6
- package/dist/commands/profiles.d.ts +8 -0
- package/dist/commands/profiles.js +117 -4
- package/dist/commands/pull.js +4 -4
- package/dist/commands/routines.js +6 -6
- package/dist/commands/rules.js +8 -4
- package/dist/commands/secrets-migrate.d.ts +24 -0
- package/dist/commands/secrets-migrate.js +198 -0
- package/dist/commands/secrets-sync.d.ts +11 -0
- package/dist/commands/secrets-sync.js +155 -0
- package/dist/commands/secrets.js +74 -39
- package/dist/commands/skills.js +22 -5
- package/dist/commands/subagents.js +69 -49
- package/dist/commands/teams.js +48 -10
- package/dist/commands/utils.d.ts +33 -0
- package/dist/commands/utils.js +139 -0
- package/dist/commands/versions.js +4 -4
- package/dist/commands/view.d.ts +6 -0
- package/dist/commands/view.js +169 -8
- package/dist/commands/workflows.js +29 -6
- package/dist/index.js +4 -0
- package/dist/lib/acp/client.js +6 -1
- package/dist/lib/agents.d.ts +4 -0
- package/dist/lib/agents.js +41 -17
- package/dist/lib/auto-pull-worker.js +18 -1
- package/dist/lib/browser/chrome.js +4 -0
- package/dist/lib/browser/drivers/ssh.js +1 -1
- package/dist/lib/browser/profiles.d.ts +3 -3
- package/dist/lib/browser/profiles.js +3 -3
- package/dist/lib/browser/service.js +19 -0
- package/dist/lib/browser/types.d.ts +4 -4
- package/dist/lib/cli-resources.d.ts +36 -8
- package/dist/lib/cli-resources.js +268 -46
- package/dist/lib/cloud/factory.d.ts +1 -1
- package/dist/lib/cloud/factory.js +1 -1
- package/dist/lib/events.d.ts +16 -2
- package/dist/lib/events.js +33 -2
- package/dist/lib/exec.d.ts +39 -11
- package/dist/lib/exec.js +90 -31
- package/dist/lib/help.js +11 -5
- package/dist/lib/hooks/cache.d.ts +38 -0
- package/dist/lib/hooks/cache.js +242 -0
- package/dist/lib/hooks/profile.d.ts +33 -0
- package/dist/lib/hooks/profile.js +129 -0
- package/dist/lib/hooks.d.ts +0 -10
- package/dist/lib/hooks.js +68 -15
- package/dist/lib/import.d.ts +21 -0
- package/dist/lib/import.js +55 -2
- package/dist/lib/mcp.d.ts +15 -0
- package/dist/lib/mcp.js +40 -0
- package/dist/lib/permissions.d.ts +13 -0
- package/dist/lib/permissions.js +51 -1
- package/dist/lib/plugin-marketplace.d.ts +10 -0
- package/dist/lib/plugin-marketplace.js +47 -1
- package/dist/lib/plugins.js +15 -1
- package/dist/lib/profiles-presets.d.ts +26 -0
- package/dist/lib/profiles-presets.js +187 -8
- package/dist/lib/profiles.d.ts +34 -0
- package/dist/lib/profiles.js +112 -1
- package/dist/lib/pty-server.js +27 -3
- package/dist/lib/routines-format.d.ts +17 -5
- package/dist/lib/routines-format.js +37 -16
- package/dist/lib/routines.d.ts +1 -1
- package/dist/lib/routines.js +2 -2
- package/dist/lib/runner.js +64 -10
- 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/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -9
- package/dist/lib/secrets/bundles.d.ts +18 -22
- package/dist/lib/secrets/bundles.js +75 -99
- package/dist/lib/secrets/index.d.ts +51 -27
- package/dist/lib/secrets/index.js +147 -156
- package/dist/lib/secrets/install-helper.d.ts +45 -0
- package/dist/lib/secrets/install-helper.js +165 -0
- package/dist/lib/secrets/linux.js +4 -4
- package/dist/lib/secrets/sync.d.ts +56 -0
- package/dist/lib/secrets/sync.js +180 -0
- package/dist/lib/session/render.js +4 -4
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/shims.d.ts +4 -1
- package/dist/lib/shims.js +5 -35
- package/dist/lib/state.d.ts +14 -1
- package/dist/lib/state.js +49 -5
- package/dist/lib/teams/agents.d.ts +5 -4
- package/dist/lib/teams/agents.js +47 -21
- package/dist/lib/teams/api.d.ts +2 -1
- package/dist/lib/teams/api.js +4 -3
- package/dist/lib/types.d.ts +57 -1
- package/dist/lib/types.js +2 -0
- package/dist/lib/usage.d.ts +27 -2
- package/dist/lib/usage.js +100 -17
- package/dist/lib/versions.d.ts +35 -1
- package/dist/lib/versions.js +288 -64
- package/package.json +13 -12
- package/scripts/install-helper.js +97 -0
- package/scripts/postinstall.js +16 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ import { registerPullCommand } from './commands/pull.js';
|
|
|
59
59
|
import { registerRepoCommands } from './commands/repo.js';
|
|
60
60
|
import { registerSetupCommand, runSetup } from './commands/setup.js';
|
|
61
61
|
import { registerStatusCommand } from './commands/status.js';
|
|
62
|
+
import { registerFeedbackCommand } from './commands/feedback.js';
|
|
62
63
|
import { registerViewCommand } from './commands/view.js';
|
|
63
64
|
import { registerCommandsCommands } from './commands/commands.js';
|
|
64
65
|
import { registerHooksCommands } from './commands/hooks.js';
|
|
@@ -89,6 +90,7 @@ import { registerBrowserCommand } from './commands/browser.js';
|
|
|
89
90
|
import { registerComputerCommand } from './commands/computer.js';
|
|
90
91
|
import { registerProfilesCommands } from './commands/profiles.js';
|
|
91
92
|
import { registerSecretsCommands } from './commands/secrets.js';
|
|
93
|
+
import { registerHelperCommand } from './commands/helper.js';
|
|
92
94
|
import { registerFactoryCommands } from './commands/factory.js';
|
|
93
95
|
import { registerUsageCommand } from './commands/usage.js';
|
|
94
96
|
import { registerAliasCommand } from './commands/alias.js';
|
|
@@ -509,6 +511,7 @@ async function maybeBootstrapShimIntegration(requestedCommand) {
|
|
|
509
511
|
// Register all commands
|
|
510
512
|
registerViewCommand(program);
|
|
511
513
|
registerStatusCommand(program);
|
|
514
|
+
registerFeedbackCommand(program);
|
|
512
515
|
registerCommandsCommands(program);
|
|
513
516
|
registerHooksCommands(program);
|
|
514
517
|
registerSkillsCommands(program);
|
|
@@ -565,6 +568,7 @@ program
|
|
|
565
568
|
});
|
|
566
569
|
registerProfilesCommands(program);
|
|
567
570
|
registerSecretsCommands(program);
|
|
571
|
+
registerHelperCommand(program);
|
|
568
572
|
registerBetaCommands(program);
|
|
569
573
|
registerSyncCommand(program);
|
|
570
574
|
registerRefreshRulesCommand(program);
|
package/dist/lib/acp/client.js
CHANGED
|
@@ -82,7 +82,12 @@ function buildClient(opts) {
|
|
|
82
82
|
return {};
|
|
83
83
|
},
|
|
84
84
|
async requestPermission(params) {
|
|
85
|
-
|
|
85
|
+
// `skip` (formerly `full`) and `auto` blanket-approve; in `auto` the
|
|
86
|
+
// upstream model has its own classifier so we just say "allow once" and
|
|
87
|
+
// let it decide. `edit` says allow_once. `plan` should never reach here
|
|
88
|
+
// (canWrite gates writes earlier), but if it does we cancel.
|
|
89
|
+
const skipAll = mode === 'skip';
|
|
90
|
+
const optionId = skipAll
|
|
86
91
|
? (params.options.find(o => o.kind === 'allow_always')?.optionId
|
|
87
92
|
?? params.options[0]?.optionId)
|
|
88
93
|
: params.options.find(o => o.kind === 'allow_once')?.optionId;
|
package/dist/lib/agents.d.ts
CHANGED
|
@@ -156,6 +156,10 @@ interface McpConfigEntry {
|
|
|
156
156
|
type?: string;
|
|
157
157
|
url?: string;
|
|
158
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Get user-scoped MCP config path for an agent.
|
|
161
|
+
*/
|
|
162
|
+
export declare function getUserMcpConfigPath(agentId: AgentId): string;
|
|
159
163
|
/**
|
|
160
164
|
* Get MCP config path for a specific HOME directory (used for version-managed agents).
|
|
161
165
|
*/
|
package/dist/lib/agents.js
CHANGED
|
@@ -55,13 +55,25 @@ function saveCliVersionCache() {
|
|
|
55
55
|
/* best-effort cache persist */
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Synchronous PATH search -- no subprocess. Returns first matching binary path.
|
|
60
|
+
*
|
|
61
|
+
* Skips our own shims dir (`~/.agents/.cache/shims/`) — those shims are
|
|
62
|
+
* dispatch helpers, not real installs. Counting them as installed produced a
|
|
63
|
+
* false positive where agents with NO real binary on the host (e.g. a
|
|
64
|
+
* never-installed Cursor whose only PATH entry was our `cursor-agent` shim
|
|
65
|
+
* dispatcher) showed up under `agents view`'s "Not Managed by Agents CLI"
|
|
66
|
+
* section, even though the user had nothing to import.
|
|
67
|
+
*/
|
|
59
68
|
function findInPath(command) {
|
|
60
69
|
const pathEnv = process.env.PATH || '';
|
|
61
70
|
const pathExt = process.platform === 'win32' ? (process.env.PATHEXT || '').split(';') : [''];
|
|
71
|
+
const shimsDir = getShimsDir();
|
|
62
72
|
for (const dir of pathEnv.split(path.delimiter)) {
|
|
63
73
|
if (!dir)
|
|
64
74
|
continue;
|
|
75
|
+
if (path.resolve(dir) === path.resolve(shimsDir))
|
|
76
|
+
continue;
|
|
65
77
|
for (const ext of pathExt) {
|
|
66
78
|
const full = path.join(dir, command + ext);
|
|
67
79
|
try {
|
|
@@ -191,7 +203,7 @@ export const AGENTS = {
|
|
|
191
203
|
format: 'markdown',
|
|
192
204
|
variableSyntax: '$ARGUMENTS',
|
|
193
205
|
supportsHooks: true,
|
|
194
|
-
capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, rulesImports: true },
|
|
206
|
+
capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true },
|
|
195
207
|
},
|
|
196
208
|
// codex hooks: gated to >= 0.116.0 (introduced [features] codex_hooks flag).
|
|
197
209
|
codex: {
|
|
@@ -210,7 +222,7 @@ export const AGENTS = {
|
|
|
210
222
|
format: 'markdown',
|
|
211
223
|
variableSyntax: '$ARGUMENTS',
|
|
212
224
|
supportsHooks: true,
|
|
213
|
-
capabilities: { hooks: { since: '0.116.0' }, mcp: true, allowlist: false, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' } },
|
|
225
|
+
capabilities: { hooks: { since: '0.116.0' }, mcp: true, allowlist: false, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, modes: ['plan', 'edit', 'skip'] },
|
|
214
226
|
},
|
|
215
227
|
gemini: {
|
|
216
228
|
id: 'gemini',
|
|
@@ -229,7 +241,7 @@ export const AGENTS = {
|
|
|
229
241
|
supportsHooks: true,
|
|
230
242
|
nativeAgentsSkillsDir: true,
|
|
231
243
|
// gemini hooks: shipped in v0.26.0 (Jan 2026); older binaries silently ignore the `hooks` key.
|
|
232
|
-
capabilities: { hooks: { since: '0.26.0' }, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, rulesImports: true },
|
|
244
|
+
capabilities: { hooks: { since: '0.26.0' }, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit', 'skip'], rulesImports: true },
|
|
233
245
|
},
|
|
234
246
|
cursor: {
|
|
235
247
|
id: 'cursor',
|
|
@@ -247,7 +259,7 @@ export const AGENTS = {
|
|
|
247
259
|
format: 'markdown',
|
|
248
260
|
variableSyntax: '$ARGUMENTS',
|
|
249
261
|
supportsHooks: false,
|
|
250
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false },
|
|
262
|
+
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['edit', 'skip'] },
|
|
251
263
|
},
|
|
252
264
|
opencode: {
|
|
253
265
|
id: 'opencode',
|
|
@@ -264,7 +276,7 @@ export const AGENTS = {
|
|
|
264
276
|
format: 'markdown',
|
|
265
277
|
variableSyntax: '$ARGUMENTS',
|
|
266
278
|
supportsHooks: false,
|
|
267
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false },
|
|
279
|
+
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit'] },
|
|
268
280
|
},
|
|
269
281
|
openclaw: {
|
|
270
282
|
id: 'openclaw',
|
|
@@ -281,7 +293,7 @@ export const AGENTS = {
|
|
|
281
293
|
format: 'markdown',
|
|
282
294
|
variableSyntax: '{{ARGUMENTS}}',
|
|
283
295
|
supportsHooks: true,
|
|
284
|
-
capabilities: { hooks: true, mcp: true, allowlist: false, skills: true, commands: false, plugins: true },
|
|
296
|
+
capabilities: { hooks: true, mcp: true, allowlist: false, skills: true, commands: false, plugins: true, modes: ['plan', 'edit', 'skip'] },
|
|
285
297
|
},
|
|
286
298
|
copilot: {
|
|
287
299
|
id: 'copilot',
|
|
@@ -298,7 +310,7 @@ export const AGENTS = {
|
|
|
298
310
|
format: 'markdown',
|
|
299
311
|
variableSyntax: '$ARGUMENTS',
|
|
300
312
|
supportsHooks: false,
|
|
301
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false },
|
|
313
|
+
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit', 'auto', 'skip'] },
|
|
302
314
|
},
|
|
303
315
|
amp: {
|
|
304
316
|
id: 'amp',
|
|
@@ -315,7 +327,7 @@ export const AGENTS = {
|
|
|
315
327
|
format: 'markdown',
|
|
316
328
|
variableSyntax: '$ARGUMENTS',
|
|
317
329
|
supportsHooks: false,
|
|
318
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false },
|
|
330
|
+
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit'] },
|
|
319
331
|
},
|
|
320
332
|
kiro: {
|
|
321
333
|
id: 'kiro',
|
|
@@ -333,7 +345,7 @@ export const AGENTS = {
|
|
|
333
345
|
format: 'markdown',
|
|
334
346
|
variableSyntax: '$ARGUMENTS',
|
|
335
347
|
supportsHooks: false,
|
|
336
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false },
|
|
348
|
+
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['edit'] },
|
|
337
349
|
},
|
|
338
350
|
goose: {
|
|
339
351
|
id: 'goose',
|
|
@@ -351,7 +363,7 @@ export const AGENTS = {
|
|
|
351
363
|
format: 'markdown',
|
|
352
364
|
variableSyntax: '$ARGUMENTS',
|
|
353
365
|
supportsHooks: false,
|
|
354
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: false, commands: false, plugins: false },
|
|
366
|
+
capabilities: { hooks: false, mcp: true, allowlist: false, skills: false, commands: false, plugins: false, modes: ['edit'] },
|
|
355
367
|
},
|
|
356
368
|
roo: {
|
|
357
369
|
id: 'roo',
|
|
@@ -369,7 +381,7 @@ export const AGENTS = {
|
|
|
369
381
|
format: 'markdown',
|
|
370
382
|
variableSyntax: '$ARGUMENTS',
|
|
371
383
|
supportsHooks: false,
|
|
372
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false },
|
|
384
|
+
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit'] },
|
|
373
385
|
},
|
|
374
386
|
// Google Antigravity CLI (`agy`) — official replacement for Gemini CLI as of IO 2026.
|
|
375
387
|
// configDir nests inside `~/.gemini/` since agy shares the parent dir with the Gemini
|
|
@@ -396,7 +408,7 @@ export const AGENTS = {
|
|
|
396
408
|
format: 'markdown',
|
|
397
409
|
variableSyntax: '{{args}}',
|
|
398
410
|
supportsHooks: true,
|
|
399
|
-
capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, rulesImports: false },
|
|
411
|
+
capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, modes: ['edit', 'skip'], rulesImports: false },
|
|
400
412
|
},
|
|
401
413
|
// xAI Grok Build CLI (`grok`) — early beta, SuperGrok Heavy. Auth via OAuth on
|
|
402
414
|
// first launch, or XAI_API_KEY env var for headless. MCP servers configured inline
|
|
@@ -410,7 +422,7 @@ export const AGENTS = {
|
|
|
410
422
|
color: 'cyanBright',
|
|
411
423
|
cliCommand: 'grok',
|
|
412
424
|
npmPackage: '',
|
|
413
|
-
installScript: 'curl -fsSL https://x.ai/cli/install.sh | bash
|
|
425
|
+
installScript: 'curl -fsSL https://x.ai/cli/install.sh | bash',
|
|
414
426
|
configDir: path.join(HOME, '.grok'),
|
|
415
427
|
commandsDir: '', // Grok primarily uses skills + slash commands from skills
|
|
416
428
|
commandsSubdir: '',
|
|
@@ -427,6 +439,7 @@ export const AGENTS = {
|
|
|
427
439
|
skills: true,
|
|
428
440
|
commands: false, // covered by skills
|
|
429
441
|
plugins: true,
|
|
442
|
+
modes: ['plan', 'edit', 'skip'],
|
|
430
443
|
rulesImports: true,
|
|
431
444
|
},
|
|
432
445
|
},
|
|
@@ -506,8 +519,16 @@ async function getCachedVersionForBinary(agentId, binaryPath) {
|
|
|
506
519
|
/* version command failed */
|
|
507
520
|
version = null;
|
|
508
521
|
}
|
|
509
|
-
|
|
510
|
-
|
|
522
|
+
// Skip persisting null results — the most common cause is a transient
|
|
523
|
+
// `--version` failure (slow startup, stdout race, etc.). A sticky-null
|
|
524
|
+
// entry kept users in a broken state where every subsequent
|
|
525
|
+
// `getCachedVersionForBinary` short-circuited to null forever, even
|
|
526
|
+
// after the binary started working. Re-probing on the next call costs
|
|
527
|
+
// one execFile; persisting null costs the whole feature.
|
|
528
|
+
if (version !== null) {
|
|
529
|
+
cache[agentId] = { binaryPath, mtime, version };
|
|
530
|
+
saveCliVersionCache();
|
|
531
|
+
}
|
|
511
532
|
return version;
|
|
512
533
|
}
|
|
513
534
|
/**
|
|
@@ -1197,7 +1218,7 @@ function parseMcpFromOpenCodeConfig(configPath) {
|
|
|
1197
1218
|
/**
|
|
1198
1219
|
* Get user-scoped MCP config path for an agent.
|
|
1199
1220
|
*/
|
|
1200
|
-
function getUserMcpConfigPath(agentId) {
|
|
1221
|
+
export function getUserMcpConfigPath(agentId) {
|
|
1201
1222
|
const agent = AGENTS[agentId];
|
|
1202
1223
|
switch (agentId) {
|
|
1203
1224
|
case 'claude':
|
|
@@ -1215,6 +1236,9 @@ function getUserMcpConfigPath(agentId) {
|
|
|
1215
1236
|
case 'openclaw':
|
|
1216
1237
|
// OpenClaw uses openclaw.json
|
|
1217
1238
|
return path.join(agent.configDir, 'openclaw.json');
|
|
1239
|
+
case 'copilot':
|
|
1240
|
+
// GitHub Copilot CLI uses mcp-config.json (matches versioned + project paths)
|
|
1241
|
+
return path.join(agent.configDir, 'mcp-config.json');
|
|
1218
1242
|
case 'antigravity':
|
|
1219
1243
|
// agy uses mcp_config.json inside its nested config dir (~/.gemini/antigravity-cli/)
|
|
1220
1244
|
return path.join(agent.configDir, 'mcp_config.json');
|
|
@@ -14,6 +14,15 @@ import { tryAutoPull, isGitRepo } from './git.js';
|
|
|
14
14
|
import { getSystemAgentsDir, getUserAgentsDir, getEnabledExtraRepos, getFetchCacheDir, } from './state.js';
|
|
15
15
|
import { lockFilePath, statusFilePath } from './auto-pull.js';
|
|
16
16
|
const LOCK_TTL_MS = 5 * 60 * 1000;
|
|
17
|
+
/**
|
|
18
|
+
* Background auto-pull of ~/.agents-system/ is off by default. When enabled it
|
|
19
|
+
* silently fast-forwards a tracked source tree that the CLI then reads as a
|
|
20
|
+
* source of skills, hooks, install manifests, and commands — anyone with push
|
|
21
|
+
* access to that upstream gets remote code execution on every user the next
|
|
22
|
+
* time they invoke a command that loads a system resource. Operators that
|
|
23
|
+
* really want the convenience can set AGENTS_AUTO_PULL=1.
|
|
24
|
+
*/
|
|
25
|
+
const ENABLE_AUTO_PULL = process.env.AGENTS_AUTO_PULL === '1';
|
|
17
26
|
function ensureFetchDir() {
|
|
18
27
|
const dir = getFetchCacheDir();
|
|
19
28
|
if (!fs.existsSync(dir)) {
|
|
@@ -84,7 +93,15 @@ async function processTarget(target) {
|
|
|
84
93
|
return;
|
|
85
94
|
try {
|
|
86
95
|
if (target.mode === 'pull') {
|
|
87
|
-
|
|
96
|
+
if (!ENABLE_AUTO_PULL) {
|
|
97
|
+
// Demote to a fetch + notify; the user still sees ahead/behind on the
|
|
98
|
+
// next foreground CLI invocation, but the source tree is never mutated
|
|
99
|
+
// by a detached worker.
|
|
100
|
+
await notifyRepo(target);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
await tryAutoPull(target.dir);
|
|
104
|
+
}
|
|
88
105
|
}
|
|
89
106
|
else {
|
|
90
107
|
await notifyRepo(target);
|
|
@@ -139,6 +139,10 @@ isElectron = false) {
|
|
|
139
139
|
'--no-default-browser-check',
|
|
140
140
|
'--disable-features=DefaultBrowserSetting,ChromeWhatsNewUI',
|
|
141
141
|
'--disable-crash-reporter',
|
|
142
|
+
// Suppress navigator.webdriver = true, which Chromium sets whenever a
|
|
143
|
+
// remote-debugging transport is active. That property is the loudest
|
|
144
|
+
// signal Cloudflare Turnstile, hCaptcha, and similar checks read.
|
|
145
|
+
'--disable-blink-features=AutomationControlled',
|
|
142
146
|
...(options.headless ? ['--headless=new'] : []),
|
|
143
147
|
`--window-size=${viewport.width},${viewport.height}`,
|
|
144
148
|
...(viewport.x !== undefined && viewport.y !== undefined
|
|
@@ -180,7 +180,7 @@ async function ensureRemoteBrowser(user, host, browserType, port, customBinary)
|
|
|
180
180
|
const remoteCmd = [
|
|
181
181
|
shellQuote(browserPath),
|
|
182
182
|
`--remote-debugging-port=${port}`,
|
|
183
|
-
shellQuote(
|
|
183
|
+
shellQuote(`--remote-allow-origins=http://127.0.0.1:${port}`),
|
|
184
184
|
'--disable-background-timer-throttling',
|
|
185
185
|
`--user-data-dir=/tmp/agents-browser-${port}`,
|
|
186
186
|
'</dev/null >/dev/null 2>&1 &',
|
|
@@ -65,12 +65,12 @@ export declare function resolveEndpoint(profile: BrowserProfile, endpointName?:
|
|
|
65
65
|
* Returns undefined for endpoint shapes that don't carry a port (e.g. ws:// without one).
|
|
66
66
|
*
|
|
67
67
|
* Ports are scoped by host: a `cdp://127.0.0.1:9222` profile (local Chrome on
|
|
68
|
-
* this machine) and an `ssh://
|
|
69
|
-
* point at different physical ports — the host disambiguates them.
|
|
68
|
+
* this machine) and an `ssh://remote-host:9222` profile (Comet on a remote
|
|
69
|
+
* host) point at different physical ports — the host disambiguates them.
|
|
70
70
|
*
|
|
71
71
|
* Accepts both `scheme://host:port` and `scheme://host?port=N` shapes (the
|
|
72
72
|
* latter is the documented form in `types.ts` for `ssh://`). Without this,
|
|
73
|
-
* `ssh://
|
|
73
|
+
* `ssh://remote-host?port=18805` would silently fall back to 9222 and every
|
|
74
74
|
* `?port=`-style SSH profile would collide on creation.
|
|
75
75
|
*/
|
|
76
76
|
export declare function extractConfiguredEndpoint(profile: BrowserProfile): {
|
|
@@ -299,12 +299,12 @@ export function resolveEndpoint(profile, endpointName) {
|
|
|
299
299
|
* Returns undefined for endpoint shapes that don't carry a port (e.g. ws:// without one).
|
|
300
300
|
*
|
|
301
301
|
* Ports are scoped by host: a `cdp://127.0.0.1:9222` profile (local Chrome on
|
|
302
|
-
* this machine) and an `ssh://
|
|
303
|
-
* point at different physical ports — the host disambiguates them.
|
|
302
|
+
* this machine) and an `ssh://remote-host:9222` profile (Comet on a remote
|
|
303
|
+
* host) point at different physical ports — the host disambiguates them.
|
|
304
304
|
*
|
|
305
305
|
* Accepts both `scheme://host:port` and `scheme://host?port=N` shapes (the
|
|
306
306
|
* latter is the documented form in `types.ts` for `ssh://`). Without this,
|
|
307
|
-
* `ssh://
|
|
307
|
+
* `ssh://remote-host?port=18805` would silently fall back to 9222 and every
|
|
308
308
|
* `?port=`-style SSH profile would collide on creation.
|
|
309
309
|
*/
|
|
310
310
|
export function extractConfiguredEndpoint(profile) {
|
|
@@ -1701,6 +1701,25 @@ export class BrowserService {
|
|
|
1701
1701
|
targetId: tabId,
|
|
1702
1702
|
flatten: true,
|
|
1703
1703
|
}));
|
|
1704
|
+
// Inject a one-shot stealth shim before any page script runs. Chromium
|
|
1705
|
+
// unconditionally exposes navigator.webdriver = true when a remote-debug
|
|
1706
|
+
// transport is attached; Cloudflare Turnstile, hCaptcha, and similar bot
|
|
1707
|
+
// checks read that property first. For browsers agents-cli spawns the
|
|
1708
|
+
// --disable-blink-features=AutomationControlled launch flag already
|
|
1709
|
+
// covers this, but for attach-to-running profiles (the Comet / Arc /
|
|
1710
|
+
// Brave case where the user launched the browser themselves) the flag
|
|
1711
|
+
// is unavailable — Page.addScriptToEvaluateOnNewDocument is the only
|
|
1712
|
+
// lever. Non-page targets (workers, service workers) will reject these
|
|
1713
|
+
// calls; we swallow the error and keep going.
|
|
1714
|
+
try {
|
|
1715
|
+
await conn.cdp.send('Page.enable', {}, sessionId);
|
|
1716
|
+
await conn.cdp.send('Page.addScriptToEvaluateOnNewDocument', {
|
|
1717
|
+
source: "Object.defineProperty(navigator,'webdriver',{get:()=>undefined});",
|
|
1718
|
+
}, sessionId);
|
|
1719
|
+
}
|
|
1720
|
+
catch {
|
|
1721
|
+
// Target doesn't support Page domain — nothing to inject.
|
|
1722
|
+
}
|
|
1704
1723
|
conn.sessionCache.set(tabId, sessionId);
|
|
1705
1724
|
return sessionId;
|
|
1706
1725
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export type BrowserType = 'chrome' | 'comet' | 'chromium' | 'brave' | 'edge' | 'custom';
|
|
2
2
|
/**
|
|
3
3
|
* A single named endpoint preset within a profile. Lets one profile cover
|
|
4
|
-
* the local + remote variants of the same app (e.g.
|
|
5
|
-
*
|
|
4
|
+
* the local + remote variants of the same app (e.g. an Electron app on this
|
|
5
|
+
* Mac vs. on a remote host) instead of forcing two parallel profiles.
|
|
6
6
|
*
|
|
7
7
|
* Per-endpoint overrides take precedence over profile-level fields.
|
|
8
8
|
*/
|
|
9
9
|
export interface EndpointPreset {
|
|
10
10
|
/** CDP URL — `cdp://host:port` or `ssh://host?port=N` */
|
|
11
11
|
target: string;
|
|
12
|
-
/** Override the profile-level binary (e.g.
|
|
12
|
+
/** Override the profile-level binary (e.g. a remote host has no local binary). */
|
|
13
13
|
binary?: string;
|
|
14
14
|
/** Override the profile-level targetFilter (Electron app builds may diverge). */
|
|
15
15
|
targetFilter?: string;
|
|
@@ -43,7 +43,7 @@ export interface BrowserProfile {
|
|
|
43
43
|
};
|
|
44
44
|
/** Directory holding source-side JSONL logs (e.g. ~/.rush/logs). */
|
|
45
45
|
logDir?: string;
|
|
46
|
-
/** Optional SSH host where logDir lives, e.g. "user@
|
|
46
|
+
/** Optional SSH host where logDir lives, e.g. "user@remote-host". */
|
|
47
47
|
logHost?: string;
|
|
48
48
|
}
|
|
49
49
|
/** Parsed form of `BrowserProfile.targetFilter`. */
|
|
@@ -16,6 +16,21 @@ export interface BinarySpec {
|
|
|
16
16
|
extract?: string;
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* How to verify a CLI is installed. Structured so we can dispatch to spawnSync
|
|
21
|
+
* with an argv array — never through a shell.
|
|
22
|
+
*
|
|
23
|
+
* `which` — just check PATH for `cmd`.
|
|
24
|
+
* `version` — spawn `cmd` with `args` and require exit 0.
|
|
25
|
+
*/
|
|
26
|
+
export type CheckSpec = {
|
|
27
|
+
kind: 'which';
|
|
28
|
+
cmd: string;
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'version';
|
|
31
|
+
cmd: string;
|
|
32
|
+
args: string[];
|
|
33
|
+
};
|
|
19
34
|
/** Parsed CLI manifest. */
|
|
20
35
|
export interface CliManifest {
|
|
21
36
|
/** Name as it appears on the command line (e.g. "higgsfield"). */
|
|
@@ -24,8 +39,8 @@ export interface CliManifest {
|
|
|
24
39
|
description?: string;
|
|
25
40
|
/** Project homepage; used in detail view + post-install messaging. */
|
|
26
41
|
homepage?: string;
|
|
27
|
-
/**
|
|
28
|
-
check:
|
|
42
|
+
/** Structured check spec; never a raw shell command. */
|
|
43
|
+
check: CheckSpec;
|
|
29
44
|
/** Install methods tried in order; first one whose tool is available is used. */
|
|
30
45
|
install: InstallMethod[];
|
|
31
46
|
/** Message printed after successful install — typically auth instructions. */
|
|
@@ -42,6 +57,13 @@ export interface CliManifestError {
|
|
|
42
57
|
/** Human-readable reason. */
|
|
43
58
|
reason: string;
|
|
44
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Parse a `check:` field into a CheckSpec. Accepts either a structured object
|
|
62
|
+
* (`{ kind: 'which'|'version', cmd, args? }`) or a legacy whitespace-separated
|
|
63
|
+
* string. String form is split on whitespace and each token is validated against
|
|
64
|
+
* SAFE_CHECK_TOKEN — manifests cannot smuggle in shell metacharacters.
|
|
65
|
+
*/
|
|
66
|
+
export declare function parseCheckSpec(raw: unknown, defaultName: string): CheckSpec;
|
|
45
67
|
/**
|
|
46
68
|
* Parse a single CLI manifest from its YAML contents.
|
|
47
69
|
* Returns a manifest on success; throws on schema violations so callers can
|
|
@@ -63,13 +85,18 @@ export declare function listCliManifests(cwd?: string): {
|
|
|
63
85
|
/** Resolve a single CLI manifest by name. Returns null when not declared. */
|
|
64
86
|
export declare function resolveCliManifest(name: string, cwd?: string): CliManifest | null;
|
|
65
87
|
export declare function hasCommand(cmd: string): boolean;
|
|
66
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Run the manifest's check. Dispatches on CheckSpec.kind — never invokes a
|
|
90
|
+
* shell, never interpolates strings into a command line.
|
|
91
|
+
*/
|
|
67
92
|
export declare function isCliInstalled(manifest: CliManifest): boolean;
|
|
68
93
|
/**
|
|
69
94
|
* Pick the first install method whose required host tool is available.
|
|
70
95
|
* Returns null when none of the declared methods can run on this host.
|
|
71
96
|
*/
|
|
72
97
|
export declare function selectInstallMethod(manifest: CliManifest): InstallMethod | null;
|
|
98
|
+
/** Render a CheckSpec back to a human-readable command string (display only). */
|
|
99
|
+
export declare function describeCheck(check: CheckSpec): string;
|
|
73
100
|
/** Short description of a method for display. */
|
|
74
101
|
export declare function describeMethod(method: InstallMethod): string;
|
|
75
102
|
export interface InstallResult {
|
|
@@ -83,6 +110,12 @@ export interface InstallResult {
|
|
|
83
110
|
/** Set when the install runner threw or exited non-zero. */
|
|
84
111
|
error?: string;
|
|
85
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Display-only rendering of how a method would be run, for `--dry-run` and
|
|
115
|
+
* status output. Not used by installCli — execution goes through runInstallMethod
|
|
116
|
+
* which dispatches to spawnSync with argv arrays.
|
|
117
|
+
*/
|
|
118
|
+
export declare function buildInstallCommand(method: InstallMethod): string;
|
|
86
119
|
/**
|
|
87
120
|
* Install a single CLI by running its first compatible method. Streams the
|
|
88
121
|
* underlying command's output to the parent terminal so users see brew/npm
|
|
@@ -91,11 +124,6 @@ export interface InstallResult {
|
|
|
91
124
|
export declare function installCli(manifest: CliManifest, opts?: {
|
|
92
125
|
dryRun?: boolean;
|
|
93
126
|
}): InstallResult;
|
|
94
|
-
/**
|
|
95
|
-
* Map a declarative method to a shell command. Centralized so tests and dry-run
|
|
96
|
-
* surface the exact string that would execute.
|
|
97
|
-
*/
|
|
98
|
-
export declare function buildInstallCommand(method: InstallMethod): string;
|
|
99
127
|
export interface CliStatus {
|
|
100
128
|
manifest: CliManifest;
|
|
101
129
|
installed: boolean;
|