@phnx-labs/agents-cli 1.15.0 → 1.17.0

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.
Files changed (111) hide show
  1. package/CHANGELOG.md +143 -39
  2. package/README.md +6 -6
  3. package/dist/commands/alias.js +2 -2
  4. package/dist/commands/browser-picker.d.ts +21 -0
  5. package/dist/commands/browser-picker.js +114 -0
  6. package/dist/commands/browser.js +793 -83
  7. package/dist/commands/cloud.js +8 -0
  8. package/dist/commands/commands.js +72 -22
  9. package/dist/commands/daemon.js +2 -2
  10. package/dist/commands/exec.js +70 -1
  11. package/dist/commands/hooks.js +71 -26
  12. package/dist/commands/mcp.js +81 -39
  13. package/dist/commands/plugins.js +224 -17
  14. package/dist/commands/prune.js +29 -1
  15. package/dist/commands/pull.js +3 -3
  16. package/dist/commands/repo.js +1 -1
  17. package/dist/commands/routines.js +2 -2
  18. package/dist/commands/secrets.js +154 -20
  19. package/dist/commands/sessions.js +62 -19
  20. package/dist/commands/{init.d.ts → setup.d.ts} +7 -6
  21. package/dist/commands/{init.js → setup.js} +22 -21
  22. package/dist/commands/skills.js +60 -19
  23. package/dist/commands/subagents.js +41 -13
  24. package/dist/commands/utils.d.ts +16 -0
  25. package/dist/commands/utils.js +32 -0
  26. package/dist/commands/view.js +78 -20
  27. package/dist/commands/workflows.d.ts +10 -0
  28. package/dist/commands/workflows.js +457 -0
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +48 -36
  31. package/dist/lib/agents.js +2 -2
  32. package/dist/lib/auto-pull-worker.js +2 -3
  33. package/dist/lib/auto-pull.js +2 -2
  34. package/dist/lib/browser/cdp.d.ts +7 -1
  35. package/dist/lib/browser/cdp.js +32 -1
  36. package/dist/lib/browser/chrome.d.ts +10 -0
  37. package/dist/lib/browser/chrome.js +41 -3
  38. package/dist/lib/browser/devices.d.ts +4 -0
  39. package/dist/lib/browser/devices.js +27 -0
  40. package/dist/lib/browser/drivers/local.js +22 -6
  41. package/dist/lib/browser/drivers/ssh.js +9 -2
  42. package/dist/lib/browser/input.d.ts +1 -0
  43. package/dist/lib/browser/input.js +3 -0
  44. package/dist/lib/browser/ipc.js +158 -23
  45. package/dist/lib/browser/profiles.d.ts +10 -2
  46. package/dist/lib/browser/profiles.js +122 -37
  47. package/dist/lib/browser/service.d.ts +91 -13
  48. package/dist/lib/browser/service.js +767 -132
  49. package/dist/lib/browser/types.d.ts +91 -3
  50. package/dist/lib/browser/types.js +16 -0
  51. package/dist/lib/cloud/rush.d.ts +28 -1
  52. package/dist/lib/cloud/rush.js +69 -14
  53. package/dist/lib/cloud/store.js +2 -2
  54. package/dist/lib/commands.d.ts +1 -15
  55. package/dist/lib/commands.js +11 -7
  56. package/dist/lib/daemon.js +2 -3
  57. package/dist/lib/doctor-diff.js +4 -4
  58. package/dist/lib/events.js +2 -2
  59. package/dist/lib/hooks.d.ts +11 -7
  60. package/dist/lib/hooks.js +138 -49
  61. package/dist/lib/migrate.d.ts +1 -1
  62. package/dist/lib/migrate.js +1237 -22
  63. package/dist/lib/models.js +2 -2
  64. package/dist/lib/permissions.d.ts +8 -66
  65. package/dist/lib/permissions.js +18 -18
  66. package/dist/lib/plugins.d.ts +94 -24
  67. package/dist/lib/plugins.js +702 -123
  68. package/dist/lib/pty-server.js +9 -10
  69. package/dist/lib/resource-patterns.d.ts +41 -0
  70. package/dist/lib/resource-patterns.js +82 -0
  71. package/dist/lib/resources/hooks.d.ts +5 -1
  72. package/dist/lib/resources/hooks.js +21 -4
  73. package/dist/lib/resources/index.d.ts +17 -0
  74. package/dist/lib/resources/index.js +7 -0
  75. package/dist/lib/resources/types.d.ts +1 -1
  76. package/dist/lib/resources/workflows.d.ts +24 -0
  77. package/dist/lib/resources/workflows.js +110 -0
  78. package/dist/lib/resources.d.ts +6 -1
  79. package/dist/lib/resources.js +12 -2
  80. package/dist/lib/rotate.js +3 -4
  81. package/dist/lib/session/active.d.ts +3 -0
  82. package/dist/lib/session/active.js +92 -6
  83. package/dist/lib/session/cloud.js +2 -2
  84. package/dist/lib/session/db.d.ts +18 -0
  85. package/dist/lib/session/db.js +109 -5
  86. package/dist/lib/session/discover.d.ts +6 -0
  87. package/dist/lib/session/discover.js +55 -29
  88. package/dist/lib/session/team-filter.js +2 -2
  89. package/dist/lib/shims.d.ts +4 -52
  90. package/dist/lib/shims.js +23 -15
  91. package/dist/lib/skills.js +6 -2
  92. package/dist/lib/sqlite.js +10 -4
  93. package/dist/lib/state.d.ts +101 -16
  94. package/dist/lib/state.js +179 -31
  95. package/dist/lib/subagents.d.ts +28 -0
  96. package/dist/lib/subagents.js +98 -1
  97. package/dist/lib/sync-manifest.d.ts +1 -1
  98. package/dist/lib/sync-manifest.js +3 -3
  99. package/dist/lib/teams/persistence.js +15 -5
  100. package/dist/lib/teams/registry.js +2 -2
  101. package/dist/lib/types.d.ts +75 -17
  102. package/dist/lib/types.js +3 -3
  103. package/dist/lib/usage.js +2 -2
  104. package/dist/lib/versions.d.ts +3 -0
  105. package/dist/lib/versions.js +158 -47
  106. package/dist/lib/workflows.d.ts +79 -0
  107. package/dist/lib/workflows.js +233 -0
  108. package/package.json +1 -5
  109. package/scripts/postinstall.js +60 -59
  110. package/dist/commands/fork.d.ts +0 -10
  111. package/dist/commands/fork.js +0 -146
