@angelky7/dsh-browser-control 1.7.9 → 1.7.10
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/README.en.md +3 -3
- package/README.md +3 -3
- package/lib/index.js +54 -9
- package/lib/types/index.js +33 -10
- package/lib/types/text.d.ts +19 -0
- package/lib/types/text.js +36 -0
- package/package.json +2 -2
package/README.en.md
CHANGED
|
@@ -64,8 +64,8 @@ In one line: **for "use MY browser" tasks (logged-in Bilibili, Zhihu, admin pane
|
|
|
64
64
|
|
|
65
65
|
| File | Purpose |
|
|
66
66
|
|---|---|
|
|
67
|
-
| [DSH-Browser-Control-v1.7.2.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.7.2/DSH-Browser-Control-v1.7.2.zip) | Chrome extension (unzip and load; **the extension no longer changes with feature updates from v1.7.2 on**: v1.7.3–v1.7.
|
|
68
|
-
| [dsh-browser-control-plugin-v1.7.
|
|
67
|
+
| [DSH-Browser-Control-v1.7.2.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.7.2/DSH-Browser-Control-v1.7.2.zip) | Chrome extension (unzip and load; **the extension no longer changes with feature updates from v1.7.2 on**: v1.7.3–v1.7.10 need no extension reinstall; the v1.7.10 session fix is plugin-side only (extension clipping improvements ship with the next extension update)) |
|
|
68
|
+
| [dsh-browser-control-plugin-v1.7.10.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.7.10/dsh-browser-control-plugin-v1.7.10.zip) | dsh plugin (offline fallback; online installs use Option A/B) |
|
|
69
69
|
|
|
70
70
|
## Install the Chrome extension (30 seconds)
|
|
71
71
|
|
|
@@ -104,7 +104,7 @@ npm install @angelky7/dsh-browser-control
|
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
106
|
# Straight from GitHub
|
|
107
|
-
dsh plugin --profile web add "github:angelky7/dsh-browser-control#v1.7.
|
|
107
|
+
dsh plugin --profile web add "github:angelky7/dsh-browser-control#v1.7.10"
|
|
108
108
|
|
|
109
109
|
# Local checkout for debugging (note: the explicit file: prefix is required)
|
|
110
110
|
dsh plugin --profile web add "file:D:\path\to\dsh-browser-control"
|
package/README.md
CHANGED
|
@@ -64,8 +64,8 @@ Chrome 扩展(CDP 驱动)
|
|
|
64
64
|
|
|
65
65
|
| 文件 | 说明 |
|
|
66
66
|
|---|---|
|
|
67
|
-
| [DSH-Browser-Control-v1.7.2.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.7.2/DSH-Browser-Control-v1.7.2.zip) | Chrome 扩展(解压后加载;**扩展从 v1.7.2 起不再随功能更新**:v1.7.3–v1.7.
|
|
68
|
-
| [dsh-browser-control-plugin-v1.7.
|
|
67
|
+
| [DSH-Browser-Control-v1.7.2.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.7.2/DSH-Browser-Control-v1.7.2.zip) | Chrome 扩展(解压后加载;**扩展从 v1.7.2 起不再随功能更新**:v1.7.3–v1.7.10 的功能更新都不需要重装扩展;v1.7.10 的会话修复只改了插件(扩展侧的截断优化随下一次扩展更新)) |
|
|
68
|
+
| [dsh-browser-control-plugin-v1.7.10.zip](https://github.com/angelky7/dsh-browser-control/releases/download/v1.7.10/dsh-browser-control-plugin-v1.7.10.zip) | dsh 插件(离线兜底,在线装直接用方式 A/B) |
|
|
69
69
|
|
|
70
70
|
## 安装 Chrome 扩展(30 秒)
|
|
71
71
|
|
|
@@ -104,7 +104,7 @@ npm install @angelky7/dsh-browser-control
|
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
106
|
# 直接从 GitHub 安装
|
|
107
|
-
dsh plugin --profile web add "github:angelky7/dsh-browser-control#v1.7.
|
|
107
|
+
dsh plugin --profile web add "github:angelky7/dsh-browser-control#v1.7.10"
|
|
108
108
|
|
|
109
109
|
# 本地目录调试(注意:必须显式 file: 前缀)
|
|
110
110
|
dsh plugin --profile web add "file:D:\path\to\dsh-browser-control"
|
package/lib/index.js
CHANGED
|
@@ -1284,6 +1284,38 @@ var SessionRegistry = class {
|
|
|
1284
1284
|
}
|
|
1285
1285
|
};
|
|
1286
1286
|
//#endregion
|
|
1287
|
+
//#region lib/types/text.js
|
|
1288
|
+
/**
|
|
1289
|
+
* UTF-16 截断守卫(issue #26)。
|
|
1290
|
+
*
|
|
1291
|
+
* 页面文本按**码元** `slice(0, n)` 会切出孤立代理项(半个 emoji)。`JSON.stringify`
|
|
1292
|
+
* 把它写成裸 `\ud83d`,模型的 JSON 解析器要求代理对成对出现,于是**整个请求体**
|
|
1293
|
+
* 被 400 拒收;坏字符进了会话历史就每轮重放,只能换会话。
|
|
1294
|
+
*
|
|
1295
|
+
* 两条规则:
|
|
1296
|
+
* 1. 切点落在高位代理项上时退一格——宁可少一个字,不留半个字符;
|
|
1297
|
+
* 2. `toWellFormed()` 兜底页面自己提供的坏字符(孤立代理项 → U+FFFD)。
|
|
1298
|
+
*/
|
|
1299
|
+
/** 把任意字符串变成 well-formed UTF-16(孤立代理项 → U+FFFD)。 */
|
|
1300
|
+
function toWellFormed(text) {
|
|
1301
|
+
const fn = text.toWellFormed;
|
|
1302
|
+
if (typeof fn === "function") return fn.call(text);
|
|
1303
|
+
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "�");
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* 按码元上限截断,但不留下半个代理对。
|
|
1307
|
+
* @param text - 原文本。
|
|
1308
|
+
* @param maxChars - 码元上限(与既有 offset/limit 语义一致);≤0 返回空串。
|
|
1309
|
+
*/
|
|
1310
|
+
function sliceWellFormed(text, maxChars) {
|
|
1311
|
+
if (maxChars <= 0) return "";
|
|
1312
|
+
if (text.length <= maxChars) return toWellFormed(text);
|
|
1313
|
+
let end = maxChars;
|
|
1314
|
+
const unit = text.charCodeAt(end - 1);
|
|
1315
|
+
if (unit >= 55296 && unit <= 56319) end -= 1;
|
|
1316
|
+
return toWellFormed(text.slice(0, Math.max(0, end)));
|
|
1317
|
+
}
|
|
1318
|
+
//#endregion
|
|
1287
1319
|
//#region lib/types/index.js
|
|
1288
1320
|
/**
|
|
1289
1321
|
* Browser-bridge plugin: one local WebSocket endpoint the DSH Browser Control
|
|
@@ -1361,11 +1393,23 @@ function defineTool$1(options) {
|
|
|
1361
1393
|
const execute = ((args, exec) => {
|
|
1362
1394
|
const agent = exec.agent;
|
|
1363
1395
|
const sessionId = typeof agent?.id === "string" && agent.id.length > 0 ? agent.id : void 0;
|
|
1364
|
-
return TOOL_SESSION.run(sessionId, () => inner(args, exec))
|
|
1396
|
+
return TOOL_SESSION.run(sessionId, () => inner(args, exec)).catch((error) => {
|
|
1397
|
+
if (error instanceof Error) error.message = toWellFormed(error.message);
|
|
1398
|
+
throw error;
|
|
1399
|
+
});
|
|
1365
1400
|
});
|
|
1401
|
+
const rawRender = options.output?.render;
|
|
1402
|
+
const output = rawRender === void 0 ? options.output : {
|
|
1403
|
+
...options.output,
|
|
1404
|
+
render: (args, value) => rawRender(args, value).map((part) => part.type === "text" && typeof part.text === "string" ? {
|
|
1405
|
+
...part,
|
|
1406
|
+
text: toWellFormed(part.text)
|
|
1407
|
+
} : part)
|
|
1408
|
+
};
|
|
1366
1409
|
return defineTool({
|
|
1367
1410
|
...options,
|
|
1368
|
-
execute
|
|
1411
|
+
execute,
|
|
1412
|
+
output
|
|
1369
1413
|
});
|
|
1370
1414
|
}
|
|
1371
1415
|
/** Cordis plugin name used by loader diagnostics. */
|
|
@@ -1783,9 +1827,10 @@ function errorMessage(error) {
|
|
|
1783
1827
|
*/
|
|
1784
1828
|
const MAX_RENDERED_CHARS = 4e3;
|
|
1785
1829
|
function clampRendered(text, maxChars = MAX_RENDERED_CHARS) {
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1830
|
+
const safe = toWellFormed(text);
|
|
1831
|
+
if (safe.length <= maxChars) return safe;
|
|
1832
|
+
const cut = sliceWellFormed(safe, maxChars);
|
|
1833
|
+
return `${cut}\n… [renderedTruncated omittedChars=${safe.length - cut.length} limitChars=${maxChars}] narrow the query and call again to see the rest`;
|
|
1789
1834
|
}
|
|
1790
1835
|
/** Model-facing budget for one snapshot listing; `limit` narrows the listing. */
|
|
1791
1836
|
const SNAPSHOT_RENDER_MAX_CHARS = 8e3;
|
|
@@ -1793,8 +1838,8 @@ const SNAPSHOT_RENDER_MAX_CHARS = 8e3;
|
|
|
1793
1838
|
const LOG_RENDER_MAX_CHARS = 6e3;
|
|
1794
1839
|
/** Collapse one page-supplied field to a single line inside a rendered listing. */
|
|
1795
1840
|
function flattenOneLine(value, maxChars) {
|
|
1796
|
-
const flat = value.replace(/\s+/g, " ").trim();
|
|
1797
|
-
return flat.length <= maxChars ? flat : `${flat
|
|
1841
|
+
const flat = toWellFormed(value.replace(/\s+/g, " ").trim());
|
|
1842
|
+
return flat.length <= maxChars ? flat : `${sliceWellFormed(flat, maxChars)}…`;
|
|
1798
1843
|
}
|
|
1799
1844
|
/** `HH:MM:SS` in UTC from an epoch-milliseconds stamp. */
|
|
1800
1845
|
function formatClock(epochMs) {
|
|
@@ -2129,7 +2174,7 @@ function applyBrowserTools(ctx, controller) {
|
|
|
2129
2174
|
tabId: args.tabId
|
|
2130
2175
|
}, exec.signal);
|
|
2131
2176
|
const full = raw.content ?? "";
|
|
2132
|
-
const content = full.slice(offset, offset + limit);
|
|
2177
|
+
const content = toWellFormed(full.slice(offset, offset + limit));
|
|
2133
2178
|
return {
|
|
2134
2179
|
tabId: raw.tabId,
|
|
2135
2180
|
mode,
|
|
@@ -4440,7 +4485,7 @@ function registerSessionsTool(ctx, controller) {
|
|
|
4440
4485
|
},
|
|
4441
4486
|
render: (_args, value) => {
|
|
4442
4487
|
const v = value;
|
|
4443
|
-
const short = (id) => id.length <= 18 ? id : `${id
|
|
4488
|
+
const short = (id) => id.length <= 18 ? id : `${sliceWellFormed(id, 16)}…`;
|
|
4444
4489
|
if (v.releasedTabId !== void 0) return [{
|
|
4445
4490
|
type: "text",
|
|
4446
4491
|
text: `Sessions: released your working tab ${v.releasedTabId} — later calls without tabId follow the browser's active tab again.`
|
package/lib/types/index.js
CHANGED
|
@@ -29,6 +29,7 @@ import { policyRefusal } from "./policy.js";
|
|
|
29
29
|
import { AUDIT_MAX_ENTRIES, AuditLog, summarizeAuditParams } from "./audit.js";
|
|
30
30
|
import { Journal, readJournal } from "./journal.js";
|
|
31
31
|
import { SessionRegistry } from "./sessions.js";
|
|
32
|
+
import { sliceWellFormed, toWellFormed } from "./text.js";
|
|
32
33
|
/** Navigation wait ceiling (same as browser_wait); the bridge gets this plus BRIDGE_MARGIN_MS. */
|
|
33
34
|
const NAV_MAX_MS = 120_000;
|
|
34
35
|
/**
|
|
@@ -76,12 +77,28 @@ function defineTool(options) {
|
|
|
76
77
|
const execute = ((args, exec) => {
|
|
77
78
|
const agent = exec.agent;
|
|
78
79
|
const sessionId = typeof agent?.id === 'string' && agent.id.length > 0 ? agent.id : undefined;
|
|
79
|
-
return TOOL_SESSION.run(sessionId, () => inner(args, exec))
|
|
80
|
+
return TOOL_SESSION.run(sessionId, () => inner(args, exec)).catch((error) => {
|
|
81
|
+
// 错误文本同样进入模型请求体:页面异常可能带孤立代理项(issue #26)。
|
|
82
|
+
// 原地改 message 而不是包一层,保留 BridgeError 的 code/effectState。
|
|
83
|
+
if (error instanceof Error)
|
|
84
|
+
error.message = toWellFormed(error.message);
|
|
85
|
+
throw error;
|
|
86
|
+
});
|
|
80
87
|
});
|
|
88
|
+
// 模型可见文本的最后一道路障(issue #26):任何 render 产出的 text 在交给宿主前
|
|
89
|
+
// 都修成 well-formed。扩展侧旧版本还在用码元截断,这里必须兜住;否则半个 emoji
|
|
90
|
+
// 会让整个请求体 400,且坏字符会随会话历史每轮重放。
|
|
91
|
+
const rawRender = options.output?.render;
|
|
92
|
+
const output = rawRender === undefined
|
|
93
|
+
? options.output
|
|
94
|
+
: {
|
|
95
|
+
...options.output,
|
|
96
|
+
render: (args, value) => rawRender(args, value).map((part) => (part.type === 'text' && typeof part.text === 'string' ? { ...part, text: toWellFormed(part.text) } : part)),
|
|
97
|
+
};
|
|
81
98
|
// Call the host helper through a non-generic point: re-instantiating its
|
|
82
99
|
// recursive InferObject types from inside a generic wrapper is what TS chokes on.
|
|
83
100
|
const hostDefine = defineHostTool;
|
|
84
|
-
return hostDefine({ ...options, execute });
|
|
101
|
+
return hostDefine({ ...options, execute, output });
|
|
85
102
|
}
|
|
86
103
|
// Re-exported so the policy tables stay testable from the published bundle
|
|
87
104
|
// (verify/verify_policy.mjs reconciles them against the extension's COMMANDS).
|
|
@@ -506,10 +523,14 @@ function errorMessage(error) {
|
|
|
506
523
|
*/
|
|
507
524
|
const MAX_RENDERED_CHARS = 4_000;
|
|
508
525
|
function clampRendered(text, maxChars = MAX_RENDERED_CHARS) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
526
|
+
// 页面文本可能自带孤立代理项,也可能是上一次按码元切出来的;先修好再判断长度。
|
|
527
|
+
const safe = toWellFormed(text);
|
|
528
|
+
if (safe.length <= maxChars)
|
|
529
|
+
return safe;
|
|
530
|
+
const cut = sliceWellFormed(safe, maxChars);
|
|
531
|
+
// 报数按**实际展示**的长度算:退掉的半个代理对算进 omitted,可见 + omitted = 总长。
|
|
532
|
+
const omitted = safe.length - cut.length;
|
|
533
|
+
return `${cut}\n… [renderedTruncated omittedChars=${omitted} limitChars=${maxChars}] narrow the query and call again to see the rest`;
|
|
513
534
|
}
|
|
514
535
|
/** Model-facing budget for one snapshot listing; `limit` narrows the listing. */
|
|
515
536
|
const SNAPSHOT_RENDER_MAX_CHARS = 8_000;
|
|
@@ -517,8 +538,8 @@ const SNAPSHOT_RENDER_MAX_CHARS = 8_000;
|
|
|
517
538
|
const LOG_RENDER_MAX_CHARS = 6_000;
|
|
518
539
|
/** Collapse one page-supplied field to a single line inside a rendered listing. */
|
|
519
540
|
function flattenOneLine(value, maxChars) {
|
|
520
|
-
const flat = value.replace(/\s+/g, ' ').trim();
|
|
521
|
-
return flat.length <= maxChars ? flat : `${flat
|
|
541
|
+
const flat = toWellFormed(value.replace(/\s+/g, ' ').trim());
|
|
542
|
+
return flat.length <= maxChars ? flat : `${sliceWellFormed(flat, maxChars)}…`;
|
|
522
543
|
}
|
|
523
544
|
/** `HH:MM:SS` in UTC from an epoch-milliseconds stamp. */
|
|
524
545
|
function formatClock(epochMs) {
|
|
@@ -835,7 +856,9 @@ function applyBrowserTools(ctx, controller) {
|
|
|
835
856
|
const limit = Math.min(READ_WINDOW_MAX_CHARS, Math.max(1, Math.floor(Number(args.limit ?? READ_WINDOW_MAX_CHARS)) || READ_WINDOW_MAX_CHARS));
|
|
836
857
|
const raw = await controller.execute('content', args.tabId === undefined ? { mode } : { mode, tabId: args.tabId }, exec.signal);
|
|
837
858
|
const full = raw.content ?? '';
|
|
838
|
-
|
|
859
|
+
// 码元窗口可能切在代理对中间;修成 well-formed,长度不变(U+FFFD 也是一码元),
|
|
860
|
+
// offset/truncated 的算术保持原语义。
|
|
861
|
+
const content = toWellFormed(full.slice(offset, offset + limit));
|
|
839
862
|
return {
|
|
840
863
|
tabId: raw.tabId,
|
|
841
864
|
mode,
|
|
@@ -2458,7 +2481,7 @@ function registerSessionsTool(ctx, controller) {
|
|
|
2458
2481
|
},
|
|
2459
2482
|
render: (_args, value) => {
|
|
2460
2483
|
const v = value;
|
|
2461
|
-
const short = (id) => (id.length <= 18 ? id : `${id
|
|
2484
|
+
const short = (id) => (id.length <= 18 ? id : `${sliceWellFormed(id, 16)}…`);
|
|
2462
2485
|
if (v.releasedTabId !== undefined) {
|
|
2463
2486
|
return [{ type: 'text', text: `Sessions: released your working tab ${v.releasedTabId} — later calls without tabId follow the browser's active tab again.` }];
|
|
2464
2487
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UTF-16 截断守卫(issue #26)。
|
|
3
|
+
*
|
|
4
|
+
* 页面文本按**码元** `slice(0, n)` 会切出孤立代理项(半个 emoji)。`JSON.stringify`
|
|
5
|
+
* 把它写成裸 `\ud83d`,模型的 JSON 解析器要求代理对成对出现,于是**整个请求体**
|
|
6
|
+
* 被 400 拒收;坏字符进了会话历史就每轮重放,只能换会话。
|
|
7
|
+
*
|
|
8
|
+
* 两条规则:
|
|
9
|
+
* 1. 切点落在高位代理项上时退一格——宁可少一个字,不留半个字符;
|
|
10
|
+
* 2. `toWellFormed()` 兜底页面自己提供的坏字符(孤立代理项 → U+FFFD)。
|
|
11
|
+
*/
|
|
12
|
+
/** 把任意字符串变成 well-formed UTF-16(孤立代理项 → U+FFFD)。 */
|
|
13
|
+
export declare function toWellFormed(text: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* 按码元上限截断,但不留下半个代理对。
|
|
16
|
+
* @param text - 原文本。
|
|
17
|
+
* @param maxChars - 码元上限(与既有 offset/limit 语义一致);≤0 返回空串。
|
|
18
|
+
*/
|
|
19
|
+
export declare function sliceWellFormed(text: string, maxChars: number): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UTF-16 截断守卫(issue #26)。
|
|
3
|
+
*
|
|
4
|
+
* 页面文本按**码元** `slice(0, n)` 会切出孤立代理项(半个 emoji)。`JSON.stringify`
|
|
5
|
+
* 把它写成裸 `\ud83d`,模型的 JSON 解析器要求代理对成对出现,于是**整个请求体**
|
|
6
|
+
* 被 400 拒收;坏字符进了会话历史就每轮重放,只能换会话。
|
|
7
|
+
*
|
|
8
|
+
* 两条规则:
|
|
9
|
+
* 1. 切点落在高位代理项上时退一格——宁可少一个字,不留半个字符;
|
|
10
|
+
* 2. `toWellFormed()` 兜底页面自己提供的坏字符(孤立代理项 → U+FFFD)。
|
|
11
|
+
*/
|
|
12
|
+
/** 把任意字符串变成 well-formed UTF-16(孤立代理项 → U+FFFD)。 */
|
|
13
|
+
export function toWellFormed(text) {
|
|
14
|
+
const fn = text.toWellFormed;
|
|
15
|
+
if (typeof fn === 'function')
|
|
16
|
+
return fn.call(text);
|
|
17
|
+
// 老运行时兜底:只替换没有配对的代理项。
|
|
18
|
+
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, '\uFFFD');
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 按码元上限截断,但不留下半个代理对。
|
|
22
|
+
* @param text - 原文本。
|
|
23
|
+
* @param maxChars - 码元上限(与既有 offset/limit 语义一致);≤0 返回空串。
|
|
24
|
+
*/
|
|
25
|
+
export function sliceWellFormed(text, maxChars) {
|
|
26
|
+
if (maxChars <= 0)
|
|
27
|
+
return '';
|
|
28
|
+
if (text.length <= maxChars)
|
|
29
|
+
return toWellFormed(text);
|
|
30
|
+
let end = maxChars;
|
|
31
|
+
const unit = text.charCodeAt(end - 1);
|
|
32
|
+
// 高位代理项(0xD800–0xDBFF)意味着下一码元才是配对的一半:退一格。
|
|
33
|
+
if (unit >= 0xd800 && unit <= 0xdbff)
|
|
34
|
+
end -= 1;
|
|
35
|
+
return toWellFormed(text.slice(0, Math.max(0, end)));
|
|
36
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angelky7/dsh-browser-control",
|
|
3
3
|
"description": "Local WebSocket bridge between dsh and the DSH Browser Control browser extension, with model-facing browser_* tools and a Settings-managed on/off switch",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"build": "tsc -p tsconfig.json && tsdown",
|
|
38
38
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
39
39
|
"check": "npm run typecheck && npm run build && publint",
|
|
40
|
-
"verify": "node verify/verify_config_patch.mjs && node verify/verify_peers.mjs && node verify/verify_surface.mjs && node verify/verify_tab_groups.mjs && node verify/verify_tabs_close_many.mjs && node verify/verify_cdp_retry.mjs && node verify/verify_snapshot_ax.mjs && node verify/verify_hard_lock.mjs && node verify/verify_policy.mjs && node verify/verify_cookies.mjs && node verify/verify_extension_e2e.mjs && node verify/verify_plugin.mjs legacy && node verify/verify_plugin.mjs conflict && node verify/verify_plugin.mjs new && node verify/verify_http.mjs && node verify/verify_errors.mjs && node verify/verify_server_hardening.mjs && node verify/verify_hold.mjs && node verify/verify_protocol.mjs && node verify/verify_bundle_http.mjs && node verify/verify_nav_report.mjs && node verify/verify_nav_landing.mjs && node verify/verify_render_contract.mjs && node verify/verify_press_events.mjs && node verify/verify_nav_timeout.mjs && node verify/verify_nav_waituntil.mjs && node verify/verify_archive.mjs && node verify/verify_windows.mjs && node verify/verify_history_nav.mjs && node verify/verify_metrics_throttle.mjs && node verify/verify_drag.mjs && node verify/verify_audit.mjs && node verify/verify_journal.mjs && node verify/verify_sessions.mjs && node verify/verify_snapshot_page.mjs && node verify/verify_compat_matrix.mjs"
|
|
40
|
+
"verify": "node verify/verify_config_patch.mjs && node verify/verify_peers.mjs && node verify/verify_surface.mjs && node verify/verify_tab_groups.mjs && node verify/verify_tabs_close_many.mjs && node verify/verify_cdp_retry.mjs && node verify/verify_snapshot_ax.mjs && node verify/verify_hard_lock.mjs && node verify/verify_policy.mjs && node verify/verify_cookies.mjs && node verify/verify_extension_e2e.mjs && node verify/verify_plugin.mjs legacy && node verify/verify_plugin.mjs conflict && node verify/verify_plugin.mjs new && node verify/verify_http.mjs && node verify/verify_errors.mjs && node verify/verify_server_hardening.mjs && node verify/verify_text_safety.mjs && node verify/verify_hold.mjs && node verify/verify_protocol.mjs && node verify/verify_bundle_http.mjs && node verify/verify_nav_report.mjs && node verify/verify_nav_landing.mjs && node verify/verify_render_contract.mjs && node verify/verify_press_events.mjs && node verify/verify_nav_timeout.mjs && node verify/verify_nav_waituntil.mjs && node verify/verify_archive.mjs && node verify/verify_windows.mjs && node verify/verify_history_nav.mjs && node verify/verify_metrics_throttle.mjs && node verify/verify_drag.mjs && node verify/verify_audit.mjs && node verify/verify_journal.mjs && node verify/verify_sessions.mjs && node verify/verify_snapshot_page.mjs && node verify/verify_compat_matrix.mjs"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public",
|