@browserbridge/bbx 1.7.3 → 1.7.5

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 (29) hide show
  1. package/package.json +7 -7
  2. package/packages/agent-client/src/atomic-write.js +99 -0
  3. package/packages/agent-client/src/cli-helpers.js +20 -3
  4. package/packages/agent-client/src/cli.js +273 -76
  5. package/packages/agent-client/src/command-registry.js +12 -6
  6. package/packages/agent-client/src/install.js +39 -4
  7. package/packages/agent-client/src/mcp-config.js +28 -14
  8. package/packages/agent-client/src/remotes.js +93 -15
  9. package/packages/agent-client/src/runtime.js +6 -3
  10. package/packages/mcp-server/src/bin.js +10 -1
  11. package/packages/mcp-server/src/guidance.js +2 -2
  12. package/packages/mcp-server/src/handlers-capture.js +62 -32
  13. package/packages/mcp-server/src/handlers-dom.js +2 -2
  14. package/packages/mcp-server/src/handlers-navigation.js +30 -9
  15. package/packages/mcp-server/src/handlers-page.js +170 -143
  16. package/packages/mcp-server/src/handlers-utils.js +277 -8
  17. package/packages/mcp-server/src/handlers.js +31 -10
  18. package/packages/mcp-server/src/lifecycle.js +413 -0
  19. package/packages/mcp-server/src/server.js +59 -30
  20. package/packages/native-host/src/daemon.js +3 -1
  21. package/packages/protocol/src/capabilities.js +1 -1
  22. package/packages/protocol/src/defaults.js +2 -0
  23. package/packages/protocol/src/protocol.js +122 -9
  24. package/packages/protocol/src/registry.js +14 -14
  25. package/packages/protocol/src/summary.js +61 -30
  26. package/packages/protocol/src/types.ts +30 -1
  27. package/skills/browser-bridge/SKILL.md +4 -4
  28. package/skills/browser-bridge/references/interaction.md +1 -1
  29. package/skills/browser-bridge/references/protocol.md +3 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserbridge/bbx",
3
- "version": "1.7.3",
3
+ "version": "1.7.5",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "agent-tools",
@@ -73,13 +73,13 @@
73
73
  "zod": "^4.4.3"
74
74
  },
75
75
  "devDependencies": {
76
- "@types/chrome": "^0.1.43",
77
- "@types/node": "^25.9.2",
76
+ "@types/chrome": "^0.2.2",
77
+ "@types/node": "^26.1.1",
78
78
  "c8": "^11.0.0",
79
- "linkedom": "^0.18.12",
80
- "oxfmt": "^0.54.0",
81
- "oxlint": "^1.69.0",
82
- "typescript": "^6.0.3"
79
+ "linkedom": "^0.18.13",
80
+ "oxfmt": "^0.58.0",
81
+ "oxlint": "^1.73.0",
82
+ "typescript": "^7.0.2"
83
83
  },