@@ -1,11 +1,11 @@
1
1
  import * as net from 'net';
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
- import { getAgentsDir } from '../state.js';
4
+ import { getHelpersDir } from '../state.js';
5
5
  import { startDaemon } from '../daemon.js';
6
6
  const SOCKET_NAME = 'browser.sock';
7
7
  export function getSocketPath() {
8
- return path.join(getAgentsDir(), SOCKET_NAME);
8
+ return path.join(getHelpersDir(), SOCKET_NAME);
9
9
  }
10
10
  export class BrowserIPCServer {
11
11
  server = null;
@@ -67,8 +67,30 @@ export class BrowserIPCServer {
67
67
  if (!request.profile) {
68
68
  return { ok: false, error: 'Profile required' };
69
69
  }
70
- const result = await this.service.start(request.profile, request.task);
71
- return { ok: true, task: result.task, windowTargetId: result.windowTargetId };
70
+ const result = await this.service.start(request.profile, {
71
+ taskName: request.taskName,
72
+ url: request.url,
73
+ });
74
+ return {
75
+ ok: true,
76
+ task: result.name,
77
+ tabId: result.tabId,
78
+ windowTargetId: result.windowId,
79
+ };
80
+ }
81
+ case 'launch-profile': {
82
+ if (!request.profile) {
83
+ return { ok: false, error: 'Profile required' };
84
+ }
85
+ const result = await this.service.launchProfile(request.profile);
86
+ return { ok: true, port: result.port, pid: result.pid };
87
+ }
88
+ case 'done': {
89
+ if (!request.task) {
90
+ return { ok: false, error: 'Task required' };
91
+ }
92
+ const result = await this.service.done(request.task);
93
+ return { ok: result.ok, error: result.ok ? undefined : 'Task not found' };
72
94
  }
73
95
  case 'stop': {
74
96
  if (request.task) {
@@ -85,6 +107,10 @@ export class BrowserIPCServer {
85
107
  const profiles = await this.service.status(request.profile);
86
108
  return { ok: true, profiles };
87
109
  }
110
+ case 'history': {
111
+ const history = await this.service.getHistory(request.limit ?? 10);
112
+ return { ok: true, history };
113
+ }
88
114
  case 'navigate': {
89
115
  if (!request.task || !request.url) {
90
116
  return { ok: false, error: 'Task and URL required' };
@@ -92,20 +118,37 @@ export class BrowserIPCServer {
92
118
  const result = await this.service.navigate(request.task, request.url, request.profile);
93
119
  return { ok: true, tabId: result.tabId };
94
120
  }
95
- case 'tabs': {
96
- const tabs = await this.service.tabs(request.task, request.profile);
97
- return { ok: true, tabs };
121
+ case 'tab-add': {
122
+ if (!request.task || !request.url) {
123
+ return { ok: false, error: 'Task and URL required' };
124
+ }
125
+ const result = await this.service.tabAdd(request.task, request.url, request.profile);
126
+ return { ok: true, tabId: result.tabId };
98
127
  }
99
- case 'close': {
128
+ case 'tab-focus': {
129
+ if (!request.task || !request.tabId) {
130
+ return { ok: false, error: 'Task and tabId required' };
131
+ }
132
+ const result = await this.service.tabFocus(request.task, request.tabId);
133
+ return { ok: true, tabId: result.tabId };
134
+ }
135
+ case 'tab-close': {
100
136
  if (!request.task) {
101
137
  return { ok: false, error: 'Task required' };
102
138
  }
103
- await this.service.close(request.task, request.tabId);
139
+ await this.service.tabClose(request.task, request.tabId);
104
140
  return { ok: true };
105
141
  }
142
+ case 'tab-list': {
143
+ if (!request.task) {
144
+ return { ok: false, error: 'Task required' };
145
+ }
146
+ const tabs = await this.service.tabList(request.task);
147
+ return { ok: true, tabs: tabs.map(t => ({ id: t.id, url: t.url, title: t.title, task: request.task })) };
148
+ }
106
149
  case 'evaluate': {
107
- if (!request.task || request.tabId === undefined || !request.expr) {
108
- return { ok: false, error: 'Task, tabId, and expression required' };
150
+ if (!request.task || !request.expr) {
151
+ return { ok: false, error: 'Task and expression required' };
109
152
  }
110
153
  const result = await this.service.evaluate(request.task, request.tabId, request.expr);
111
154
  return { ok: true, result };
@@ -128,33 +171,125 @@ export class BrowserIPCServer {
128
171
  return { ok: true, refs };
129
172
  }
130
173
  case 'click': {
131
- if (!request.task || !request.tabId || request.ref === undefined) {
132
- return { ok: false, error: 'Task, tabId, and ref required' };
174
+ if (!request.task || request.ref === undefined) {
175
+ return { ok: false, error: 'Task and ref required' };
133
176
  }
134
- await this.service.click(request.task, request.tabId, request.ref);
177
+ await this.service.click(request.task, request.ref, request.tabId);
135
178
  return { ok: true };
136
179
  }
137
180
  case 'type': {
138
- if (!request.task || !request.tabId || request.ref === undefined || !request.text) {
139
- return { ok: false, error: 'Task, tabId, ref, and text required' };
181
+ if (!request.task || request.ref === undefined || !request.text) {
182
+ return { ok: false, error: 'Task, ref, and text required' };
140
183
  }
141
- await this.service.type(request.task, request.tabId, request.ref, request.text);
184
+ await this.service.type(request.task, request.ref, request.text, request.tabId);
142
185
  return { ok: true };
143
186
  }
144
187
  case 'press': {
145
- if (!request.task || !request.tabId || !request.key) {
146
- return { ok: false, error: 'Task, tabId, and key required' };
188
+ if (!request.task || !request.key) {
189
+ return { ok: false, error: 'Task and key required' };
147
190
  }
148
- await this.service.press(request.task, request.tabId, request.key);
191
+ await this.service.press(request.task, request.key, request.tabId);
149
192
  return { ok: true };
150
193
  }
151
194
  case 'hover': {
152
- if (!request.task || !request.tabId || request.ref === undefined) {
153
- return { ok: false, error: 'Task, tabId, and ref required' };
195
+ if (!request.task || request.ref === undefined) {
196
+ return { ok: false, error: 'Task and ref required' };
197
+ }
198
+ await this.service.hover(request.task, request.ref, request.tabId);
199
+ return { ok: true };
200
+ }
201
+ case 'scroll': {
202
+ if (!request.task) {
203
+ return { ok: false, error: 'Task required' };
204
+ }
205
+ await this.service.scroll(request.task, request.scrollX ?? 0, request.scrollY ?? 0, request.scrollAtX, request.scrollAtY, request.tabId);
206
+ return { ok: true };
207
+ }
208
+ case 'set-viewport': {
209
+ if (!request.task || !request.width || !request.height) {
210
+ return { ok: false, error: 'Task, width, and height required' };
211
+ }
212
+ await this.service.setViewport(request.task, request.width, request.height, {
213
+ mobile: request.mobile,
214
+ deviceScaleFactor: request.deviceScaleFactor,
215
+ tabHint: request.tabId,
216
+ });
217
+ return { ok: true };
218
+ }
219
+ case 'set-device': {
220
+ if (!request.task || !request.deviceName) {
221
+ return { ok: false, error: 'Task and device name required' };
154
222
  }
155
- await this.service.hover(request.task, request.tabId, request.ref);
223
+ await this.service.setDevice(request.task, request.deviceName, request.tabId);
156
224
  return { ok: true };
157
225
  }
226
+ case 'console': {
227
+ if (!request.task) {
228
+ return { ok: false, error: 'Task required' };
229
+ }
230
+ const logs = await this.service.getConsoleLogs(request.task, {
231
+ level: request.level,
232
+ clear: request.clear,
233
+ tabHint: request.tabId,
234
+ });
235
+ return { ok: true, logs };
236
+ }
237
+ case 'errors': {
238
+ if (!request.task) {
239
+ return { ok: false, error: 'Task required' };
240
+ }
241
+ const errors = await this.service.getErrors(request.task, {
242
+ clear: request.clear,
243
+ tabHint: request.tabId,
244
+ });
245
+ return { ok: true, errors };
246
+ }
247
+ case 'requests': {
248
+ if (!request.task) {
249
+ return { ok: false, error: 'Task required' };
250
+ }
251
+ const requests = await this.service.getNetworkRequests(request.task, {
252
+ filter: request.filter,
253
+ clear: request.clear,
254
+ tabHint: request.tabId,
255
+ });
256
+ return { ok: true, requests };
257
+ }
258
+ case 'response-body': {
259
+ if (!request.task || !request.urlPattern) {
260
+ return { ok: false, error: 'Task and URL pattern required' };
261
+ }
262
+ const body = await this.service.getResponseBody(request.task, request.urlPattern, {
263
+ timeout: request.timeout,
264
+ maxChars: request.maxChars,
265
+ tabHint: request.tabId,
266
+ });
267
+ return { ok: true, body };
268
+ }
269
+ case 'wait': {
270
+ if (!request.task || !request.waitType || request.waitValue === undefined) {
271
+ return { ok: false, error: 'Task, wait type, and wait value required' };
272
+ }
273
+ await this.service.wait(request.task, request.waitType, request.waitValue, {
274
+ timeout: request.timeout,
275
+ tabHint: request.tabId,
276
+ });
277
+ return { ok: true };
278
+ }
279
+ case 'set-download-path': {
280
+ if (!request.task || !request.downloadPath) {
281
+ return { ok: false, error: 'Task and download path required' };
282
+ }
283
+ await this.service.setDownloadPath(request.task, request.downloadPath, request.tabId);
284
+ return { ok: true };
285
+ }
286
+ case 'wait-download': {
287
+ if (!request.task) {
288
+ return { ok: false, error: 'Task required' };
289
+ }
290
+ const downloadPath = await this.service.waitForDownload(request.task, request.timeout);
291
+ return { ok: true, downloadPath };
292
+ }
158
293
  default:
159
294
  return { ok: false, error: `Unknown action: ${request.action}` };
160
295
  }
@@ -1,11 +1,19 @@
1
1
  import type { BrowserProfile } from './types.js';
2
2
  export type { BrowserProfile } from './types.js';
3
- export declare function getBrowserProfilesDir(): string;
4
3
  export declare function getBrowserRuntimeDir(): string;
5
- export declare function getProfilePath(name: string): string;
6
4
  export declare function getProfileRuntimeDir(name: string): string;
7
5
  export declare function listProfiles(): Promise<BrowserProfile[]>;
8
6
  export declare function getProfile(name: string): Promise<BrowserProfile | null>;
7
+ /**
8
+ * Find a port in 9222–9399 that is not already claimed by another profile
9
+ * and is not currently in use by any OS process.
10
+ */
11
+ export declare function findFreeProfilePort(): Promise<number>;
9
12
  export declare function createProfile(profile: BrowserProfile): Promise<void>;
10
13
  export declare function updateProfile(profile: BrowserProfile): Promise<void>;
11
14
  export declare function deleteProfile(name: string): Promise<void>;
15
+ /**
16
+ * Extract the port intended by the profile's first endpoint.
17
+ * Returns undefined for endpoint shapes that don't carry a port (e.g. ws:// without one).
18
+ */
19
+ export declare function extractConfiguredPort(profile: BrowserProfile): number | undefined;
@@ -1,61 +1,146 @@
1
- import * as fs from 'fs';
2
1
  import * as path from 'path';
3
- import * as os from 'os';
4
- import * as yaml from 'yaml';
5
- import { getUserAgentsDir } from '../state.js';
6
- export function getBrowserProfilesDir() {
7
- return path.join(getUserAgentsDir(), 'browser', 'profiles');
8
- }
2
+ import { execSync } from 'child_process';
3
+ import { getBrowserRuntimeDir as getBrowserRuntimeDirRoot, readMeta, writeMeta, } from '../state.js';
4
+ import { findBrowserPath } from './chrome.js';
9
5
  export function getBrowserRuntimeDir() {
10
- const agentsDir = path.join(os.homedir(), '.agents');
11
- return path.join(agentsDir, 'browser');
12
- }
13
- export function getProfilePath(name) {
14
- return path.join(getBrowserProfilesDir(), `${name}.yaml`);
6
+ return getBrowserRuntimeDirRoot();
15
7
  }
16
8
  export function getProfileRuntimeDir(name) {
17
9
  return path.join(getBrowserRuntimeDir(), name);
18
10
  }
11
+ function configToProfile(name, config) {
12
+ return {
13
+ name,
14
+ description: config.description,
15
+ browser: config.browser,
16
+ binary: config.binary,
17
+ electron: config.electron,
18
+ endpoints: config.endpoints,
19
+ chrome: config.chrome,
20
+ secrets: config.secrets,
21
+ viewport: config.viewport,
22
+ };
23
+ }
24
+ function profileToConfig(profile) {
25
+ const config = {
26
+ browser: profile.browser,
27
+ endpoints: profile.endpoints,
28
+ };
29
+ if (profile.description)
30
+ config.description = profile.description;
31
+ if (profile.binary)
32
+ config.binary = profile.binary;
33
+ if (profile.electron)
34
+ config.electron = profile.electron;
35
+ if (profile.chrome)
36
+ config.chrome = profile.chrome;
37
+ if (profile.secrets)
38
+ config.secrets = profile.secrets;
39
+ if (profile.viewport)
40
+ config.viewport = profile.viewport;
41
+ return config;
42
+ }
19
43
  export async function listProfiles() {
20
- const dir = getBrowserProfilesDir();
21
- if (!fs.existsSync(dir))
44
+ const meta = readMeta();
45
+ if (!meta.browser)
22
46
  return [];
23
- const files = fs.readdirSync(dir).filter((f) => f.endsWith('.yaml'));
24
- const profiles = [];
25
- for (const file of files) {
26
- const content = fs.readFileSync(path.join(dir, file), 'utf-8');
27
- const profile = yaml.parse(content);
28
- profiles.push(profile);
29
- }
30
- return profiles;
47
+ return Object.entries(meta.browser).map(([name, config]) => configToProfile(name, config));
31
48
  }
32
49
  export async function getProfile(name) {
33
- const filePath = getProfilePath(name);
34
- if (!fs.existsSync(filePath))
50
+ const meta = readMeta();
51
+ const config = meta.browser?.[name];
52
+ if (!config)
35
53
  return null;
36
- const content = fs.readFileSync(filePath, 'utf-8');
37
- return yaml.parse(content);
54
+ return configToProfile(name, config);
55
+ }
56
+ /**
57
+ * Find a port in 9222–9399 that is not already claimed by another profile
58
+ * and is not currently in use by any OS process.
59
+ */
60
+ export async function findFreeProfilePort() {
61
+ const profiles = await listProfiles();
62
+ const usedByProfile = new Set();
63
+ for (const p of profiles) {
64
+ const port = extractConfiguredPort(p);
65
+ if (port !== undefined)
66
+ usedByProfile.add(port);
67
+ }
68
+ for (let port = 9222; port <= 9399; port++) {
69
+ if (usedByProfile.has(port))
70
+ continue;
71
+ try {
72
+ execSync(`lsof -i :${port}`, { stdio: 'ignore' });
73
+ // lsof succeeded → something is listening → port is in use
74
+ }
75
+ catch {
76
+ // lsof threw → nothing on this port → it's free
77
+ return port;
78
+ }
79
+ }
80
+ throw new Error('No available ports in range 9222-9399');
38
81
  }
39
82
  export async function createProfile(profile) {
40
- const dir = getBrowserProfilesDir();
41
- fs.mkdirSync(dir, { recursive: true });
42
- const filePath = getProfilePath(profile.name);
43
- if (fs.existsSync(filePath)) {
83
+ const meta = readMeta();
84
+ if (meta.browser?.[profile.name]) {
44
85
  throw new Error(`Profile "${profile.name}" already exists`);
45
86
  }
46
- fs.writeFileSync(filePath, yaml.stringify(profile), 'utf-8');
87
+ // Check for port collision with existing profiles
88
+ const newPort = extractConfiguredPort(profile);
89
+ if (newPort !== undefined && meta.browser) {
90
+ for (const [existingName, existingConfig] of Object.entries(meta.browser)) {
91
+ const existingProfile = configToProfile(existingName, existingConfig);
92
+ const existingPort = extractConfiguredPort(existingProfile);
93
+ if (existingPort === newPort) {
94
+ throw new Error(`Port ${newPort} is already used by profile "${existingName}". ` +
95
+ `Each profile must own a unique port. Use a different port or omit --endpoint to auto-assign.`);
96
+ }
97
+ }
98
+ }
99
+ // Resolve the browser binary at create time. Fails fast with an actionable
100
+ // error ("Comet not installed at /Applications/Comet.app") rather than
101
+ // deferring the failure to the first task. `findBrowserPath` short-circuits
102
+ // for browser=custom without a binary by throwing — same outcome.
103
+ findBrowserPath(profile.browser, profile.binary);
104
+ meta.browser = meta.browser ?? {};
105
+ meta.browser[profile.name] = profileToConfig(profile);
106
+ writeMeta(meta);
47
107
  }
48
108
  export async function updateProfile(profile) {
49
- const filePath = getProfilePath(profile.name);
50
- if (!fs.existsSync(filePath)) {
109
+ const meta = readMeta();
110
+ if (!meta.browser?.[profile.name]) {
51
111
  throw new Error(`Profile "${profile.name}" does not exist`);
52
112
  }
53
- fs.writeFileSync(filePath, yaml.stringify(profile), 'utf-8');
113
+ meta.browser[profile.name] = profileToConfig(profile);
114
+ writeMeta(meta);
54
115
  }
55
116
  export async function deleteProfile(name) {
56
- const filePath = getProfilePath(name);
57
- if (!fs.existsSync(filePath)) {
117
+ const meta = readMeta();
118
+ if (!meta.browser?.[name]) {
58
119
  throw new Error(`Profile "${name}" does not exist`);
59
120
  }
60
- fs.unlinkSync(filePath);
121
+ delete meta.browser[name];
122
+ writeMeta(meta);
123
+ }
124
+ /**
125
+ * Extract the port intended by the profile's first endpoint.
126
+ * Returns undefined for endpoint shapes that don't carry a port (e.g. ws:// without one).
127
+ */
128
+ export function extractConfiguredPort(profile) {
129
+ const endpoint = profile.endpoints[0];
130
+ if (!endpoint)
131
+ return undefined;
132
+ let url;
133
+ try {
134
+ url = new URL(endpoint);
135
+ }
136
+ catch {
137
+ return undefined;
138
+ }
139
+ if (url.port)
140
+ return parseInt(url.port, 10);
141
+ if (url.protocol === 'cdp:')
142
+ return 9222;
143
+ if (url.protocol === 'ssh:')
144
+ return 9222;
145
+ return undefined;
61
146
  }
@@ -1,13 +1,36 @@
1
- import { type TabInfo, type ProfileStatus } from './types.js';
1
+ import { type TabInfo, type ProfileStatus, type HistoricalTask } from './types.js';
2
2
  import { type RefOpts, type RefNode } from './refs.js';
3
3
  export declare class BrowserService {
4
4
  private connections;
5
5
  private forkingProfiles;
6
- start(profileName: string, taskId?: string): Promise<{
6
+ private consoleLogs;
7
+ private pageErrors;
8
+ private networkRequests;
9
+ private pendingDownloads;
10
+ private enabledSessions;
11
+ start(profileName: string, opts?: {
12
+ taskName?: string;
13
+ url?: string;
14
+ }): Promise<{
7
15
  task: string;
8
- windowTargetId?: string;
16
+ name: string;
17
+ tabId?: string;
18
+ windowId?: string;
9
19
  }>;
10
- stop(taskId: string): Promise<{
20
+ /**
21
+ * Launch (or attach to) the profile's browser without creating a task. Used by
22
+ * `agents browser profiles launch <name>` so users can warm up the browser —
23
+ * including the first-run onboarding flow — before any automation starts.
24
+ */
25
+ launchProfile(profileName: string): Promise<{
26
+ port: number;
27
+ pid: number;
28
+ }>;
29
+ stop(taskName: string): Promise<{
30
+ ok: boolean;
31
+ profile?: string;
32
+ }>;
33
+ done(taskName: string): Promise<{
11
34
  ok: boolean;
12
35
  profile?: string;
13
36
  }>;
@@ -15,28 +38,81 @@ export declare class BrowserService {
15
38
  navigate(taskId: string, url: string, profileName?: string): Promise<{
16
39
  tabId: string;
17
40
  url: string;
41
+ created: boolean;
42
+ }>;
43
+ tabAdd(taskId: string, url: string, profileName?: string): Promise<{
44
+ tabId: string;
45
+ url: string;
18
46
  }>;
47
+ tabFocus(taskId: string, tabHint: string): Promise<{
48
+ tabId: string;
49
+ }>;
50
+ tabList(taskId: string): Promise<Array<{
51
+ id: string;
52
+ url: string;
53
+ title: string;
54
+ current: boolean;
55
+ }>>;
56
+ private resolveTabHint;
57
+ private resolveCurrentTab;
58
+ private getCdpTargetId;
19
59
  tabs(taskId?: string, profileName?: string): Promise<TabInfo[]>;
20
- close(taskId: string, tabId?: string): Promise<void>;
21
- evaluate(taskId: string, tabId: string, expression: string): Promise<unknown>;
22
- screenshot(taskId: string, tabId?: string, outputPath?: string): Promise<string>;
60
+ tabClose(taskId: string, tabHint?: string): Promise<void>;
61
+ evaluate(taskId: string, tabHint: string | undefined, expression: string): Promise<unknown>;
62
+ screenshot(taskId: string, tabHint?: string, outputPath?: string): Promise<string>;
23
63
  private refsCache;
24
- refs(taskId: string, tabId?: string, opts?: RefOpts): Promise<{
64
+ refs(taskId: string, tabHint?: string, opts?: RefOpts): Promise<{
25
65
  refs: string;
26
66
  nodeMap: Map<number, RefNode>;
27
67
  }>;
28
- click(taskId: string, tabId: string, ref: number): Promise<void>;
29
- type(taskId: string, tabId: string, ref: number, text: string): Promise<void>;
30
- press(taskId: string, tabId: string, key: string): Promise<void>;
31
- hover(taskId: string, tabId: string, ref: number): Promise<void>;
68
+ click(taskId: string, ref: number, tabHint?: string): Promise<void>;
69
+ type(taskId: string, ref: number, text: string, tabHint?: string): Promise<void>;
70
+ press(taskId: string, key: string, tabHint?: string): Promise<void>;
71
+ hover(taskId: string, ref: number, tabHint?: string): Promise<void>;
72
+ scroll(taskId: string, deltaX: number, deltaY: number, atX?: number, atY?: number, tabHint?: string): Promise<void>;
32
73
  status(profileName?: string): Promise<ProfileStatus[]>;
74
+ private reconcileFromDisk;
75
+ setViewport(taskId: string, width: number, height: number, options?: {
76
+ mobile?: boolean;
77
+ deviceScaleFactor?: number;
78
+ tabHint?: string;
79
+ }): Promise<void>;
80
+ setDevice(taskId: string, deviceName: string, tabHint?: string): Promise<void>;
81
+ private enableRuntimeForSession;
82
+ getConsoleLogs(taskId: string, options?: {
83
+ level?: string;
84
+ clear?: boolean;
85
+ tabHint?: string;
86
+ }): Promise<import('./types.js').ConsoleEntry[]>;
87
+ getErrors(taskId: string, options?: {
88
+ clear?: boolean;
89
+ tabHint?: string;
90
+ }): Promise<import('./types.js').ErrorEntry[]>;
91
+ private enableNetworkForSession;
92
+ getNetworkRequests(taskId: string, options?: {
93
+ filter?: string;
94
+ clear?: boolean;
95
+ tabHint?: string;
96
+ }): Promise<import('./types.js').NetworkRequest[]>;
97
+ getResponseBody(taskId: string, urlPattern: string, options?: {
98
+ timeout?: number;
99
+ maxChars?: number;
100
+ tabHint?: string;
101
+ }): Promise<string>;
102
+ wait(taskId: string, type: 'time' | 'selector' | 'url' | 'function' | 'load', value: string | number, options?: {
103
+ timeout?: number;
104
+ tabHint?: string;
105
+ }): Promise<void>;
106
+ setDownloadPath(taskId: string, downloadPath: string, tabHint?: string): Promise<void>;
107
+ waitForDownload(taskId: string, timeout?: number): Promise<string>;
108
+ private findTaskBySession;
33
109
  shutdown(): Promise<void>;
34
110
  private findAvailableFork;
35
111
  private forkElectronProfile;
36
112
  private connectProfile;
37
113
  private connectEndpoint;
38
114
  private enableDomains;
39
- private createTaskWindow;
115
+ private getOrCreateWindow;
40
116
  private findTask;
41
117
  private getTabsForTask;
42
118
  private getProfileStatus;
@@ -45,4 +121,6 @@ export declare class BrowserService {
45
121
  private invalidateTargetCache;
46
122
  private saveTaskState;
47
123
  private loadTaskState;
124
+ private saveToHistory;
125
+ getHistory(limit?: number): Promise<HistoricalTask[]>;
48
126
  }