@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 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
@@ -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 only (Chrome, Comet, Chromium, Brave, Edge, Arc).
362
- Safari and Firefox are not supported — they don't speak the Chrome DevTools
363
- Protocol the way agents browser expects. On Windows, Edge is the default
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
- '(Safari and Firefox are not supported — agents browser drives over the Chrome DevTools Protocol.)';
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. Safari and Firefox are
191
- // intentionally excluded — they don't speak the Chrome DevTools Protocol the
192
- // way cdp.ts expects, so they'd need separate drivers.
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
@@ -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
- tabs?: TabInfo[];
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.95",
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",