@phnx-labs/agents-cli 1.22.40 → 1.22.41
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 +22 -0
- package/README.md +5 -0
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.js +0 -1
- package/dist/cli/command-registry.js +1 -2
- package/dist/commands/browser.d.ts +15 -0
- package/dist/commands/browser.js +115 -40
- package/dist/commands/feed.js +3 -11
- package/dist/commands/secrets.js +87 -97
- package/dist/commands/sessions-picker.d.ts +1 -0
- package/dist/commands/sessions-picker.js +27 -5
- package/dist/commands/sessions-share.d.ts +25 -0
- package/dist/commands/sessions-share.js +166 -0
- package/dist/commands/sessions.js +2 -0
- package/dist/commands/setup-browser.js +1 -1
- package/dist/commands/setup-preferences.js +2 -2
- package/dist/commands/webhook.js +14 -9
- package/dist/lib/browser/cdp.js +4 -0
- package/dist/lib/browser/chrome.js +12 -1
- package/dist/lib/browser/drivers/ssh.js +1 -0
- package/dist/lib/browser/profiles.d.ts +3 -3
- package/dist/lib/browser/profiles.js +7 -7
- package/dist/lib/browser/service.d.ts +24 -1
- package/dist/lib/browser/service.js +38 -14
- package/dist/lib/browser/types.d.ts +1 -1
- package/dist/lib/daemon-webhooks.d.ts +3 -1
- package/dist/lib/daemon-webhooks.js +12 -7
- package/dist/lib/device-config.js +1 -1
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/observe-aliases.d.ts +2 -2
- package/dist/lib/observe-aliases.js +2 -11
- package/dist/lib/project-key.js +7 -0
- package/dist/lib/runner.js +21 -6
- 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/bundles.d.ts +1 -1
- package/dist/lib/secrets/bundles.js +1 -1
- package/dist/lib/secrets/headless.d.ts +16 -0
- package/dist/lib/secrets/headless.js +21 -0
- package/dist/lib/secrets/remote.d.ts +9 -7
- package/dist/lib/secrets/remote.js +18 -9
- package/dist/lib/session/share-html.d.ts +55 -0
- package/dist/lib/session/share-html.js +319 -0
- package/dist/lib/settings-manifest.d.ts +2 -0
- package/dist/lib/settings-manifest.js +81 -3
- package/dist/lib/share/publish.d.ts +38 -0
- package/dist/lib/share/publish.js +81 -8
- package/dist/lib/startup/command-registry.d.ts +2 -1
- package/dist/lib/startup/command-registry.js +4 -2
- package/dist/lib/triggers/handlers.d.ts +37 -2
- package/dist/lib/triggers/handlers.js +56 -5
- package/dist/lib/triggers/webhook.d.ts +80 -6
- package/dist/lib/triggers/webhook.js +127 -3
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/wrap.d.ts +33 -0
- package/dist/lib/wrap.js +70 -0
- package/package.json +1 -1
package/dist/commands/webhook.js
CHANGED
|
@@ -25,8 +25,10 @@ function readWebhookSecrets(bundleName) {
|
|
|
25
25
|
secrets.github = env.GITHUB_WEBHOOK_SECRET;
|
|
26
26
|
if (env.LINEAR_WEBHOOK_SECRET)
|
|
27
27
|
secrets.linear = env.LINEAR_WEBHOOK_SECRET;
|
|
28
|
-
if (
|
|
29
|
-
|
|
28
|
+
if (env.SLACK_SIGNING_SECRET)
|
|
29
|
+
secrets.slack = env.SLACK_SIGNING_SECRET;
|
|
30
|
+
if (!secrets.github && !secrets.linear && !secrets.slack) {
|
|
31
|
+
throw new Error(`Bundle '${bundleName}' must contain GITHUB_WEBHOOK_SECRET, LINEAR_WEBHOOK_SECRET, or SLACK_SIGNING_SECRET.`);
|
|
30
32
|
}
|
|
31
33
|
return secrets;
|
|
32
34
|
}
|
|
@@ -36,8 +38,8 @@ export function registerWebhooksCommand(program) {
|
|
|
36
38
|
.description('Run a localhost signed webhook receiver for routine triggers.');
|
|
37
39
|
webhooks
|
|
38
40
|
.command('serve')
|
|
39
|
-
.description('Receive signed GitHub/Linear webhooks on /hooks/<source> and fire matching routines.')
|
|
40
|
-
.requiredOption('--secrets-bundle <name>', 'agents secrets bundle containing GITHUB_WEBHOOK_SECRET and/or
|
|
41
|
+
.description('Receive signed GitHub/Linear/Slack webhooks on /hooks/<source> and fire matching routines and handlers.')
|
|
42
|
+
.requiredOption('--secrets-bundle <name>', 'agents secrets bundle containing GITHUB_WEBHOOK_SECRET, LINEAR_WEBHOOK_SECRET, and/or SLACK_SIGNING_SECRET')
|
|
41
43
|
.option('--bind <addr>', `Bind address (default ${DEFAULT_HOST})`, DEFAULT_HOST)
|
|
42
44
|
.option('-p, --port <n>', `Local port (default ${DEFAULT_PORT})`, String(DEFAULT_PORT))
|
|
43
45
|
.option('--rate-limit <n>', 'Accepted deliveries per source per minute', '60')
|
|
@@ -61,12 +63,15 @@ export function registerWebhooksCommand(program) {
|
|
|
61
63
|
// Durable delivery dedup: replays survive a receiver restart (an
|
|
62
64
|
// in-memory store would forget every seen delivery on restart).
|
|
63
65
|
deliveryStore: createFileDeliveryStore(path.join(getRuntimeStateDir(), 'webhook', 'deliveries.json')),
|
|
64
|
-
|
|
66
|
+
// Logged at MATCH time, before dispatch — a `run.command` handler can
|
|
67
|
+
// block on a shelled-out agent run for minutes, and that must never
|
|
68
|
+
// delay the log that says a delivery fired (RUSH-2722).
|
|
69
|
+
onMatch: (webhook, matchedJobNames, matchedHandlerNames) => {
|
|
65
70
|
const parts = [];
|
|
66
|
-
if (
|
|
67
|
-
parts.push(`routines ${
|
|
68
|
-
if (
|
|
69
|
-
parts.push(`handlers ${
|
|
71
|
+
if (matchedJobNames.length)
|
|
72
|
+
parts.push(`routines ${matchedJobNames.join(', ')}`);
|
|
73
|
+
if (matchedHandlerNames.length)
|
|
74
|
+
parts.push(`handlers ${matchedHandlerNames.join(', ')}`);
|
|
70
75
|
console.log(`${new Date().toISOString()} ${webhook.source}:${webhook.event} ` +
|
|
71
76
|
(parts.length ? `fired ${parts.join('; ')}` : 'no match'));
|
|
72
77
|
},
|
package/dist/lib/browser/cdp.js
CHANGED
|
@@ -227,6 +227,10 @@ export function verifyBrowserIdentity(reported, expected, port, host = 'localhos
|
|
|
227
227
|
// doesn't override the Chromium branding. Accept chrome here so attaching
|
|
228
228
|
// to a Comet instance doesn't trip a false "identity mismatch".
|
|
229
229
|
comet: ['comet', 'chrome'],
|
|
230
|
+
// Arc reports itself as plain "Chrome/<version>" in /json/version (verified
|
|
231
|
+
// live: Arc 1.15x → "Chrome/151"), so accept chrome to avoid a false
|
|
232
|
+
// identity mismatch when attaching to a real Arc.
|
|
233
|
+
arc: ['arc', 'chrome'],
|
|
230
234
|
brave: ['brave', 'brave-browser', 'chrome'],
|
|
231
235
|
// Windows/Chromium Edge reports its /json/version "Browser" field as
|
|
232
236
|
// "Edg/<version>" (the `Edg` token, not `Edge`), which normalizeBrowserName
|
|
@@ -23,6 +23,7 @@ const BROWSER_PATHS = {
|
|
|
23
23
|
chromium: ['/Applications/Chromium.app/Contents/MacOS/Chromium'],
|
|
24
24
|
brave: ['/Applications/Brave Browser.app/Contents/MacOS/Brave Browser'],
|
|
25
25
|
edge: ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'],
|
|
26
|
+
arc: ['/Applications/Arc.app/Contents/MacOS/Arc'],
|
|
26
27
|
custom: [],
|
|
27
28
|
},
|
|
28
29
|
linux: {
|
|
@@ -31,6 +32,8 @@ const BROWSER_PATHS = {
|
|
|
31
32
|
chromium: ['/usr/bin/chromium', '/usr/bin/chromium-browser', '/snap/bin/chromium'],
|
|
32
33
|
brave: ['/usr/bin/brave-browser', '/usr/bin/brave'],
|
|
33
34
|
edge: ['/usr/bin/microsoft-edge'],
|
|
35
|
+
// Arc has no Linux build (macOS + Windows only).
|
|
36
|
+
arc: [],
|
|
34
37
|
custom: [],
|
|
35
38
|
},
|
|
36
39
|
win32: {
|
|
@@ -57,6 +60,9 @@ const BROWSER_PATHS = {
|
|
|
57
60
|
`${WIN_PROGRAMFILES_X86}\\Microsoft\\Edge\\Application\\msedge.exe`,
|
|
58
61
|
`${WIN_LOCALAPPDATA}\\Microsoft\\Edge\\Application\\msedge.exe`,
|
|
59
62
|
],
|
|
63
|
+
// Arc ships a Windows build, but its install path is not yet verified here;
|
|
64
|
+
// leave unlisted (undetected) rather than guess a path that false-positives.
|
|
65
|
+
arc: [],
|
|
60
66
|
custom: [],
|
|
61
67
|
},
|
|
62
68
|
};
|
|
@@ -162,6 +168,9 @@ export function findBrowserPath(browserType, customBinary) {
|
|
|
162
168
|
if (browserType === 'comet' && platform === 'linux') {
|
|
163
169
|
throw new Error('Browser "comet" is not available on Linux (Comet ships macOS and Windows builds only). Use chrome, chromium, brave, or edge on this platform.');
|
|
164
170
|
}
|
|
171
|
+
if (browserType === 'arc' && platform === 'linux') {
|
|
172
|
+
throw new Error('Browser "arc" is not available on Linux (Arc ships macOS and Windows builds only). Use chrome, chromium, brave, or edge on this platform.');
|
|
173
|
+
}
|
|
165
174
|
throw new Error(`Browser "${browserType}" not found. Install it first.`);
|
|
166
175
|
}
|
|
167
176
|
// Per-platform Chromium-family priority list for "no --profile" auto-pick.
|
|
@@ -170,7 +179,9 @@ export function findBrowserPath(browserType, customBinary) {
|
|
|
170
179
|
// way cdp.ts expects, so they'd need separate drivers.
|
|
171
180
|
const DEFAULT_BROWSER_PRIORITY = {
|
|
172
181
|
// macOS: Chrome leads (>70% of dev machines), then the rest of the family.
|
|
173
|
-
|
|
182
|
+
// Arc is last: it's in maintenance mode and needs a blank tab to drive, so it
|
|
183
|
+
// shouldn't win auto-pick over a mainstream browser.
|
|
184
|
+
darwin: ['chrome', 'brave', 'edge', 'chromium', 'comet', 'arc'],
|
|
174
185
|
// Linux: Chrome/Chromium first (apt/snap), then Brave/Edge if present.
|
|
175
186
|
linux: ['chrome', 'chromium', 'brave', 'edge'],
|
|
176
187
|
// Windows: Edge is preinstalled on every supported build, so it's the
|
|
@@ -142,6 +142,7 @@ const POSIX_BROWSER_PATHS = {
|
|
|
142
142
|
chromium: '/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
143
143
|
brave: '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
|
|
144
144
|
edge: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
|
|
145
|
+
arc: '/Applications/Arc.app/Contents/MacOS/Arc',
|
|
145
146
|
};
|
|
146
147
|
// Windows App Paths registry keys per browser. CreateProcess (used by WMI
|
|
147
148
|
// Win32_Process.Create) does not honor App Paths the way ShellExecute/`start`
|
|
@@ -14,7 +14,7 @@ export interface ScopedBrowserProfile {
|
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* The device-local configured default profile name (set via
|
|
17
|
-
* `agents browser
|
|
17
|
+
* `agents browser use`), or undefined when unset. When set, it
|
|
18
18
|
* is the profile `agents browser start` resolves to for BOTH the no-`--profile`
|
|
19
19
|
* path and an explicit `--profile default`. Stored as this machine's
|
|
20
20
|
* `browser.profile` device-config key (the per-device doc's `config:` block —
|
|
@@ -62,7 +62,7 @@ export declare function isProfileLaunchableHere(profile: BrowserProfile): boolea
|
|
|
62
62
|
* set-default <name>`) when it names an existing profile that can launch here;
|
|
63
63
|
* (2) an existing `default` profile that can launch here; (3) auto-pick the
|
|
64
64
|
* first installed Chromium-family browser per the platform priority list in
|
|
65
|
-
* chrome.ts (macOS: chrome > brave > edge > chromium > comet; Linux: chrome >
|
|
65
|
+
* chrome.ts (macOS: chrome > brave > edge > chromium > comet > arc; Linux: chrome >
|
|
66
66
|
* chromium > brave > edge; Windows: edge > chrome > brave > comet) and pin a
|
|
67
67
|
* `default` profile to it. Throws an actionable error if none of those binaries
|
|
68
68
|
* are installed.
|
|
@@ -133,7 +133,7 @@ export declare function padColumn(text: string, width: number): string;
|
|
|
133
133
|
* Two things used to be called "default" in this listing with no way to tell
|
|
134
134
|
* them apart (RUSH-2710): the profile literally NAMED `default` (the
|
|
135
135
|
* auto-detected one), and whichever profile this machine resolves a bare
|
|
136
|
-
* `agents browser start` to (`agents
|
|
136
|
+
* `agents browser start` to (`agents browser use <name>`). They
|
|
137
137
|
* are frequently different profiles. Now only the second is a marker — a `*` in
|
|
138
138
|
* a leading column, explained by a legend line — so the name column carries just
|
|
139
139
|
* the name and `default` in it always means the profile of that name.
|
|
@@ -25,7 +25,7 @@ function isMachineLocalProfile(name) {
|
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* The device-local configured default profile name (set via
|
|
28
|
-
* `agents browser
|
|
28
|
+
* `agents browser use`), or undefined when unset. When set, it
|
|
29
29
|
* is the profile `agents browser start` resolves to for BOTH the no-`--profile`
|
|
30
30
|
* path and an explicit `--profile default`. Stored as this machine's
|
|
31
31
|
* `browser.profile` device-config key (the per-device doc's `config:` block —
|
|
@@ -165,7 +165,7 @@ export function isProfileLaunchableHere(profile) {
|
|
|
165
165
|
* set-default <name>`) when it names an existing profile that can launch here;
|
|
166
166
|
* (2) an existing `default` profile that can launch here; (3) auto-pick the
|
|
167
167
|
* first installed Chromium-family browser per the platform priority list in
|
|
168
|
-
* chrome.ts (macOS: chrome > brave > edge > chromium > comet; Linux: chrome >
|
|
168
|
+
* chrome.ts (macOS: chrome > brave > edge > chromium > comet > arc; Linux: chrome >
|
|
169
169
|
* chromium > brave > edge; Windows: edge > chrome > brave > comet) and pin a
|
|
170
170
|
* `default` profile to it. Throws an actionable error if none of those binaries
|
|
171
171
|
* are installed.
|
|
@@ -187,18 +187,18 @@ export async function ensureDefaultBrowserProfile() {
|
|
|
187
187
|
console.warn(chosen
|
|
188
188
|
? `warning: configured default browser profile "${configured}" can't launch on this ` +
|
|
189
189
|
`machine (its browser/binary isn't installed here); falling back to auto-detect. ` +
|
|
190
|
-
`Fix with: agents browser
|
|
190
|
+
`Fix with: agents browser use <name> (or --unset)`
|
|
191
191
|
: `warning: configured default browser profile "${configured}" no longer exists; ` +
|
|
192
|
-
`falling back to auto-detect. Fix with: agents browser
|
|
192
|
+
`falling back to auto-detect. Fix with: agents browser use <name> (or --unset)`);
|
|
193
193
|
}
|
|
194
194
|
const existing = await getProfile(DEFAULT_BROWSER_PROFILE_NAME);
|
|
195
195
|
if (existing && isProfileLaunchableHere(existing))
|
|
196
196
|
return existing;
|
|
197
197
|
const detected = findFirstInstalledBrowser();
|
|
198
198
|
if (!detected) {
|
|
199
|
-
throw new Error('No supported browser found. Install one of: Chrome, Brave, Edge, Chromium, or
|
|
199
|
+
throw new Error('No supported browser found. Install one of: Chrome, Brave, Edge, Chromium, Comet, or Arc, ' +
|
|
200
200
|
'then re-run `agents browser start`. Or create a profile explicitly with ' +
|
|
201
|
-
'`agents browser profiles create <name> --browser <chrome|comet|chromium|brave|edge|custom>`. ' +
|
|
201
|
+
'`agents browser profiles create <name> --browser <chrome|comet|chromium|brave|edge|arc|custom>`. ' +
|
|
202
202
|
'Note: Safari and Firefox are not supported — agents browser drives over the ' +
|
|
203
203
|
'Chrome DevTools Protocol, which they don\'t implement.');
|
|
204
204
|
}
|
|
@@ -431,7 +431,7 @@ export function padColumn(text, width) {
|
|
|
431
431
|
* Two things used to be called "default" in this listing with no way to tell
|
|
432
432
|
* them apart (RUSH-2710): the profile literally NAMED `default` (the
|
|
433
433
|
* auto-detected one), and whichever profile this machine resolves a bare
|
|
434
|
-
* `agents browser start` to (`agents
|
|
434
|
+
* `agents browser start` to (`agents browser use <name>`). They
|
|
435
435
|
* are frequently different profiles. Now only the second is a marker — a `*` in
|
|
436
436
|
* a leading column, explained by a legend line — so the name column carries just
|
|
437
437
|
* the name and `default` in it always means the profile of that name.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CDPClient } from './cdp.js';
|
|
2
2
|
import { type ResolvedDomainSkill } from './domain-skills.js';
|
|
3
|
-
import { type Task, type TabInfo, type ProfileStatus, type HistoricalTask, type ReapResult } from './types.js';
|
|
3
|
+
import { type Task, type TabInfo, type ProfileStatus, type BrowserType, type HistoricalTask, type ReapResult } from './types.js';
|
|
4
4
|
import { type ReapOptions } from './hygiene.js';
|
|
5
5
|
import { type RefOpts, type RefNode } from './refs.js';
|
|
6
6
|
import type { TargetFilter } from './types.js';
|
|
@@ -50,6 +50,13 @@ interface ProfileConnection {
|
|
|
50
50
|
port: number;
|
|
51
51
|
pid: number;
|
|
52
52
|
electron?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The profile's declared browser family. Load-bearing for Arc: Arc answers
|
|
55
|
+
* `Browser.getVersion` but exposes zero CDP page targets and CRASHES on
|
|
56
|
+
* `Target.createTarget` (verified live, PR #2778), so any tab-creating path
|
|
57
|
+
* must refuse rather than crash the user's Arc. See `createPageTarget`.
|
|
58
|
+
*/
|
|
59
|
+
browserType?: BrowserType;
|
|
53
60
|
/** Raw `url:<v>` / `title:<v>` filter copied from the profile config. */
|
|
54
61
|
targetFilter?: string;
|
|
55
62
|
/**
|
|
@@ -83,6 +90,14 @@ interface ProfileConnection {
|
|
|
83
90
|
}
|
|
84
91
|
/** Join error lines so callers get a next command, not a dead-end message. */
|
|
85
92
|
export declare function actionable(...lines: string[]): string;
|
|
93
|
+
/**
|
|
94
|
+
* Arc answers `Browser.getVersion` (so a connection succeeds) but exposes zero
|
|
95
|
+
* CDP page targets via every discovery method and CRASHES when a new tab is
|
|
96
|
+
* requested via `Target.createTarget` (verified live, PR #2778). It is therefore
|
|
97
|
+
* not drivable. This is the single clear, actionable error every tab-creating
|
|
98
|
+
* path throws instead of crashing the user's Arc window.
|
|
99
|
+
*/
|
|
100
|
+
export declare function arcNotDrivableError(profileName?: string): Error;
|
|
86
101
|
/** Derive a human label from an explicit title or a navigated URL. */
|
|
87
102
|
export declare function deriveTaskLabel(opts: {
|
|
88
103
|
title?: string;
|
|
@@ -392,6 +407,14 @@ export declare class BrowserService {
|
|
|
392
407
|
private connectProfile;
|
|
393
408
|
private connectEndpoint;
|
|
394
409
|
private enableDomains;
|
|
410
|
+
/**
|
|
411
|
+
* The single path that opens a new CDP page/window target. It exists so the
|
|
412
|
+
* Arc guard lives in exactly one place: `Target.createTarget` crashes Arc
|
|
413
|
+
* (verified, PR #2778), so an Arc connection throws a clear, actionable error
|
|
414
|
+
* here instead of every call site re-checking. Every drivable Chromium-family
|
|
415
|
+
* browser goes straight through.
|
|
416
|
+
*/
|
|
417
|
+
private createPageTarget;
|
|
395
418
|
private getOrCreateWindow;
|
|
396
419
|
private hasTaskNamed;
|
|
397
420
|
/** Map key for a task on a connection (tasks are keyed by `name`). */
|
|
@@ -243,6 +243,17 @@ async function isConnHealthy(conn, timeoutMs = 1000) {
|
|
|
243
243
|
export function actionable(...lines) {
|
|
244
244
|
return lines.filter((l) => l != null && l !== '').join('\n');
|
|
245
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Arc answers `Browser.getVersion` (so a connection succeeds) but exposes zero
|
|
248
|
+
* CDP page targets via every discovery method and CRASHES when a new tab is
|
|
249
|
+
* requested via `Target.createTarget` (verified live, PR #2778). It is therefore
|
|
250
|
+
* not drivable. This is the single clear, actionable error every tab-creating
|
|
251
|
+
* path throws instead of crashing the user's Arc window.
|
|
252
|
+
*/
|
|
253
|
+
export function arcNotDrivableError(profileName) {
|
|
254
|
+
const scope = profileName ? ` (profile "${profileName}")` : '';
|
|
255
|
+
return new Error(actionable(`Browser "arc"${scope} is not drivable: Arc exposes no CDP page targets and`, 'crashes when a new tab is requested, so `agents browser` cannot control it.', 'Use a Chromium-family browser instead — Comet, Chrome, Chromium, or Brave:', ' agents browser profiles create <name> --browser comet'));
|
|
256
|
+
}
|
|
246
257
|
/** Derive a human label from an explicit title or a navigated URL. */
|
|
247
258
|
export function deriveTaskLabel(opts) {
|
|
248
259
|
if (opts.title?.trim())
|
|
@@ -404,9 +415,9 @@ export class BrowserService {
|
|
|
404
415
|
if (!opts.url && !conn.electron) {
|
|
405
416
|
const { targetInfos } = (await conn.cdp.send('Target.getTargets'));
|
|
406
417
|
if (!targetInfos.some((t) => t.type === 'page')) {
|
|
407
|
-
const created =
|
|
418
|
+
const created = await this.createPageTarget(conn, {
|
|
408
419
|
url: 'about:blank',
|
|
409
|
-
})
|
|
420
|
+
});
|
|
410
421
|
startupBlankTargetId = created.targetId;
|
|
411
422
|
this.invalidateTargetCache(conn);
|
|
412
423
|
}
|
|
@@ -485,10 +496,7 @@ export class BrowserService {
|
|
|
485
496
|
let tabId;
|
|
486
497
|
if (opts.url && !conn.electron) {
|
|
487
498
|
const adopted = opts.fresh ? undefined : await this.adoptTabShowing(conn, opts.url);
|
|
488
|
-
const targetId = adopted ??
|
|
489
|
-
(await conn.cdp.send('Target.createTarget', {
|
|
490
|
-
url: opts.url,
|
|
491
|
-
})).targetId;
|
|
499
|
+
const targetId = adopted ?? (await this.createPageTarget(conn, { url: opts.url })).targetId;
|
|
492
500
|
const shortId = generateShortId();
|
|
493
501
|
task.tabs[shortId] = targetId;
|
|
494
502
|
task.currentTabId = shortId;
|
|
@@ -747,9 +755,7 @@ export class BrowserService {
|
|
|
747
755
|
return { tabId: shortId, url, created: true };
|
|
748
756
|
}
|
|
749
757
|
// Chrome: create new tab
|
|
750
|
-
const result =
|
|
751
|
-
url,
|
|
752
|
-
}));
|
|
758
|
+
const result = await this.createPageTarget(conn, { url });
|
|
753
759
|
const shortId = generateShortId();
|
|
754
760
|
task.tabs[shortId] = result.targetId;
|
|
755
761
|
task.currentTabId = shortId;
|
|
@@ -763,9 +769,7 @@ export class BrowserService {
|
|
|
763
769
|
if (conn.electron) {
|
|
764
770
|
throw new Error('Electron apps do not support opening additional tabs');
|
|
765
771
|
}
|
|
766
|
-
const result =
|
|
767
|
-
url,
|
|
768
|
-
}));
|
|
772
|
+
const result = await this.createPageTarget(conn, { url });
|
|
769
773
|
const shortId = generateShortId();
|
|
770
774
|
task.tabs[shortId] = result.targetId;
|
|
771
775
|
task.currentTabId = shortId; // new tab becomes current
|
|
@@ -1929,6 +1933,7 @@ export class BrowserService {
|
|
|
1929
1933
|
port,
|
|
1930
1934
|
pid,
|
|
1931
1935
|
electron: true,
|
|
1936
|
+
browserType: profile.browser,
|
|
1932
1937
|
targetFilter: profile.targetFilter,
|
|
1933
1938
|
profileName: forkName,
|
|
1934
1939
|
forkedFrom: profile.name,
|
|
@@ -1963,6 +1968,7 @@ export class BrowserService {
|
|
|
1963
1968
|
port: existingInfo.port,
|
|
1964
1969
|
pid: existingInfo.pid,
|
|
1965
1970
|
electron: effectiveProfile.electron,
|
|
1971
|
+
browserType: effectiveProfile.browser,
|
|
1966
1972
|
targetFilter: effectiveProfile.targetFilter,
|
|
1967
1973
|
tasks,
|
|
1968
1974
|
sessionCache: new Map(),
|
|
@@ -1994,6 +2000,7 @@ export class BrowserService {
|
|
|
1994
2000
|
port: conn.port,
|
|
1995
2001
|
pid: conn.pid,
|
|
1996
2002
|
electron: profile.electron,
|
|
2003
|
+
browserType: profile.browser,
|
|
1997
2004
|
targetFilter: profile.targetFilter,
|
|
1998
2005
|
tasks: conn.pid === 0 ? this.loadTaskState(profile.name) : new Map(),
|
|
1999
2006
|
sessionCache: new Map(),
|
|
@@ -2007,6 +2014,7 @@ export class BrowserService {
|
|
|
2007
2014
|
port: conn.port,
|
|
2008
2015
|
pid: conn.pid,
|
|
2009
2016
|
electron: profile.electron,
|
|
2017
|
+
browserType: profile.browser,
|
|
2010
2018
|
targetFilter: profile.targetFilter,
|
|
2011
2019
|
tasks: new Map(),
|
|
2012
2020
|
sessionCache: new Map(),
|
|
@@ -2028,6 +2036,7 @@ export class BrowserService {
|
|
|
2028
2036
|
port: 0,
|
|
2029
2037
|
pid: 0,
|
|
2030
2038
|
electron: profile.electron,
|
|
2039
|
+
browserType: profile.browser,
|
|
2031
2040
|
targetFilter: profile.targetFilter,
|
|
2032
2041
|
tasks: this.loadTaskState(profile.name),
|
|
2033
2042
|
sessionCache: new Map(),
|
|
@@ -2045,6 +2054,7 @@ export class BrowserService {
|
|
|
2045
2054
|
port,
|
|
2046
2055
|
pid: 0,
|
|
2047
2056
|
electron: profile.electron,
|
|
2057
|
+
browserType: profile.browser,
|
|
2048
2058
|
targetFilter: profile.targetFilter,
|
|
2049
2059
|
tasks: this.loadTaskState(profile.name),
|
|
2050
2060
|
sessionCache: new Map(),
|
|
@@ -2055,6 +2065,19 @@ export class BrowserService {
|
|
|
2055
2065
|
async enableDomains(cdp) {
|
|
2056
2066
|
await cdp.send('Target.setDiscoverTargets', { discover: true });
|
|
2057
2067
|
}
|
|
2068
|
+
/**
|
|
2069
|
+
* The single path that opens a new CDP page/window target. It exists so the
|
|
2070
|
+
* Arc guard lives in exactly one place: `Target.createTarget` crashes Arc
|
|
2071
|
+
* (verified, PR #2778), so an Arc connection throws a clear, actionable error
|
|
2072
|
+
* here instead of every call site re-checking. Every drivable Chromium-family
|
|
2073
|
+
* browser goes straight through.
|
|
2074
|
+
*/
|
|
2075
|
+
async createPageTarget(conn, params) {
|
|
2076
|
+
if (conn.browserType === 'arc') {
|
|
2077
|
+
throw arcNotDrivableError(conn.bareName ?? conn.profileName);
|
|
2078
|
+
}
|
|
2079
|
+
return (await conn.cdp.send('Target.createTarget', params));
|
|
2080
|
+
}
|
|
2058
2081
|
async getOrCreateWindow(conn) {
|
|
2059
2082
|
// Already have a window for this profile?
|
|
2060
2083
|
if (conn.windowId) {
|
|
@@ -2086,10 +2109,10 @@ export class BrowserService {
|
|
|
2086
2109
|
`Available page targets:\n${candidates || ' (none)'}`);
|
|
2087
2110
|
}
|
|
2088
2111
|
// First ever use - create window
|
|
2089
|
-
const result =
|
|
2112
|
+
const result = await this.createPageTarget(conn, {
|
|
2090
2113
|
url: 'about:blank',
|
|
2091
2114
|
newWindow: true,
|
|
2092
|
-
})
|
|
2115
|
+
});
|
|
2093
2116
|
conn.windowId = result.targetId;
|
|
2094
2117
|
return result.targetId;
|
|
2095
2118
|
}
|
|
@@ -2358,6 +2381,7 @@ export class BrowserService {
|
|
|
2358
2381
|
port,
|
|
2359
2382
|
pid: existingInfo?.pid ?? 0,
|
|
2360
2383
|
electron: profile.electron,
|
|
2384
|
+
browserType: profile.browser,
|
|
2361
2385
|
targetFilter: resolved.targetFilter ?? profile.targetFilter,
|
|
2362
2386
|
profileName: dirName,
|
|
2363
2387
|
bareName: bare,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type BrowserType = 'chrome' | 'comet' | 'chromium' | 'brave' | 'edge' | 'custom';
|
|
1
|
+
export type BrowserType = 'chrome' | 'comet' | 'chromium' | 'brave' | 'edge' | 'arc' | 'custom';
|
|
2
2
|
/**
|
|
3
3
|
* A single named endpoint preset within a profile. Lets one profile cover
|
|
4
4
|
* the local + remote variants of the same app (e.g. an Electron app on this
|
|
@@ -23,7 +23,9 @@ export interface HostedReceiverFunnel {
|
|
|
23
23
|
}
|
|
24
24
|
/** One receiver this box hosts. */
|
|
25
25
|
export interface HostedReceiverConfig {
|
|
26
|
-
/** Secrets bundle holding GITHUB_WEBHOOK_SECRET and/or
|
|
26
|
+
/** Secrets bundle holding GITHUB_WEBHOOK_SECRET, LINEAR_WEBHOOK_SECRET, and/or
|
|
27
|
+
* SLACK_SIGNING_SECRET (a Slack receiver also carries SLACK_BOT_TOKEN when the
|
|
28
|
+
* agent replies with the Slack Web API rather than `agents send`). */
|
|
27
29
|
bundle: string;
|
|
28
30
|
/** Local bind port (default 8787). */
|
|
29
31
|
port?: number;
|
|
@@ -112,8 +112,10 @@ export function resolveReceiverSecrets(bundle) {
|
|
|
112
112
|
secrets.github = env.GITHUB_WEBHOOK_SECRET;
|
|
113
113
|
if (env.LINEAR_WEBHOOK_SECRET)
|
|
114
114
|
secrets.linear = env.LINEAR_WEBHOOK_SECRET;
|
|
115
|
-
if (
|
|
116
|
-
|
|
115
|
+
if (env.SLACK_SIGNING_SECRET)
|
|
116
|
+
secrets.slack = env.SLACK_SIGNING_SECRET;
|
|
117
|
+
if (!secrets.github && !secrets.linear && !secrets.slack) {
|
|
118
|
+
throw new Error(`bundle '${bundle}' has none of GITHUB_WEBHOOK_SECRET, LINEAR_WEBHOOK_SECRET, or SLACK_SIGNING_SECRET`);
|
|
117
119
|
}
|
|
118
120
|
return secrets;
|
|
119
121
|
}
|
|
@@ -176,12 +178,15 @@ export async function startHostedWebhookReceivers(opts) {
|
|
|
176
178
|
// Per-port durable delivery dedup so replays survive a daemon restart and
|
|
177
179
|
// two receivers on distinct ports never share a dedup ledger.
|
|
178
180
|
deliveryStore: createFileDeliveryStore(path.join(getRuntimeStateDir(), 'webhook', `deliveries-${port}.json`)),
|
|
179
|
-
|
|
181
|
+
// Logged at MATCH time, before dispatch — a `run.command` handler can
|
|
182
|
+
// block on a shelled-out agent run for minutes, and that must never
|
|
183
|
+
// delay the log that says a delivery fired (RUSH-2722).
|
|
184
|
+
onMatch: (webhook, matchedJobNames, matchedHandlerNames) => {
|
|
180
185
|
const parts = [];
|
|
181
|
-
if (
|
|
182
|
-
parts.push(`routines ${
|
|
183
|
-
if (
|
|
184
|
-
parts.push(`handlers ${
|
|
186
|
+
if (matchedJobNames.length)
|
|
187
|
+
parts.push(`routines ${matchedJobNames.join(', ')}`);
|
|
188
|
+
if (matchedHandlerNames.length)
|
|
189
|
+
parts.push(`handlers ${matchedHandlerNames.join(', ')}`);
|
|
185
190
|
log('INFO', `webhook ${webhook.source}:${webhook.event} ${parts.length ? `fired ${parts.join('; ')}` : 'no match'}`);
|
|
186
191
|
},
|
|
187
192
|
// The 202 ack means no HTTP status carries a settle failure — the daemon
|
|
@@ -96,7 +96,7 @@ export const CONFIG_KEYS = [
|
|
|
96
96
|
scope: 'device',
|
|
97
97
|
visibility: 'machine',
|
|
98
98
|
type: 'string',
|
|
99
|
-
description: 'Browser profile `agents browser start` resolves to without --profile (set via `agents browser
|
|
99
|
+
description: 'Browser profile `agents browser start` resolves to without --profile (set via `agents browser use`).',
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
102
|
name: 'agents.max-concurrent',
|
|
Binary file
|
|
Binary file
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* remain the stores; these are doors that point at the right reader.
|
|
6
6
|
*
|
|
7
7
|
* inbox → feed (needs-you default)
|
|
8
|
-
* timeline → feed --filter updates (agent progress stream)
|
|
9
8
|
*
|
|
10
9
|
* `roster` was removed — use `agents sessions --active`.
|
|
10
|
+
* `timeline` was removed — use `agents feed --filter updates` (RUSH-2692).
|
|
11
11
|
* `audit` is NOT an alias here — `agents audit` is already the tamper-evident
|
|
12
12
|
* run-dispatch log. Ops trail = `agents events` (optionally `--audit`).
|
|
13
13
|
*/
|
|
14
|
-
export type ObserveAlias = 'inbox'
|
|
14
|
+
export type ObserveAlias = 'inbox';
|
|
15
15
|
export declare const OBSERVE_ALIASES: readonly ObserveAlias[];
|
|
16
16
|
export interface ObserveExpandResult {
|
|
17
17
|
/** argv for the real command (no program name): e.g. ['feed', '--filter', 'updates'] */
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* remain the stores; these are doors that point at the right reader.
|
|
6
6
|
*
|
|
7
7
|
* inbox → feed (needs-you default)
|
|
8
|
-
* timeline → feed --filter updates (agent progress stream)
|
|
9
8
|
*
|
|
10
9
|
* `roster` was removed — use `agents sessions --active`.
|
|
10
|
+
* `timeline` was removed — use `agents feed --filter updates` (RUSH-2692).
|
|
11
11
|
* `audit` is NOT an alias here — `agents audit` is already the tamper-evident
|
|
12
12
|
* run-dispatch log. Ops trail = `agents events` (optionally `--audit`).
|
|
13
13
|
*/
|
|
14
|
-
export const OBSERVE_ALIASES = ['inbox'
|
|
14
|
+
export const OBSERVE_ALIASES = ['inbox'];
|
|
15
15
|
/** True when `rest` already carries a `--filter` / `--filter=…` flag. */
|
|
16
16
|
export function hasFilterFlag(rest) {
|
|
17
17
|
return rest.some((a) => a === '--filter' || a.startsWith('--filter='));
|
|
@@ -32,15 +32,6 @@ export function expandObserveAlias(alias, rest = []) {
|
|
|
32
32
|
argv: ['feed', ...tail],
|
|
33
33
|
note: 'agents inbox → agents feed (needs-you inbox)',
|
|
34
34
|
};
|
|
35
|
-
case 'timeline': {
|
|
36
|
-
const argv = hasFilterFlag(tail)
|
|
37
|
-
? ['feed', ...tail]
|
|
38
|
-
: ['feed', '--filter', 'updates', ...tail];
|
|
39
|
-
return {
|
|
40
|
-
argv,
|
|
41
|
-
note: 'agents timeline → agents feed --filter updates',
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
35
|
default:
|
|
45
36
|
return null;
|
|
46
37
|
}
|
package/dist/lib/project-key.js
CHANGED
|
@@ -55,6 +55,13 @@ export function repoRootForCwd(dir, home = os.homedir()) {
|
|
|
55
55
|
for (;;) {
|
|
56
56
|
if (fs.existsSync(path.join(current, '.git')))
|
|
57
57
|
return current === stop ? undefined : current;
|
|
58
|
+
// Never climb past $HOME: an ancestor of home (/tmp, /, ...) is not part of
|
|
59
|
+
// any project this cwd belongs to, and treating one as the repo root would
|
|
60
|
+
// let unrelated host state (e.g. a stray /tmp/.git) swallow every loose
|
|
61
|
+
// directory under home. Mirrors the shim's own home boundary — see
|
|
62
|
+
// shims.ts shimExecTail.
|
|
63
|
+
if (current === stop)
|
|
64
|
+
return undefined;
|
|
58
65
|
const parent = path.dirname(current);
|
|
59
66
|
if (parent === current)
|
|
60
67
|
return undefined;
|
package/dist/lib/runner.js
CHANGED
|
@@ -26,7 +26,7 @@ import { resolveModel, buildReasoningFlags } from './models.js';
|
|
|
26
26
|
import { createTimer, redactPrompt, emitRoutineEnd } from './feed/events.js';
|
|
27
27
|
import { codexEditWritableRoots, codexPolicyArgs } from './codex-policy.js';
|
|
28
28
|
import { applyAddDirs } from './add-dir.js';
|
|
29
|
-
import { normalizeMode, resolveHeadlessMode, buildExecEnv, detectRateLimit, detectAuthFailure, isAuthFailureFromLog, authFailureReason, } from './exec.js';
|
|
29
|
+
import { normalizeMode, resolveHeadlessMode, buildExecEnv, detectRateLimit, detectAuthFailure, isAuthFailureFromLog, authFailureReason, AGENT_COMMANDS, } from './exec.js';
|
|
30
30
|
import { resolveActor } from './actor.js';
|
|
31
31
|
import { loadTask as loadHostTask } from './hosts/tasks.js';
|
|
32
32
|
import { reconcileTask as reconcileHostTask } from './hosts/reconcile.js';
|
|
@@ -39,7 +39,7 @@ import { resolveClaudeSetupToken } from './claude-account-token.js';
|
|
|
39
39
|
import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, } from './accounting/rotate.js';
|
|
40
40
|
import { readAuthHealth, isDeadVerdict } from './auth-health.js';
|
|
41
41
|
import { machineId } from './machine-id.js';
|
|
42
|
-
import { isSelfUpdatingAgent, ROUTINE_AGENT_COMMANDS
|
|
42
|
+
import { isSelfUpdatingAgent, ROUTINE_AGENT_COMMANDS, isAgentHardDeprecated, hardDeprecationError } from './agents.js';
|
|
43
43
|
export class RoutineAlreadyRunningError extends Error {
|
|
44
44
|
constructor(jobName, runId) {
|
|
45
45
|
super(`Routine '${jobName}' already has a running execution (${runId})`);
|
|
@@ -416,6 +416,17 @@ async function runWithAttempt(config, trigger, run, wrapTerminal) {
|
|
|
416
416
|
function terminateRoutineTree(pid) {
|
|
417
417
|
if (!pid)
|
|
418
418
|
return;
|
|
419
|
+
// Never take THIS process down. Both kills below are unconditional SIGKILLs,
|
|
420
|
+
// so a RunMeta naming the reaper's own pid -- a reused pid, a record written
|
|
421
|
+
// by the process now doing the reaping, or a hand-written fixture -- makes the
|
|
422
|
+
// reaper SIGKILL itself, and via `-pid` its whole process group with it. There
|
|
423
|
+
// is no recovery from that: the run is never finalized, and on the daemon it
|
|
424
|
+
// takes the scheduler down mid-sweep. daemon.ts:457 already refuses to evict an
|
|
425
|
+
// incumbent whose pid is `process.pid` for exactly this reason; the reap path
|
|
426
|
+
// needs the same guard. Skipping the kill still lets the caller finalize the
|
|
427
|
+
// run record, which is the part that matters.
|
|
428
|
+
if (pid === process.pid)
|
|
429
|
+
return;
|
|
419
430
|
if (process.platform === 'win32') {
|
|
420
431
|
killTree(pid);
|
|
421
432
|
return;
|
|
@@ -503,7 +514,7 @@ export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
|
|
|
503
514
|
cmd.push('--account', config.account);
|
|
504
515
|
return cmd;
|
|
505
516
|
}
|
|
506
|
-
const template =
|
|
517
|
+
const template = ROUTINE_AGENT_COMMANDS[agent];
|
|
507
518
|
if (!template) {
|
|
508
519
|
throw new Error(`Unsupported agent for daemon jobs: ${agent}`);
|
|
509
520
|
}
|
|
@@ -610,7 +621,7 @@ export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
|
|
|
610
621
|
return cmd;
|
|
611
622
|
}
|
|
612
623
|
/**
|
|
613
|
-
* Append
|
|
624
|
+
* Append the agent's canonical model flag and reasoning flags to a command.
|
|
614
625
|
*
|
|
615
626
|
* Pass-through model resolution: validates against the installed (agent, version)
|
|
616
627
|
* catalog when possible and writes a warning to stderr on miss, but never blocks.
|
|
@@ -621,15 +632,19 @@ function appendModelAndReasoning(cmd, config) {
|
|
|
621
632
|
const agent = config.agent;
|
|
622
633
|
const model = config.config?.model;
|
|
623
634
|
if (model) {
|
|
635
|
+
const modelFlag = AGENT_COMMANDS[agent].modelFlag;
|
|
636
|
+
if (!modelFlag) {
|
|
637
|
+
throw new Error(`Agent ${agent} does not support routine model selection`);
|
|
638
|
+
}
|
|
624
639
|
if (config.version) {
|
|
625
640
|
const resolved = resolveModel(agent, config.version, model);
|
|
626
641
|
if (resolved.warning) {
|
|
627
642
|
process.stderr.write(`[agents] ${resolved.warning}\n`);
|
|
628
643
|
}
|
|
629
|
-
cmd.push(
|
|
644
|
+
cmd.push(modelFlag, resolved.forwarded);
|
|
630
645
|
}
|
|
631
646
|
else {
|
|
632
|
-
cmd.push(
|
|
647
|
+
cmd.push(modelFlag, model);
|
|
633
648
|
}
|
|
634
649
|
}
|
|
635
650
|
const reasoning = config.config?.reasoning;
|
|
Binary file
|
|
Binary file
|
|
@@ -305,7 +305,7 @@ export declare function resolveBundleEnv(bundle: SecretsBundle, _opts?: ResolveB
|
|
|
305
305
|
* path in index.ts can share it without a bundles↔index import cycle. See that
|
|
306
306
|
* module for the full contract.
|
|
307
307
|
*/
|
|
308
|
-
export { isHeadlessSecretsContext } from './headless.js';
|
|
308
|
+
export { isHeadlessSecretsContext, isAgentInvocationContext } from './headless.js';
|
|
309
309
|
/**
|
|
310
310
|
* Read a bundle's metadata AND resolve its env in a single Touch ID prompt.
|
|
311
311
|
*
|
|
@@ -1211,7 +1211,7 @@ export function resolveBundleEnv(bundle, _opts = {}) {
|
|
|
1211
1211
|
* path in index.ts can share it without a bundles↔index import cycle. See that
|
|
1212
1212
|
* module for the full contract.
|
|
1213
1213
|
*/
|
|
1214
|
-
export { isHeadlessSecretsContext } from './headless.js';
|
|
1214
|
+
export { isHeadlessSecretsContext, isAgentInvocationContext } from './headless.js';
|
|
1215
1215
|
/**
|
|
1216
1216
|
* Read a bundle's metadata AND resolve its env in a single Touch ID prompt.
|
|
1217
1217
|
*
|