@phnx-labs/agents-cli 1.22.95 → 1.22.97
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 +6 -0
- package/dist/commands/browser.js +20 -4
- package/dist/commands/setup-browser.js +2 -1
- package/dist/lib/browser/chrome.js +4 -3
- package/dist/lib/browser/ipc.js +4 -0
- package/dist/lib/browser/service.d.ts +9 -1
- package/dist/lib/browser/service.js +58 -0
- package/dist/lib/browser/types.d.ts +19 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.97
|
|
4
|
+
|
|
5
|
+
- `agents browser --help` and the `agents setup browser` install hint no longer claim Firefox is unsupported: they now state Chromium-family over CDP, Arc natively, and Firefox over WebDriver BiDi (Safari stays unsupported). `tabs --all` gains a help example and rows in the Arc and Firefox capability tables.
|
|
6
|
+
|
|
7
|
+
- **`agents browser tabs --all` shows every tab open in the profile browser, yours included (PHNX-4043).** Until now `tabs` listed only the task's own tabs, so an agent sharing an Arc Space, a Comet store, or a Firefox profile with you could not see what else was open. `--all` lists every top-level tab: OWNER names the agents-cli task that owns it, or `you` for a tab no task owns; `*` still marks the task's current tab. Read-only on every backend (Chromium `Target.getTargets`, the Arc Space's tabs, Firefox `browsingContext.getTree`). Source: `cli/src/lib/browser/service.ts` `profileTabs`, `cli/src/commands/browser.ts`.
|
|
8
|
+
|
|
3
9
|
## 1.22.95
|
|
4
10
|
|
|
5
11
|
- **Firefox automation over WebDriver BiDi (PHNX-4043).** `agents browser` now drives
|
package/dist/commands/browser.js
CHANGED
|
@@ -329,6 +329,9 @@ export function registerBrowserCommand(program) {
|
|
|
329
329
|
agents browser navigate https://example.com
|
|
330
330
|
agents browser screenshot
|
|
331
331
|
|
|
332
|
+
# Every tab open in the profile browser, yours next to the task's (OWNER column)
|
|
333
|
+
agents browser tabs --all
|
|
334
|
+
|
|
332
335
|
# Keep one process and browser-service socket warm for repeated actions
|
|
333
336
|
agents browser stream --task "$AGENTS_BROWSER_TASK"
|
|
334
337
|
|
|
@@ -358,9 +361,10 @@ export function registerBrowserCommand(program) {
|
|
|
358
361
|
Most agent workflows should use the 'browser' skill instead of raw subcommands.
|
|
359
362
|
The skill wraps profile selection, snapshotting, and tunneling.
|
|
360
363
|
|
|
361
|
-
Browser support: Chromium-family
|
|
362
|
-
|
|
363
|
-
|
|
364
|
+
Browser support: Chromium-family over the Chrome DevTools Protocol (Chrome,
|
|
365
|
+
Comet, Chromium, Brave, Edge), Arc natively (one profile per Space), and
|
|
366
|
+
Firefox over WebDriver BiDi (one profile per profiles.ini entry; Firefox 129+
|
|
367
|
+
dropped CDP). Safari is not supported. On Windows, Edge is the default
|
|
364
368
|
because it's preinstalled. On macOS and Linux, Chrome is preferred when
|
|
365
369
|
installed; otherwise the first Chromium-family binary on disk wins.
|
|
366
370
|
`,
|
|
@@ -2235,15 +2239,17 @@ function registerTaskCommands(browser) {
|
|
|
2235
2239
|
});
|
|
2236
2240
|
browser
|
|
2237
2241
|
.command('tabs')
|
|
2238
|
-
.description('List tabs open for the current task')
|
|
2242
|
+
.description('List tabs open for the current task; --all shows every tab open in the profile browser, yours included')
|
|
2239
2243
|
.option(TASK_OPTION_FLAG, TASK_OPTION_DESC)
|
|
2240
2244
|
.option(DEVICE_ON_PAGE_VERB_FLAG, DEVICE_ON_PAGE_VERB_DESC)
|
|
2245
|
+
.option('--all', "Every tab open in the profile browser, not only this task's: OWNER names the owning task, or \"you\" for a tab no task owns")
|
|
2241
2246
|
.option('--json', 'Output machine-readable JSON')
|
|
2242
2247
|
.action(async (opts) => {
|
|
2243
2248
|
const task = resolveTaskName(opts);
|
|
2244
2249
|
const response = await sendIPCRequest({
|
|
2245
2250
|
action: 'tab-list',
|
|
2246
2251
|
task,
|
|
2252
|
+
...(opts.all ? { all: true } : {}),
|
|
2247
2253
|
});
|
|
2248
2254
|
if (!response.ok) {
|
|
2249
2255
|
if (opts.json) {
|
|
@@ -2262,6 +2268,16 @@ function registerTaskCommands(browser) {
|
|
|
2262
2268
|
console.log('No tabs open');
|
|
2263
2269
|
return;
|
|
2264
2270
|
}
|
|
2271
|
+
if (opts.all) {
|
|
2272
|
+
console.log('TAB'.padEnd(12) + 'OWNER'.padEnd(18) + 'URL');
|
|
2273
|
+
console.log('-'.repeat(88));
|
|
2274
|
+
for (const t of response.tabs) {
|
|
2275
|
+
const owner = t.task ?? 'you';
|
|
2276
|
+
const current = t.current ? ' *' : '';
|
|
2277
|
+
console.log(t.id.slice(0, 11).padEnd(12) + owner.slice(0, 17).padEnd(18) + t.url.slice(0, 55) + current);
|
|
2278
|
+
}
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2265
2281
|
console.log('TAB'.padEnd(12) + 'URL');
|
|
2266
2282
|
console.log('-'.repeat(70));
|
|
2267
2283
|
for (const t of response.tabs) {
|
|
@@ -15,7 +15,8 @@ import { DEFAULT_VIEWPORT } from '../lib/browser/devices.js';
|
|
|
15
15
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
16
16
|
import { defaultBrowserChoice } from './setup-preferences.js';
|
|
17
17
|
const INSTALL_HINT = 'Install one of: Google Chrome, Brave, Microsoft Edge, Chromium, Comet, or Arc, then re-run `agents setup browser`.\n' +
|
|
18
|
-
'(
|
|
18
|
+
'(This wizard picks a Chromium-family browser. An installed Firefox is not auto-picked here; its profiles are ' +
|
|
19
|
+
'discovered from profiles.ini and selected with `agents browser use firefox-<name>`. Safari is not supported.)';
|
|
19
20
|
/**
|
|
20
21
|
* Interactive browser setup. Returns true if a usable default profile exists
|
|
21
22
|
* afterwards, false if none exists / the user backed out. Non-interactively it
|
|
@@ -187,9 +187,10 @@ export function findBrowserPath(browserType, customBinary) {
|
|
|
187
187
|
throw new Error(`Browser "${browserType}" not found. Install it first.`);
|
|
188
188
|
}
|
|
189
189
|
// Per-platform Chromium-family priority list for "no --profile" auto-pick.
|
|
190
|
-
// Order is: most-likely-installed-and-stable first.
|
|
191
|
-
//
|
|
192
|
-
//
|
|
190
|
+
// Order is: most-likely-installed-and-stable first. Firefox is intentionally
|
|
191
|
+
// absent: it has its own WebDriver BiDi driver (drivers/firefox.ts) and its
|
|
192
|
+
// profiles come from profiles.ini discovery, never from this binary pick.
|
|
193
|
+
// Safari has no driver at all.
|
|
193
194
|
const DEFAULT_BROWSER_PRIORITY = {
|
|
194
195
|
// macOS: Chrome leads (>70% of dev machines), then the rest of the family.
|
|
195
196
|
// Arc is last: it's in maintenance mode and needs a blank tab to drive, so it
|
package/dist/lib/browser/ipc.js
CHANGED
|
@@ -935,6 +935,10 @@ export class BrowserIPCServer {
|
|
|
935
935
|
if (!request.task) {
|
|
936
936
|
return { ok: false, error: 'Task required' };
|
|
937
937
|
}
|
|
938
|
+
if (request.all) {
|
|
939
|
+
const tabs = await this.service.profileTabs(request.task);
|
|
940
|
+
return { ok: true, tabs: tabs.map(t => ({ id: t.id, url: t.url, title: t.title, current: t.current, task: t.task })) };
|
|
941
|
+
}
|
|
938
942
|
const tabs = await this.service.tabList(request.task);
|
|
939
943
|
// Carry `current` through — it is the one field a caller needs to show
|
|
940
944
|
// which tab URL-less verbs act on, and dropping it here left `agents
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CDPClient } from './cdp.js';
|
|
2
2
|
import { type DeviceProbe } from './resolve-target.js';
|
|
3
3
|
import { type ResolvedDomainSkill } from './domain-skills.js';
|
|
4
|
-
import { type Task, type TabInfo, type ProfileStatus, type BrowserType, type HistoricalTask, type ReapResult, type ProfileName, type ConnectionKey } from './types.js';
|
|
4
|
+
import { type Task, type TabInfo, type ProfileTabInfo, type ProfileStatus, type BrowserType, type HistoricalTask, type ReapResult, type ProfileName, type ConnectionKey } from './types.js';
|
|
5
5
|
import { type ReapOptions } from './hygiene.js';
|
|
6
6
|
import { type RefOpts, type RefNode } from './refs.js';
|
|
7
7
|
import type { TargetFilter, PageOpenResult, ArcNativeProfileIdentity, FirefoxProfileIdentity } from './types.js';
|
|
@@ -380,6 +380,14 @@ export declare class BrowserService {
|
|
|
380
380
|
title: string;
|
|
381
381
|
current: boolean;
|
|
382
382
|
}>>;
|
|
383
|
+
/**
|
|
384
|
+
* Every top-level tab open in the task's profile browser, the owner's tabs
|
|
385
|
+
* included (`agents browser tabs --all`). Read-only: nothing is focused,
|
|
386
|
+
* navigated, or closed. A tab an agents-cli task owns carries that task's
|
|
387
|
+
* name and short id (so it can be addressed); any other tab carries the
|
|
388
|
+
* browser's own id and no task. Same rows on every backend.
|
|
389
|
+
*/
|
|
390
|
+
profileTabs(taskId: string): Promise<ProfileTabInfo[]>;
|
|
383
391
|
private resolveTabHint;
|
|
384
392
|
private resolveCurrentTab;
|
|
385
393
|
private getCdpTargetId;
|
|
@@ -1242,6 +1242,64 @@ export class BrowserService {
|
|
|
1242
1242
|
}
|
|
1243
1243
|
return tabs;
|
|
1244
1244
|
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Every top-level tab open in the task's profile browser, the owner's tabs
|
|
1247
|
+
* included (`agents browser tabs --all`). Read-only: nothing is focused,
|
|
1248
|
+
* navigated, or closed. A tab an agents-cli task owns carries that task's
|
|
1249
|
+
* name and short id (so it can be addressed); any other tab carries the
|
|
1250
|
+
* browser's own id and no task. Same rows on every backend.
|
|
1251
|
+
*/
|
|
1252
|
+
async profileTabs(taskId) {
|
|
1253
|
+
const { conn, task } = await this.findTask(taskId);
|
|
1254
|
+
const owners = new Map();
|
|
1255
|
+
for (const [, owner] of conn.tasks) {
|
|
1256
|
+
const entries = conn.backend === 'arc-native'
|
|
1257
|
+
? Object.entries(owner.arcNative?.tabs ?? {}).map(([shortId, ref]) => [shortId, ref.tabId])
|
|
1258
|
+
: Object.entries(owner.tabs);
|
|
1259
|
+
for (const [shortId, nativeId] of entries)
|
|
1260
|
+
owners.set(nativeId, { owner, shortId });
|
|
1261
|
+
}
|
|
1262
|
+
const row = (nativeId, url, title) => {
|
|
1263
|
+
const owned = owners.get(nativeId);
|
|
1264
|
+
if (!owned)
|
|
1265
|
+
return { id: nativeId, url, title };
|
|
1266
|
+
return {
|
|
1267
|
+
id: owned.shortId,
|
|
1268
|
+
url,
|
|
1269
|
+
title,
|
|
1270
|
+
task: owned.owner.name,
|
|
1271
|
+
current: owned.owner === task && owned.shortId === task.currentTabId,
|
|
1272
|
+
};
|
|
1273
|
+
};
|
|
1274
|
+
if (conn.backend === 'arc-native') {
|
|
1275
|
+
const native = this.requireArcTask(task);
|
|
1276
|
+
const space = (await enumerateArcSpaces()).find((candidate) => candidate.windowId === native.windowId && candidate.spaceId === native.spaceId);
|
|
1277
|
+
if (!space) {
|
|
1278
|
+
throw new Error(`Arc Space "${native.spaceTitle}" is not open in its original window (${native.windowId}); ` +
|
|
1279
|
+
'open it in Arc and retry.');
|
|
1280
|
+
}
|
|
1281
|
+
return space.tabs.map((tab) => row(tab.tabId, tab.url, tab.title));
|
|
1282
|
+
}
|
|
1283
|
+
if (conn.backend === 'bidi') {
|
|
1284
|
+
const rows = [];
|
|
1285
|
+
for (const context of await bidiTopLevelContexts(conn.bidi)) {
|
|
1286
|
+
let title = '';
|
|
1287
|
+
try {
|
|
1288
|
+
title = String(await bidiEvaluate(conn.bidi, context.context, 'document.title'));
|
|
1289
|
+
}
|
|
1290
|
+
catch {
|
|
1291
|
+
/* about: pages or a mid-navigation context — url is enough */
|
|
1292
|
+
}
|
|
1293
|
+
rows.push(row(context.context, context.url, title));
|
|
1294
|
+
}
|
|
1295
|
+
return rows;
|
|
1296
|
+
}
|
|
1297
|
+
requireCdp(conn, 'enumerate');
|
|
1298
|
+
const { targetInfos } = (await conn.cdp.send('Target.getTargets'));
|
|
1299
|
+
return targetInfos
|
|
1300
|
+
.filter((target) => target.type === 'page')
|
|
1301
|
+
.map((target) => row(target.targetId, target.url, target.title));
|
|
1302
|
+
}
|
|
1245
1303
|
async resolveTabHint(conn, task, hint) {
|
|
1246
1304
|
// Exact match
|
|
1247
1305
|
if (task.tabs[hint])
|
|
@@ -327,6 +327,20 @@ export interface TabInfo {
|
|
|
327
327
|
/** Whether this is the task's current tab (the one URL-less verbs act on). */
|
|
328
328
|
current?: boolean;
|
|
329
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* One row of `agents browser tabs --all`: a top-level tab open in the profile's
|
|
332
|
+
* browser. Owned by an agents-cli task → `task` names it and `id` is that
|
|
333
|
+
* task's short tab id; otherwise (the owner's own tab, or a tab from another
|
|
334
|
+
* tool) `task` is absent and `id` is the browser's own tab id.
|
|
335
|
+
*/
|
|
336
|
+
export interface ProfileTabInfo {
|
|
337
|
+
id: string;
|
|
338
|
+
url: string;
|
|
339
|
+
title: string;
|
|
340
|
+
task?: string;
|
|
341
|
+
/** The queried task's current tab. */
|
|
342
|
+
current?: boolean;
|
|
343
|
+
}
|
|
330
344
|
export interface ProfileStatus {
|
|
331
345
|
/**
|
|
332
346
|
* The BARE profile name — what the user passed to `--profile` and what
|
|
@@ -360,6 +374,7 @@ export interface TaskStatus {
|
|
|
360
374
|
url: string;
|
|
361
375
|
title?: string;
|
|
362
376
|
current?: boolean;
|
|
377
|
+
task?: string;
|
|
363
378
|
}>;
|
|
364
379
|
}
|
|
365
380
|
export interface HistoricalTask {
|
|
@@ -395,6 +410,8 @@ export interface IPCRequest {
|
|
|
395
410
|
action: IPCAction;
|
|
396
411
|
task?: string;
|
|
397
412
|
taskName?: string;
|
|
413
|
+
/** tab-list: every tab open in the profile browser, not only the task's own. */
|
|
414
|
+
all?: boolean;
|
|
398
415
|
profile?: string;
|
|
399
416
|
url?: string;
|
|
400
417
|
tabId?: string;
|
|
@@ -543,7 +560,8 @@ export interface IPCResponse {
|
|
|
543
560
|
*/
|
|
544
561
|
reused?: boolean;
|
|
545
562
|
windowTargetId?: string;
|
|
546
|
-
|
|
563
|
+
/** `tab-list` rows; `task` is absent for a tab no agents-cli task owns (`--all`). */
|
|
564
|
+
tabs?: ProfileTabInfo[];
|
|
547
565
|
profiles?: ProfileStatus[];
|
|
548
566
|
history?: HistoricalTask[];
|
|
549
567
|
/** `gc`: what the abandoned-task reaper closed (or would close under dryRun). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.97",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|