@phnx-labs/agents-cli 1.22.95 → 1.22.96
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,6 +1,8 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.22.
|
|
3
|
+
## 1.22.96
|
|
4
|
+
|
|
5
|
+
- **`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`.
|
|
4
6
|
|
|
5
7
|
- **Firefox automation over WebDriver BiDi (PHNX-4043).** `agents browser` now drives
|
|
6
8
|
Firefox, which dropped the Chrome DevTools Protocol in 129. Every Firefox profile in
|
package/dist/commands/browser.js
CHANGED
|
@@ -2235,15 +2235,17 @@ function registerTaskCommands(browser) {
|
|
|
2235
2235
|
});
|
|
2236
2236
|
browser
|
|
2237
2237
|
.command('tabs')
|
|
2238
|
-
.description('List tabs open for the current task')
|
|
2238
|
+
.description('List tabs open for the current task; --all shows every tab open in the profile browser, yours included')
|
|
2239
2239
|
.option(TASK_OPTION_FLAG, TASK_OPTION_DESC)
|
|
2240
2240
|
.option(DEVICE_ON_PAGE_VERB_FLAG, DEVICE_ON_PAGE_VERB_DESC)
|
|
2241
|
+
.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
2242
|
.option('--json', 'Output machine-readable JSON')
|
|
2242
2243
|
.action(async (opts) => {
|
|
2243
2244
|
const task = resolveTaskName(opts);
|
|
2244
2245
|
const response = await sendIPCRequest({
|
|
2245
2246
|
action: 'tab-list',
|
|
2246
2247
|
task,
|
|
2248
|
+
...(opts.all ? { all: true } : {}),
|
|
2247
2249
|
});
|
|
2248
2250
|
if (!response.ok) {
|
|
2249
2251
|
if (opts.json) {
|
|
@@ -2262,6 +2264,16 @@ function registerTaskCommands(browser) {
|
|
|
2262
2264
|
console.log('No tabs open');
|
|
2263
2265
|
return;
|
|
2264
2266
|
}
|
|
2267
|
+
if (opts.all) {
|
|
2268
|
+
console.log('TAB'.padEnd(12) + 'OWNER'.padEnd(18) + 'URL');
|
|
2269
|
+
console.log('-'.repeat(88));
|
|
2270
|
+
for (const t of response.tabs) {
|
|
2271
|
+
const owner = t.task ?? 'you';
|
|
2272
|
+
const current = t.current ? ' *' : '';
|
|
2273
|
+
console.log(t.id.slice(0, 11).padEnd(12) + owner.slice(0, 17).padEnd(18) + t.url.slice(0, 55) + current);
|
|
2274
|
+
}
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2265
2277
|
console.log('TAB'.padEnd(12) + 'URL');
|
|
2266
2278
|
console.log('-'.repeat(70));
|
|
2267
2279
|
for (const t of response.tabs) {
|
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.96",
|
|
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",
|