@phi-code-admin/browser 1.0.1 → 1.0.2

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/dist/index.d.ts CHANGED
@@ -30,11 +30,21 @@ export declare function closeAll(): Promise<void>;
30
30
  export interface CreateTabResult {
31
31
  tabId: string;
32
32
  userId: string;
33
+ sessionKey: string;
33
34
  url?: string;
34
35
  }
35
- /** Open a new browser tab. Returns the tab id used by the other tools. */
36
- export declare function createTab(options: {
36
+ /**
37
+ * Open a new browser tab. Returns the tab id used by the other tools.
38
+ *
39
+ * The camofox-browser REST contract requires every tab to be associated
40
+ * with a `userId` (logical user) AND a `sessionKey` (logical session
41
+ * inside that user — used to group tabs that should share cookies /
42
+ * fingerprints / proxies). Phi-code's chat agents only need one of each,
43
+ * so both default to a constant sentinel when omitted.
44
+ */
45
+ export declare function createTab(options?: {
37
46
  userId?: string;
47
+ sessionKey?: string;
38
48
  url?: string;
39
49
  viewport?: {
40
50
  width: number;
@@ -56,6 +66,7 @@ export declare function navigate(options: {
56
66
  url: string;
57
67
  tabId?: string;
58
68
  userId?: string;
69
+ sessionKey?: string;
59
70
  waitUntil?: "load" | "domcontentloaded" | "networkidle";
60
71
  timeoutMs?: number;
61
72
  }): Promise<NavigateResult>;
@@ -65,6 +76,7 @@ export declare function navigate(options: {
65
76
  */
66
77
  export declare function snapshot(options: {
67
78
  tabId: string;
79
+ userId?: string;
68
80
  }): Promise<unknown>;
69
81
  export interface ExtractResult {
70
82
  url?: string;
@@ -82,6 +94,7 @@ export interface ExtractResult {
82
94
  export declare function extract(options: {
83
95
  tabId?: string;
84
96
  userId?: string;
97
+ sessionKey?: string;
85
98
  url?: string;
86
99
  mode?: "readability" | "html" | "text";
87
100
  }): Promise<ExtractResult>;
@@ -93,6 +106,7 @@ export interface ScreenshotResult {
93
106
  /** Capture a screenshot of the given tab as a base64-encoded PNG. */
94
107
  export declare function screenshot(options: {
95
108
  tabId: string;
109
+ userId?: string;
96
110
  fullPage?: boolean;
97
111
  clip?: {
98
112
  x: number;
@@ -110,10 +124,12 @@ export declare function search(options: {
110
124
  query: string;
111
125
  engine?: "google" | "duckduckgo" | "bing";
112
126
  userId?: string;
127
+ sessionKey?: string;
113
128
  }): Promise<ExtractResult>;
114
129
  /** Click an element by ref (from `snapshot`) or CSS selector. */
115
130
  export declare function click(options: {
116
131
  tabId: string;
132
+ userId?: string;
117
133
  ref?: string;
118
134
  selector?: string;
119
135
  button?: "left" | "right" | "middle";
@@ -123,6 +139,7 @@ export declare function click(options: {
123
139
  /** Type text into a focused element (or one targeted via ref/selector). */
124
140
  export declare function type(options: {
125
141
  tabId: string;
142
+ userId?: string;
126
143
  text: string;
127
144
  ref?: string;
128
145
  selector?: string;
@@ -134,6 +151,7 @@ export declare function type(options: {
134
151
  /** Scroll the page or a specific element by ref. */
135
152
  export declare function scroll(options: {
136
153
  tabId: string;
154
+ userId?: string;
137
155
  direction: "up" | "down" | "left" | "right";
138
156
  ref?: string;
139
157
  pixels?: number;
@@ -143,6 +161,7 @@ export declare function scroll(options: {
143
161
  /** Close a single tab. The underlying browser context is kept warm. */
144
162
  export declare function closeTab(options: {
145
163
  tabId: string;
164
+ userId?: string;
146
165
  }): Promise<{
147
166
  tabId: string;
148
167
  }>;
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ let serverProcess = null;
26
26
  let serverPort = null;
27
27
  let bootPromise = null;
28
28
  const DEFAULT_USER_ID = "phi-default";
29
+ const DEFAULT_SESSION_KEY = "phi-default-session";
29
30
  const HEALTH_TIMEOUT_MS = 30_000;
30
31
  const HEALTH_POLL_INTERVAL_MS = 250;
31
32
  async function findAvailablePort() {
@@ -243,16 +244,25 @@ async function request(pathname, options = {}) {
243
244
  clearTimeout(timeout);
244
245
  }
245
246
  }
246
- /** Open a new browser tab. Returns the tab id used by the other tools. */
247
- export async function createTab(options) {
247
+ /**
248
+ * Open a new browser tab. Returns the tab id used by the other tools.
249
+ *
250
+ * The camofox-browser REST contract requires every tab to be associated
251
+ * with a `userId` (logical user) AND a `sessionKey` (logical session
252
+ * inside that user — used to group tabs that should share cookies /
253
+ * fingerprints / proxies). Phi-code's chat agents only need one of each,
254
+ * so both default to a constant sentinel when omitted.
255
+ */
256
+ export async function createTab(options = {}) {
248
257
  const userId = options.userId ?? DEFAULT_USER_ID;
249
- const body = { userId };
258
+ const sessionKey = options.sessionKey ?? DEFAULT_SESSION_KEY;
259
+ const body = { userId, sessionKey };
250
260
  if (options.url)
251
261
  body.url = options.url;
252
262
  if (options.viewport)
253
263
  body.viewport = options.viewport;
254
264
  const res = await request("/tabs", { method: "POST", body });
255
- return { tabId: res.tabId, userId, url: options.url };
265
+ return { tabId: res.tabId, userId, sessionKey, url: options.url };
256
266
  }
257
267
  /**
258
268
  * Navigate the given tab (or a freshly opened one) to a URL.
@@ -262,11 +272,19 @@ export async function createTab(options) {
262
272
  export async function navigate(options) {
263
273
  let tabId = options.tabId;
264
274
  if (!tabId) {
265
- const tab = await createTab({ userId: options.userId, url: options.url });
275
+ const tab = await createTab({
276
+ userId: options.userId,
277
+ sessionKey: options.sessionKey,
278
+ url: options.url,
279
+ });
266
280
  tabId = tab.tabId;
267
281
  return { tabId, url: options.url };
268
282
  }
269
- const body = { url: options.url };
283
+ const body = {
284
+ userId: options.userId ?? DEFAULT_USER_ID,
285
+ sessionKey: options.sessionKey ?? DEFAULT_SESSION_KEY,
286
+ url: options.url,
287
+ };
270
288
  if (options.waitUntil)
271
289
  body.waitUntil = options.waitUntil;
272
290
  if (options.timeoutMs)
@@ -279,7 +297,9 @@ export async function navigate(options) {
279
297
  * Refs returned here can be used with `click`/`type`/`scroll`.
280
298
  */
281
299
  export async function snapshot(options) {
282
- return await request(`/tabs/${encodeURIComponent(options.tabId)}/snapshot`);
300
+ const userId = options.userId ?? DEFAULT_USER_ID;
301
+ const qs = `?userId=${encodeURIComponent(userId)}`;
302
+ return await request(`/tabs/${encodeURIComponent(options.tabId)}/snapshot${qs}`);
283
303
  }
284
304
  /**
285
305
  * Extract the readable content of the current page (Readability-style).
@@ -292,32 +312,45 @@ export async function extract(options) {
292
312
  if (!options.url) {
293
313
  throw new Error("extract() requires either tabId or url");
294
314
  }
295
- const tab = await createTab({ userId: options.userId, url: options.url });
315
+ const tab = await createTab({
316
+ userId: options.userId,
317
+ sessionKey: options.sessionKey,
318
+ url: options.url,
319
+ });
296
320
  tabId = tab.tabId;
297
321
  // Wait for the navigation to settle before extracting.
298
322
  await request(`/tabs/${encodeURIComponent(tabId)}/wait`, {
299
323
  method: "POST",
300
- body: { event: "load" },
324
+ body: { userId: options.userId ?? DEFAULT_USER_ID, event: "load" },
301
325
  }).catch(() => { });
302
326
  }
303
327
  else if (options.url) {
304
- await navigate({ tabId, url: options.url });
328
+ await navigate({
329
+ tabId,
330
+ url: options.url,
331
+ userId: options.userId,
332
+ sessionKey: options.sessionKey,
333
+ });
305
334
  }
306
335
  const res = await request(`/tabs/${encodeURIComponent(tabId)}/extract`, {
307
336
  method: "POST",
308
- body: { mode: options.mode ?? "readability" },
337
+ body: {
338
+ userId: options.userId ?? DEFAULT_USER_ID,
339
+ sessionKey: options.sessionKey ?? DEFAULT_SESSION_KEY,
340
+ mode: options.mode ?? "readability",
341
+ },
309
342
  });
310
343
  return res;
311
344
  }
312
345
  /** Capture a screenshot of the given tab as a base64-encoded PNG. */
313
346
  export async function screenshot(options) {
314
347
  const query = new URLSearchParams();
348
+ query.set("userId", options.userId ?? DEFAULT_USER_ID);
315
349
  if (options.fullPage)
316
350
  query.set("fullPage", "1");
317
351
  if (options.clip)
318
352
  query.set("clip", JSON.stringify(options.clip));
319
- const qs = query.toString();
320
- const res = await request(`/tabs/${encodeURIComponent(options.tabId)}/screenshot${qs ? `?${qs}` : ""}`);
353
+ const res = await request(`/tabs/${encodeURIComponent(options.tabId)}/screenshot?${query.toString()}`);
321
354
  return {
322
355
  tabId: options.tabId,
323
356
  mimeType: res.mimeType ?? "image/png",
@@ -336,14 +369,14 @@ export async function search(options) {
336
369
  : engine === "bing"
337
370
  ? `https://www.bing.com/search?q=${encodeURIComponent(options.query)}`
338
371
  : `https://duckduckgo.com/?q=${encodeURIComponent(options.query)}`;
339
- return await extract({ url, userId: options.userId });
372
+ return await extract({ url, userId: options.userId, sessionKey: options.sessionKey });
340
373
  }
341
374
  /** Click an element by ref (from `snapshot`) or CSS selector. */
342
375
  export async function click(options) {
343
376
  if (!options.ref && !options.selector) {
344
377
  throw new Error("click() requires `ref` or `selector`");
345
378
  }
346
- const body = {};
379
+ const body = { userId: options.userId ?? DEFAULT_USER_ID };
347
380
  if (options.ref)
348
381
  body.ref = options.ref;
349
382
  if (options.selector)
@@ -358,7 +391,10 @@ export async function click(options) {
358
391
  }
359
392
  /** Type text into a focused element (or one targeted via ref/selector). */
360
393
  export async function type(options) {
361
- const body = { text: options.text };
394
+ const body = {
395
+ userId: options.userId ?? DEFAULT_USER_ID,
396
+ text: options.text,
397
+ };
362
398
  if (options.ref)
363
399
  body.ref = options.ref;
364
400
  if (options.selector)
@@ -375,7 +411,10 @@ export async function type(options) {
375
411
  }
376
412
  /** Scroll the page or a specific element by ref. */
377
413
  export async function scroll(options) {
378
- const body = { direction: options.direction };
414
+ const body = {
415
+ userId: options.userId ?? DEFAULT_USER_ID,
416
+ direction: options.direction,
417
+ };
379
418
  if (options.ref)
380
419
  body.ref = options.ref;
381
420
  if (options.pixels)
@@ -388,7 +427,9 @@ export async function scroll(options) {
388
427
  }
389
428
  /** Close a single tab. The underlying browser context is kept warm. */
390
429
  export async function closeTab(options) {
391
- await request(`/tabs/${encodeURIComponent(options.tabId)}`, { method: "DELETE" });
430
+ const userId = options.userId ?? DEFAULT_USER_ID;
431
+ const qs = `?userId=${encodeURIComponent(userId)}`;
432
+ await request(`/tabs/${encodeURIComponent(options.tabId)}${qs}`, { method: "DELETE" });
392
433
  return { tabId: options.tabId };
393
434
  }
394
435
  /** List all open tabs for a user. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-code-admin/browser",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Phi-code browser automation API: lazy-start the bundled Camoufox + camofox-browser server and expose 10 high-level tools (navigate, extract, screenshot, click, type, scroll, snapshot, search, close_tab, list_tabs) as plain ES module functions. Zero external dependencies at runtime.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",