84
84
  "engines": {
85
85
  "node": ">=18"
@@ -0,0 +1,99 @@
1
+ // @ts-check
2
+
3
+ import { randomUUID } from 'node:crypto';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+
7
+ /**
8
+ * Replace a file through a sibling temporary file so readers never observe a
9
+ * partial write. Existing permissions are preserved unless an explicit mode is
10
+ * requested.
11
+ *
12
+ * @param {string} targetPath
13
+ * @param {string | NodeJS.ArrayBufferView} data
14
+ * @param {{ encoding?: BufferEncoding, mode?: number, preserveMode?: boolean }} [options={ }]
15
+ * @returns {Promise<void>}
16
+ */
17
+ export async function atomicWriteFile(targetPath, data, options = {}) {
18
+ const directory = path.dirname(targetPath);
19
+ const temporaryPath = path.join(
20
+ directory,
21
+ `.${path.basename(targetPath)}.${process.pid}.${randomUUID()}.tmp`
22
+ );
23
+ await fs.promises.mkdir(directory, { recursive: true });
24
+
25
+ let existingMode;
26
+ if (options.preserveMode !== false && options.mode === undefined) {
27
+ try {
28
+ existingMode = (await fs.promises.stat(targetPath)).mode & 0o777;
29
+ } catch (error) {
30
+ if (!isMissingFileError(error)) {
31
+ throw error;
32
+ }
33
+ }
34
+ }
35
+
36
+ const mode = options.mode ?? existingMode;
37
+ try {
38
+ await fs.promises.writeFile(temporaryPath, data, {
39
+ ...(options.encoding ? { encoding: options.encoding } : {}),
40
+ ...(mode === undefined ? {} : { mode }),
41
+ flag: 'wx',
42
+ });
43
+ if (process.platform !== 'win32' && mode !== undefined) {
44
+ await fs.promises.chmod(temporaryPath, mode);
45
+ }
46
+ try {
47
+ await fs.promises.rename(temporaryPath, targetPath);
48
+ } catch (error) {
49
+ if (process.platform !== 'win32' || !isReplaceError(error)) {
50
+ throw error;
51
+ }
52
+ try {
53
+ await fs.promises.stat(targetPath);
54
+ } catch {
55
+ throw error;
56
+ }
57
+
58
+ const backupPath = `${temporaryPath}.backup`;
59
+ await fs.promises.rename(targetPath, backupPath);
60
+ try {
61
+ await fs.promises.rename(temporaryPath, targetPath);
62
+ } catch (replaceError) {
63
+ await fs.promises.rename(backupPath, targetPath).catch(() => {});
64
+ throw replaceError;
65
+ }
66
+ await fs.promises.rm(backupPath, { force: true }).catch(() => {});
67
+ }
68
+ if (process.platform !== 'win32' && options.mode !== undefined) {
69
+ await fs.promises.chmod(targetPath, options.mode);
70
+ }
71
+ } catch (error) {
72
+ await fs.promises.rm(temporaryPath, { force: true }).catch(() => {});
73
+ throw error;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Windows does not replace an existing file with rename().
79
+ *
80
+ * @param {unknown} error
81
+ * @returns {boolean}
82
+ */
83
+ function isReplaceError(error) {
84
+ const code =
85
+ error && typeof error === 'object' ? /** @type {{ code?: unknown }} */ (error).code : null;
86
+ return code === 'EEXIST' || code === 'EPERM' || code === 'EACCES';
87
+ }
88
+
89
+ /**
90
+ * @param {unknown} error
91
+ * @returns {boolean}
92
+ */
93
+ function isMissingFileError(error) {
94
+ return Boolean(
95
+ error &&
96
+ typeof error === 'object' &&
97
+ /** @type {{ code?: unknown }} */ (error).code === 'ENOENT'
98
+ );
99
+ }
@@ -96,7 +96,7 @@ export function parseJsonObject(value) {
96
96
 
97
97
  /**
98
98
  * Parse a CLI argument as a positive integer, throwing a user-friendly error
99
- * if the value is missing or not a finite number.
99
+ * if the value is missing or not a positive integer.
100
100
  *
101
101
  * @param {string | undefined} value
102
102
  * @param {string} argName - The argument name shown in the error message
@@ -104,12 +104,29 @@ export function parseJsonObject(value) {
104
104
  */
105
105
  export function parseIntArg(value, argName) {
106
106
  const n = Number(value);
107
- if (!value || !Number.isFinite(n)) {
108
- throw new Error(`${argName} must be a number (got ${JSON.stringify(value)}).`);
107
+ if (!value || !Number.isInteger(n) || n <= 0) {
108
+ throw new Error(`${argName} must be a positive integer (got ${JSON.stringify(value)}).`);
109
109
  }
110
110
  return n;
111
111
  }
112
112
 
113
+ /**
114
+ * Parse a CLI argument as a finite number. This is intentionally separate from
115
+ * positive integer parsing because viewport coordinates may be fractional or
116
+ * negative.
117
+ *
118
+ * @param {string | undefined} value
119
+ * @param {string} argName
120
+ * @returns {number}
121
+ */
122
+ export function parseNumberArg(value, argName) {
123
+ const number = Number(value);
124
+ if (value === undefined || value === '' || !Number.isFinite(number)) {
125
+ throw new Error(`${argName} must be a number (got ${JSON.stringify(value)}).`);
126
+ }
127
+ return number;
128
+ }
129
+
113
130
  /**
114
131
  * @param {string} method
115
132
  * @returns {boolean}