@jackwener/opencli 1.7.16 → 1.7.17

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 (149) hide show
  1. package/README.md +8 -9
  2. package/README.zh-CN.md +8 -8
  3. package/cli-manifest.json +97 -271
  4. package/clis/chatgpt/ask.js +1 -1
  5. package/clis/chatgpt/commands.test.js +2 -2
  6. package/clis/chatgpt/detail.js +1 -1
  7. package/clis/chatgpt/history.js +1 -1
  8. package/clis/chatgpt/image.js +38 -4
  9. package/clis/chatgpt/image.test.js +68 -1
  10. package/clis/chatgpt/new.js +1 -1
  11. package/clis/chatgpt/read.js +1 -1
  12. package/clis/chatgpt/send.js +1 -1
  13. package/clis/chatgpt/status.js +1 -1
  14. package/clis/chatgpt/utils.js +208 -16
  15. package/clis/chatgpt/utils.test.js +131 -2
  16. package/clis/claude/ask.js +1 -1
  17. package/clis/claude/detail.js +1 -1
  18. package/clis/claude/history.js +1 -1
  19. package/clis/claude/new.js +1 -1
  20. package/clis/claude/read.js +1 -1
  21. package/clis/claude/send.js +1 -1
  22. package/clis/claude/status.js +1 -1
  23. package/clis/deepseek/ask.js +1 -1
  24. package/clis/deepseek/detail.js +1 -1
  25. package/clis/deepseek/history.js +1 -1
  26. package/clis/deepseek/new.js +1 -1
  27. package/clis/deepseek/read.js +1 -1
  28. package/clis/deepseek/send.js +1 -1
  29. package/clis/deepseek/status.js +1 -1
  30. package/clis/doubao/ask.js +1 -1
  31. package/clis/doubao/detail.js +1 -1
  32. package/clis/doubao/history.js +1 -1
  33. package/clis/doubao/meeting-summary.js +1 -1
  34. package/clis/doubao/meeting-transcript.js +1 -1
  35. package/clis/doubao/new.js +1 -1
  36. package/clis/doubao/read.js +1 -1
  37. package/clis/doubao/send.js +1 -1
  38. package/clis/doubao/status.js +1 -1
  39. package/clis/gemini/ask.js +1 -1
  40. package/clis/gemini/deep-research-result.js +1 -1
  41. package/clis/gemini/deep-research.js +1 -1
  42. package/clis/gemini/image.js +1 -1
  43. package/clis/gemini/new.js +1 -1
  44. package/clis/grok/ask.js +1 -1
  45. package/clis/grok/detail.js +1 -1
  46. package/clis/grok/history.js +1 -1
  47. package/clis/grok/image.js +1 -1
  48. package/clis/grok/new.js +1 -1
  49. package/clis/grok/read.js +1 -1
  50. package/clis/grok/send.js +1 -1
  51. package/clis/grok/status.js +1 -1
  52. package/clis/notebooklm/current.js +1 -1
  53. package/clis/notebooklm/get.js +1 -1
  54. package/clis/notebooklm/history.js +1 -1
  55. package/clis/notebooklm/note-list.js +1 -1
  56. package/clis/notebooklm/notes-get.js +1 -1
  57. package/clis/notebooklm/open.js +2 -2
  58. package/clis/notebooklm/open.test.js +1 -1
  59. package/clis/notebooklm/source-fulltext.js +1 -1
  60. package/clis/notebooklm/source-get.js +1 -1
  61. package/clis/notebooklm/source-guide.js +1 -1
  62. package/clis/notebooklm/source-list.js +1 -1
  63. package/clis/notebooklm/summary.js +1 -1
  64. package/clis/qwen/ask.js +1 -1
  65. package/clis/qwen/detail.js +1 -1
  66. package/clis/qwen/history.js +1 -1
  67. package/clis/qwen/image.js +1 -1
  68. package/clis/qwen/new.js +1 -1
  69. package/clis/qwen/read.js +1 -1
  70. package/clis/qwen/send.js +1 -1
  71. package/clis/qwen/status.js +1 -1
  72. package/clis/reddit/comment.js +1 -1
  73. package/clis/reddit/frontpage.js +1 -1
  74. package/clis/reddit/popular.js +1 -1
  75. package/clis/reddit/read.js +1 -1
  76. package/clis/reddit/read.test.js +2 -2
  77. package/clis/reddit/save.js +1 -1
  78. package/clis/reddit/saved.js +1 -1
  79. package/clis/reddit/search.js +1 -1
  80. package/clis/reddit/subreddit.js +1 -1
  81. package/clis/reddit/subscribe.js +1 -1
  82. package/clis/reddit/upvote.js +1 -1
  83. package/clis/reddit/upvoted.js +1 -1
  84. package/clis/reddit/user-comments.js +1 -1
  85. package/clis/reddit/user-posts.js +1 -1
  86. package/clis/reddit/user.js +1 -1
  87. package/clis/twitter/article.js +1 -1
  88. package/clis/twitter/bookmark-folder.js +1 -1
  89. package/clis/twitter/bookmark-folders.js +1 -1
  90. package/clis/twitter/bookmarks.js +1 -1
  91. package/clis/twitter/download.js +1 -1
  92. package/clis/twitter/followers.js +1 -1
  93. package/clis/twitter/following.js +1 -1
  94. package/clis/twitter/likes.js +1 -1
  95. package/clis/twitter/list-tweets.js +1 -1
  96. package/clis/twitter/lists.js +1 -1
  97. package/clis/twitter/notifications.js +1 -1
  98. package/clis/twitter/profile.js +1 -1
  99. package/clis/twitter/search.js +1 -1
  100. package/clis/twitter/thread.js +1 -1
  101. package/clis/twitter/timeline.js +1 -1
  102. package/clis/twitter/trending.js +1 -1
  103. package/clis/twitter/tweets.js +1 -1
  104. package/clis/yuanbao/ask.js +1 -1
  105. package/clis/yuanbao/detail.js +1 -1
  106. package/clis/yuanbao/history.js +1 -1
  107. package/clis/yuanbao/new.js +1 -1
  108. package/clis/yuanbao/read.js +1 -1
  109. package/clis/yuanbao/send.js +1 -1
  110. package/clis/yuanbao/status.js +1 -1
  111. package/dist/src/browser/bridge.d.ts +3 -1
  112. package/dist/src/browser/bridge.js +3 -1
  113. package/dist/src/browser/cdp.d.ts +3 -1
  114. package/dist/src/browser/daemon-client.d.ts +7 -14
  115. package/dist/src/browser/daemon-client.js +2 -6
  116. package/dist/src/browser/network-cache.d.ts +5 -5
  117. package/dist/src/browser/network-cache.js +8 -8
  118. package/dist/src/browser/network-cache.test.js +4 -4
  119. package/dist/src/browser/page.d.ts +8 -7
  120. package/dist/src/browser/page.js +23 -16
  121. package/dist/src/browser/page.test.js +60 -30
  122. package/dist/src/build-manifest.js +1 -1
  123. package/dist/src/cli.js +60 -162
  124. package/dist/src/cli.test.js +178 -197
  125. package/dist/src/commanderAdapter.js +2 -0
  126. package/dist/src/discovery.js +1 -1
  127. package/dist/src/doctor.d.ts +0 -4
  128. package/dist/src/doctor.js +8 -72
  129. package/dist/src/doctor.test.js +26 -97
  130. package/dist/src/execution.d.ts +1 -0
  131. package/dist/src/execution.js +20 -21
  132. package/dist/src/execution.test.js +27 -31
  133. package/dist/src/help.js +7 -1
  134. package/dist/src/main.js +0 -19
  135. package/dist/src/manifest-types.d.ts +2 -4
  136. package/dist/src/observation/artifact.js +1 -1
  137. package/dist/src/observation/artifact.test.js +3 -3
  138. package/dist/src/observation/events.d.ts +1 -1
  139. package/dist/src/observation/manager.js +1 -1
  140. package/dist/src/observation/manager.test.js +3 -3
  141. package/dist/src/registry-api.d.ts +1 -1
  142. package/dist/src/registry.d.ts +3 -12
  143. package/dist/src/registry.js +6 -10
  144. package/dist/src/runtime.d.ts +7 -2
  145. package/dist/src/runtime.js +3 -1
  146. package/dist/src/serialization.d.ts +1 -1
  147. package/dist/src/serialization.js +1 -1
  148. package/dist/src/types.d.ts +0 -15
  149. package/package.json +1 -1
@@ -6,7 +6,7 @@
6
6
  * stable references to request bodies after running other commands,
7
7
  * so every `browser network` call snapshots its results to disk.
8
8
  *
9
- * Layout: <cacheDir>/browser-network/<workspace>.json
9
+ * Layout: <cacheDir>/browser-network/<session>.json
10
10
  * Entries expire after DEFAULT_TTL_MS (24h).
11
11
  */
12
12
  export declare const DEFAULT_TTL_MS: number;
@@ -29,12 +29,12 @@ export interface CachedNetworkEntry {
29
29
  }
30
30
  export interface NetworkCacheFile {
31
31
  version: 1;
32
- workspace: string;
32
+ session: string;
33
33
  savedAt: string;
34
34
  entries: CachedNetworkEntry[];
35
35
  }
36
- export declare function getCachePath(workspace: string, baseDir?: string): string;
37
- export declare function saveNetworkCache(workspace: string, entries: CachedNetworkEntry[], baseDir?: string): void;
36
+ export declare function getCachePath(session: string, baseDir?: string): string;
37
+ export declare function saveNetworkCache(session: string, entries: CachedNetworkEntry[], baseDir?: string): void;
38
38
  export interface LoadOptions {
39
39
  baseDir?: string;
40
40
  ttlMs?: number;
@@ -45,5 +45,5 @@ export interface LoadResult {
45
45
  file?: NetworkCacheFile;
46
46
  ageMs?: number;
47
47
  }
48
- export declare function loadNetworkCache(workspace: string, opts?: LoadOptions): LoadResult;
48
+ export declare function loadNetworkCache(session: string, opts?: LoadOptions): LoadResult;
49
49
  export declare function findEntry(file: NetworkCacheFile, key: string): CachedNetworkEntry | null;
@@ -6,7 +6,7 @@
6
6
  * stable references to request bodies after running other commands,
7
7
  * so every `browser network` call snapshots its results to disk.
8
8
  *
9
- * Layout: <cacheDir>/browser-network/<workspace>.json
9
+ * Layout: <cacheDir>/browser-network/<session>.json
10
10
  * Entries expire after DEFAULT_TTL_MS (24h).
11
11
  */
12
12
  import * as fs from 'node:fs';
@@ -16,23 +16,23 @@ export const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
16
16
  function getDefaultCacheDir() {
17
17
  return process.env.OPENCLI_CACHE_DIR || path.join(os.homedir(), '.opencli', 'cache');
18
18
  }
19
- export function getCachePath(workspace, baseDir = getDefaultCacheDir()) {
20
- const safe = workspace.replace(/[^a-zA-Z0-9_-]+/g, '_');
19
+ export function getCachePath(session, baseDir = getDefaultCacheDir()) {
20
+ const safe = session.replace(/[^a-zA-Z0-9_-]+/g, '_');
21
21
  return path.join(baseDir, 'browser-network', `${safe}.json`);
22
22
  }
23
- export function saveNetworkCache(workspace, entries, baseDir) {
24
- const target = getCachePath(workspace, baseDir);
23
+ export function saveNetworkCache(session, entries, baseDir) {
24
+ const target = getCachePath(session, baseDir);
25
25
  fs.mkdirSync(path.dirname(target), { recursive: true });
26
26
  const payload = {
27
27
  version: 1,
28
- workspace,
28
+ session,
29
29
  savedAt: new Date().toISOString(),
30
30
  entries,
31
31
  };
32
32
  fs.writeFileSync(target, JSON.stringify(payload), 'utf-8');
33
33
  }
34
- export function loadNetworkCache(workspace, opts = {}) {
35
- const target = getCachePath(workspace, opts.baseDir);
34
+ export function loadNetworkCache(session, opts = {}) {
35
+ const target = getCachePath(session, opts.baseDir);
36
36
  let raw;
37
37
  try {
38
38
  raw = fs.readFileSync(target, 'utf-8');
@@ -14,9 +14,9 @@ describe('network-cache', () => {
14
14
  afterEach(() => {
15
15
  fs.rmSync(baseDir, { recursive: true, force: true });
16
16
  });
17
- it('sanitizes workspace names into safe filenames', () => {
18
- const p = getCachePath('browser:default', baseDir);
19
- expect(path.basename(p)).toBe('browser_default.json');
17
+ it('sanitizes session names into safe filenames', () => {
18
+ const p = getCachePath('twitter/agent 1', baseDir);
19
+ expect(path.basename(p)).toBe('twitter_agent_1.json');
20
20
  });
21
21
  it('round-trips entries through save + load', () => {
22
22
  saveNetworkCache('ws', [makeEntry('UserTweets'), makeEntry('UserByScreenName')], baseDir);
@@ -49,7 +49,7 @@ describe('network-cache', () => {
49
49
  });
50
50
  it('findEntry returns matching entry or null', () => {
51
51
  const file = {
52
- version: 1, workspace: 'ws', savedAt: new Date().toISOString(),
52
+ version: 1, session: 'ws', savedAt: new Date().toISOString(),
53
53
  entries: [makeEntry('A'), makeEntry('B')],
54
54
  };
55
55
  expect(findEntry(file, 'B')?.key).toBe('B');
@@ -14,23 +14,24 @@ import { BasePage } from './base-page.js';
14
14
  * Page — implements IPage by talking to the daemon via HTTP.
15
15
  */
16
16
  export declare class Page extends BasePage {
17
- private readonly workspace;
17
+ private readonly session;
18
18
  readonly contextId?: string | undefined;
19
19
  private readonly windowMode?;
20
+ private readonly surface;
21
+ private readonly siteSession?;
20
22
  private readonly _idleTimeout;
21
- constructor(workspace?: string, idleTimeout?: number, contextId?: string | undefined, windowMode?: "foreground" | "background" | undefined);
23
+ constructor(session: string, idleTimeout?: number, contextId?: string | undefined, windowMode?: "foreground" | "background" | undefined, surface?: 'browser' | 'adapter', siteSession?: "ephemeral" | "persistent" | undefined);
22
24
  /** Active page identity (targetId), set after navigate and used in all subsequent commands */
23
25
  private _page;
24
26
  private _networkCaptureUnsupported;
25
27
  private _networkCaptureWarned;
26
- /** Helper: spread workspace into command params */
27
- private _wsOpt;
28
- /** Helper: spread workspace + page identity into command params */
28
+ /** Helper: spread session into command params */
29
+ private _sessionOpts;
30
+ /** Helper: spread session + page identity into command params */
29
31
  private _cmdOpts;
30
32
  goto(url: string, options?: {
31
33
  waitUntil?: 'load' | 'none';
32
34
  settleMs?: number;
33
- allowBoundNavigation?: boolean;
34
35
  }): Promise<void>;
35
36
  /** Get the active page identity (targetId) */
36
37
  getActivePage(): string | undefined;
@@ -42,7 +43,7 @@ export declare class Page extends BasePage {
42
43
  domain?: string;
43
44
  url?: string;
44
45
  }): Promise<BrowserCookie[]>;
45
- /** Release the current automation tab lease in the extension */
46
+ /** Release the current browser session lease in the extension */
46
47
  closeWindow(): Promise<void>;
47
48
  tabs(): Promise<unknown[]>;
48
49
  newTab(url?: string): Promise<string | undefined>;
@@ -26,45 +26,52 @@ function isUnsupportedNetworkCaptureError(err) {
26
26
  * Page — implements IPage by talking to the daemon via HTTP.
27
27
  */
28
28
  export class Page extends BasePage {
29
- workspace;
29
+ session;
30
30
  contextId;
31
31
  windowMode;
32
+ surface;
33
+ siteSession;
32
34
  _idleTimeout;
33
- constructor(workspace = 'default', idleTimeout, contextId, windowMode) {
35
+ constructor(session, idleTimeout, contextId, windowMode, surface = 'browser', siteSession) {
34
36
  super();
35
- this.workspace = workspace;
37
+ this.session = session;
36
38
  this.contextId = contextId;
37
39
  this.windowMode = windowMode;
40
+ this.surface = surface;
41
+ this.siteSession = siteSession;
38
42
  this._idleTimeout = idleTimeout;
39
43
  }
40
44
  /** Active page identity (targetId), set after navigate and used in all subsequent commands */
41
45
  _page;
42
46
  _networkCaptureUnsupported = false;
43
47
  _networkCaptureWarned = false;
44
- /** Helper: spread workspace into command params */
45
- _wsOpt() {
48
+ /** Helper: spread session into command params */
49
+ _sessionOpts() {
46
50
  return {
47
- workspace: this.workspace,
51
+ session: this.session,
52
+ surface: this.surface,
48
53
  ...(this.contextId && { contextId: this.contextId }),
49
54
  ...(this._idleTimeout != null && { idleTimeout: this._idleTimeout }),
50
55
  ...(this.windowMode && { windowMode: this.windowMode }),
56
+ ...(this.siteSession && { siteSession: this.siteSession }),
51
57
  };
52
58
  }
53
- /** Helper: spread workspace + page identity into command params */
59
+ /** Helper: spread session + page identity into command params */
54
60
  _cmdOpts() {
55
61
  return {
56
- workspace: this.workspace,
62
+ session: this.session,
63
+ surface: this.surface,
57
64
  ...(this.contextId && { contextId: this.contextId }),
58
65
  ...(this._page !== undefined && { page: this._page }),
59
66
  ...(this._idleTimeout != null && { idleTimeout: this._idleTimeout }),
60
67
  ...(this.windowMode && { windowMode: this.windowMode }),
68
+ ...(this.siteSession && { siteSession: this.siteSession }),
61
69
  };
62
70
  }
63
71
  async goto(url, options) {
64
72
  const result = await sendCommandFull('navigate', {
65
73
  url,
66
74
  ...this._cmdOpts(),
67
- ...(options?.allowBoundNavigation === true && { allowBoundNavigation: true }),
68
75
  });
69
76
  // Remember the page identity (targetId) for subsequent calls
70
77
  if (result.page) {
@@ -144,13 +151,13 @@ export class Page extends BasePage {
144
151
  }
145
152
  }
146
153
  async getCookies(opts = {}) {
147
- const result = await sendCommand('cookies', { ...this._wsOpt(), ...opts });
154
+ const result = await sendCommand('cookies', { ...this._sessionOpts(), ...opts });
148
155
  return Array.isArray(result) ? result : [];
149
156
  }
150
- /** Release the current automation tab lease in the extension */
157
+ /** Release the current browser session lease in the extension */
151
158
  async closeWindow() {
152
159
  try {
153
- await sendCommand('close-window', { ...this._wsOpt() });
160
+ await sendCommand('close-window', { ...this._sessionOpts() });
154
161
  }
155
162
  catch {
156
163
  // Window may already be closed or daemon may be down
@@ -163,20 +170,20 @@ export class Page extends BasePage {
163
170
  }
164
171
  }
165
172
  async tabs() {
166
- const result = await sendCommand('tabs', { op: 'list', ...this._wsOpt() });
173
+ const result = await sendCommand('tabs', { op: 'list', ...this._sessionOpts() });
167
174
  return Array.isArray(result) ? result : [];
168
175
  }
169
176
  async newTab(url) {
170
177
  const result = await sendCommandFull('tabs', {
171
178
  op: 'new',
172
179
  ...(url !== undefined && { url }),
173
- ...this._wsOpt(),
180
+ ...this._sessionOpts(),
174
181
  });
175
182
  this._lastUrl = null;
176
183
  return result.page;
177
184
  }
178
185
  async closeTab(target) {
179
- const params = { op: 'close', ...this._wsOpt() };
186
+ const params = { op: 'close', ...this._sessionOpts() };
180
187
  if (typeof target === 'number')
181
188
  params.index = target;
182
189
  else if (typeof target === 'string')
@@ -194,7 +201,7 @@ export class Page extends BasePage {
194
201
  const result = await sendCommandFull('tabs', {
195
202
  op: 'select',
196
203
  ...(typeof target === 'number' ? { index: target } : { page: target }),
197
- ...this._wsOpt(),
204
+ ...this._sessionOpts(),
198
205
  });
199
206
  if (result.page)
200
207
  this._page = result.page;
@@ -24,21 +24,40 @@ describe('Page.getCurrentUrl', () => {
24
24
  });
25
25
  it('reads the real browser URL when no local navigation cache exists', async () => {
26
26
  sendCommandMock.mockResolvedValueOnce('https://notebooklm.google.com/notebook/nb-live');
27
- const page = new Page('site:notebooklm');
27
+ const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
28
28
  const url = await page.getCurrentUrl();
29
29
  expect(url).toBe('https://notebooklm.google.com/notebook/nb-live');
30
30
  expect(sendCommandMock).toHaveBeenCalledTimes(1);
31
31
  expect(sendCommandMock).toHaveBeenCalledWith('exec', expect.objectContaining({
32
- workspace: 'site:notebooklm',
32
+ session: 'notebooklm',
33
+ surface: 'adapter',
33
34
  }));
34
35
  });
35
36
  it('caches the discovered browser URL for later reads', async () => {
36
37
  sendCommandMock.mockResolvedValueOnce('https://notebooklm.google.com/notebook/nb-live');
37
- const page = new Page('site:notebooklm');
38
+ const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
38
39
  expect(await page.getCurrentUrl()).toBe('https://notebooklm.google.com/notebook/nb-live');
39
40
  expect(await page.getCurrentUrl()).toBe('https://notebooklm.google.com/notebook/nb-live');
40
41
  expect(sendCommandMock).toHaveBeenCalledTimes(1);
41
42
  });
43
+ it('passes adapter site session lifecycle through daemon commands', async () => {
44
+ sendCommandFullMock.mockResolvedValueOnce({ page: 'page-1', data: { url: 'https://chatgpt.com/' } });
45
+ sendCommandMock.mockResolvedValueOnce(null);
46
+ const page = new Page('site:chatgpt', undefined, undefined, undefined, 'adapter', 'persistent');
47
+ await page.goto('https://chatgpt.com/', { waitUntil: 'none' });
48
+ await page.evaluate('document.title');
49
+ expect(sendCommandFullMock).toHaveBeenCalledWith('navigate', expect.objectContaining({
50
+ session: 'site:chatgpt',
51
+ surface: 'adapter',
52
+ siteSession: 'persistent',
53
+ }));
54
+ expect(sendCommandMock).toHaveBeenCalledWith('exec', expect.objectContaining({
55
+ session: 'site:chatgpt',
56
+ surface: 'adapter',
57
+ siteSession: 'persistent',
58
+ page: 'page-1',
59
+ }));
60
+ });
42
61
  });
43
62
  describe('Page.evaluate', () => {
44
63
  beforeEach(() => {
@@ -50,7 +69,7 @@ describe('Page.evaluate', () => {
50
69
  sendCommandMock
51
70
  .mockRejectedValueOnce(new Error('{"code":-32000,"message":"Inspected target navigated or closed"}'))
52
71
  .mockResolvedValueOnce(42);
53
- const page = new Page('site:notebooklm');
72
+ const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
54
73
  const value = await page.evaluate('21 + 21');
55
74
  expect(value).toBe(42);
56
75
  expect(sendCommandMock).toHaveBeenCalledTimes(2);
@@ -64,30 +83,32 @@ describe('Page network capture compatibility', () => {
64
83
  });
65
84
  it('treats unknown network-capture-start as unsupported and memoizes it', async () => {
66
85
  sendCommandMock.mockRejectedValueOnce(new Error('Unknown action: network-capture-start'));
67
- const page = new Page('site:notebooklm');
86
+ const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
68
87
  await expect(page.startNetworkCapture()).resolves.toBe(false);
69
88
  await expect(page.startNetworkCapture()).resolves.toBe(false);
70
89
  expect(sendCommandMock).toHaveBeenCalledTimes(1);
71
90
  expect(warnMock).toHaveBeenCalledTimes(1);
72
91
  expect(warnMock).toHaveBeenCalledWith(expect.stringContaining('does not support network capture'));
73
92
  expect(sendCommandMock).toHaveBeenCalledWith('network-capture-start', expect.objectContaining({
74
- workspace: 'site:notebooklm',
93
+ session: 'notebooklm',
94
+ surface: 'adapter',
75
95
  }));
76
96
  });
77
97
  it('returns an empty capture when network-capture-read is unsupported', async () => {
78
98
  sendCommandMock.mockRejectedValueOnce(new Error('Unknown action: network-capture-read'));
79
- const page = new Page('site:notebooklm');
99
+ const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
80
100
  await expect(page.readNetworkCapture()).resolves.toEqual([]);
81
101
  await expect(page.readNetworkCapture()).resolves.toEqual([]);
82
102
  expect(sendCommandMock).toHaveBeenCalledTimes(1);
83
103
  expect(warnMock).toHaveBeenCalledTimes(1);
84
104
  expect(sendCommandMock).toHaveBeenCalledWith('network-capture-read', expect.objectContaining({
85
- workspace: 'site:notebooklm',
105
+ session: 'notebooklm',
106
+ surface: 'adapter',
86
107
  }));
87
108
  });
88
109
  it('rethrows unrelated network capture failures', async () => {
89
110
  sendCommandMock.mockRejectedValueOnce(new Error('Extension disconnected'));
90
- const page = new Page('site:notebooklm');
111
+ const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
91
112
  await expect(page.startNetworkCapture()).rejects.toThrow('Extension disconnected');
92
113
  expect(sendCommandMock).toHaveBeenCalledTimes(1);
93
114
  expect(warnMock).not.toHaveBeenCalled();
@@ -96,7 +117,7 @@ describe('Page network capture compatibility', () => {
96
117
  sendCommandMock
97
118
  .mockRejectedValueOnce(new Error('Unknown action: network-capture-start'))
98
119
  .mockRejectedValueOnce(new Error('Unknown action: network-capture-read'));
99
- const page = new Page('site:notebooklm');
120
+ const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
100
121
  await expect(page.startNetworkCapture()).resolves.toBe(false);
101
122
  await expect(page.readNetworkCapture()).resolves.toEqual([]);
102
123
  expect(warnMock).toHaveBeenCalledTimes(1);
@@ -108,14 +129,14 @@ describe('Page download waits', () => {
108
129
  sendCommandFullMock.mockReset();
109
130
  warnMock.mockReset();
110
131
  });
111
- it('sends wait-download through the daemon with workspace and timeout', async () => {
132
+ it('sends wait-download through the daemon with session and timeout', async () => {
112
133
  sendCommandMock.mockResolvedValueOnce({
113
134
  downloaded: true,
114
135
  filename: '/tmp/receipt.pdf',
115
136
  state: 'complete',
116
137
  elapsedMs: 5,
117
138
  });
118
- const page = new Page('site:mercury');
139
+ const page = new Page('mercury', undefined, undefined, undefined, 'adapter');
119
140
  const result = await page.waitForDownload('receipt', 1234);
120
141
  expect(result).toEqual({
121
142
  downloaded: true,
@@ -124,7 +145,8 @@ describe('Page download waits', () => {
124
145
  elapsedMs: 5,
125
146
  });
126
147
  expect(sendCommandMock).toHaveBeenCalledWith('wait-download', expect.objectContaining({
127
- workspace: 'site:mercury',
148
+ session: 'mercury',
149
+ surface: 'adapter',
128
150
  pattern: 'receipt',
129
151
  timeoutMs: 1234,
130
152
  }));
@@ -138,10 +160,11 @@ describe('Page CDP helpers', () => {
138
160
  });
139
161
  it('handles JavaScript dialogs through the CDP passthrough', async () => {
140
162
  sendCommandMock.mockResolvedValueOnce({});
141
- const page = new Page('browser:default');
163
+ const page = new Page('default');
142
164
  await page.handleJavaScriptDialog(true, 'confirm');
143
165
  expect(sendCommandMock).toHaveBeenCalledWith('cdp', expect.objectContaining({
144
- workspace: 'browser:default',
166
+ session: 'default',
167
+ surface: 'browser',
145
168
  cdpMethod: 'Page.handleJavaScriptDialog',
146
169
  cdpParams: { accept: true, promptText: 'confirm' },
147
170
  }));
@@ -158,24 +181,26 @@ describe('Page active target tracking', () => {
158
181
  .mockResolvedValueOnce({ data: { url: 'https://first.example' }, page: 'page-1' })
159
182
  .mockResolvedValueOnce({ data: { selected: true }, page: 'page-2' });
160
183
  sendCommandMock.mockResolvedValue('ok');
161
- const page = new Page('browser:default');
184
+ const page = new Page('default');
162
185
  await page.goto('https://first.example', { waitUntil: 'none' });
163
186
  expect(page.getActivePage()).toBe('page-1');
164
187
  await page.selectTab(1);
165
188
  expect(page.getActivePage()).toBe('page-2');
166
189
  await page.evaluate('1 + 1');
167
190
  expect(sendCommandMock).toHaveBeenLastCalledWith('exec', expect.objectContaining({
168
- workspace: 'browser:default',
191
+ session: 'default',
192
+ surface: 'browser',
169
193
  page: 'page-2',
170
194
  }));
171
195
  });
172
196
  it('allows the caller to bind a specific active page identity explicitly', async () => {
173
197
  sendCommandMock.mockResolvedValue('bound');
174
- const page = new Page('browser:default');
198
+ const page = new Page('default');
175
199
  page.setActivePage?.('page-explicit');
176
200
  await page.evaluate('1 + 1');
177
201
  expect(sendCommandMock).toHaveBeenCalledWith('exec', expect.objectContaining({
178
- workspace: 'browser:default',
202
+ session: 'default',
203
+ surface: 'browser',
179
204
  page: 'page-explicit',
180
205
  }));
181
206
  });
@@ -187,14 +212,15 @@ describe('Page active target tracking', () => {
187
212
  page: 'page-2',
188
213
  });
189
214
  sendCommandMock.mockResolvedValue('ok');
190
- const page = new Page('browser:default');
215
+ const page = new Page('default');
191
216
  await page.goto('https://first.example', { waitUntil: 'none' });
192
217
  const created = await page.newTab?.('https://second.example');
193
218
  expect(created).toBe('page-2');
194
219
  expect(page.getActivePage()).toBe('page-1');
195
220
  await page.evaluate('1 + 1');
196
221
  expect(sendCommandMock).toHaveBeenLastCalledWith('exec', expect.objectContaining({
197
- workspace: 'browser:default',
222
+ session: 'default',
223
+ surface: 'browser',
198
224
  page: 'page-1',
199
225
  }));
200
226
  });
@@ -203,7 +229,7 @@ describe('Page active target tracking', () => {
203
229
  data: { url: 'https://second.example' },
204
230
  page: 'page-2',
205
231
  });
206
- const page = new Page('browser:default');
232
+ const page = new Page('default');
207
233
  const created = await page.newTab?.('https://second.example');
208
234
  expect(created).toBe('page-2');
209
235
  expect(page.getActivePage()).toBeUndefined();
@@ -212,16 +238,18 @@ describe('Page active target tracking', () => {
212
238
  expect(sendCommandFullMock).toHaveBeenCalledWith('tabs', expect.objectContaining({
213
239
  op: 'new',
214
240
  url: 'https://second.example',
215
- workspace: 'browser:default',
241
+ session: 'default',
242
+ surface: 'browser',
216
243
  }));
217
244
  });
218
245
  it('closes a tab by explicit page identity', async () => {
219
246
  sendCommandMock.mockResolvedValueOnce({ closed: 'page-2' });
220
- const page = new Page('browser:default');
247
+ const page = new Page('default');
221
248
  await page.closeTab?.('page-2');
222
249
  expect(sendCommandMock).toHaveBeenCalledWith('tabs', expect.objectContaining({
223
250
  op: 'close',
224
- workspace: 'browser:default',
251
+ session: 'default',
252
+ surface: 'browser',
225
253
  page: 'page-2',
226
254
  }));
227
255
  });
@@ -230,7 +258,7 @@ describe('Page active target tracking', () => {
230
258
  sendCommandMock
231
259
  .mockResolvedValueOnce({ closed: 'page-2' })
232
260
  .mockResolvedValueOnce('ok');
233
- const page = new Page('browser:default');
261
+ const page = new Page('default');
234
262
  await page.selectTab(1);
235
263
  expect(page.getActivePage()).toBe('page-2');
236
264
  await page.closeTab?.(1);
@@ -239,7 +267,8 @@ describe('Page active target tracking', () => {
239
267
  const evalCall = sendCommandMock.mock.calls.at(-1);
240
268
  expect(evalCall?.[0]).toBe('exec');
241
269
  expect(evalCall?.[1]).toEqual(expect.objectContaining({
242
- workspace: 'browser:default',
270
+ session: 'default',
271
+ surface: 'browser',
243
272
  }));
244
273
  expect(evalCall?.[1]).not.toHaveProperty('page');
245
274
  });
@@ -252,18 +281,19 @@ describe('Page.screenshot', () => {
252
281
  });
253
282
  it('forwards width / height / fullPage options to the bridge', async () => {
254
283
  sendCommandMock.mockResolvedValueOnce('BASE64');
255
- const page = new Page('browser:default');
284
+ const page = new Page('default');
256
285
  const data = await page.screenshot({ fullPage: true, width: 1080 });
257
286
  expect(data).toBe('BASE64');
258
287
  expect(sendCommandMock).toHaveBeenCalledWith('screenshot', expect.objectContaining({
259
- workspace: 'browser:default',
288
+ session: 'default',
289
+ surface: 'browser',
260
290
  fullPage: true,
261
291
  width: 1080,
262
292
  }));
263
293
  });
264
294
  it('omits viewport overrides when none are set', async () => {
265
295
  sendCommandMock.mockResolvedValueOnce('BASE64');
266
- const page = new Page('browser:default');
296
+ const page = new Page('default');
267
297
  await page.screenshot();
268
298
  const call = sendCommandMock.mock.calls.at(-1);
269
299
  expect(call?.[0]).toBe('screenshot');
@@ -102,7 +102,7 @@ function toManifestEntry(cmd, modulePath, sourceFile) {
102
102
  modulePath,
103
103
  sourceFile,
104
104
  navigateBefore: cmd.navigateBefore,
105
- browserSession: cmd.browserSession,
105
+ siteSession: cmd.siteSession,
106
106
  };
107
107
  }
108
108
  /**