@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24

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 (327) hide show
  1. package/README.md +102 -194
  2. package/README.zh-CN.md +115 -210
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-bootstrap.js +466 -35
  5. package/bin/gotry-inner.js +255 -36
  6. package/bin/gotry-process-liveness.d.ts +27 -0
  7. package/bin/gotry-process-liveness.js +97 -0
  8. package/bin/gotry-runtime-resolution.d.ts +1 -1
  9. package/bin/gotry-runtime-resolution.js +2 -2
  10. package/client/client.js +174 -0
  11. package/cordis.gotry-patch.yml +70 -93
  12. package/dist/capabilities/agent-reach-deep.js +45 -122
  13. package/dist/capabilities/agent-reach.js +63 -148
  14. package/dist/capabilities/anything.js +89 -75
  15. package/dist/capabilities/artifacts.js +123 -104
  16. package/dist/capabilities/channel-health.js +65 -45
  17. package/dist/capabilities/channel-registry.js +108 -87
  18. package/dist/capabilities/doctor.js +357 -235
  19. package/dist/capabilities/effect.js +223 -309
  20. package/dist/capabilities/fact-log.js +28 -14
  21. package/dist/capabilities/flyai.js +245 -156
  22. package/dist/capabilities/ground-transfer.js +509 -0
  23. package/dist/capabilities/hbcli.js +144 -148
  24. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  25. package/dist/capabilities/incident-log.js +131 -61
  26. package/dist/capabilities/model-override.js +30 -10
  27. package/dist/capabilities/opensky.js +54 -43
  28. package/dist/capabilities/resilience.js +50 -32
  29. package/dist/capabilities/session/action-cache.js +50 -44
  30. package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
  31. package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
  32. package/dist/capabilities/session/adapters/dida-portal.js +206 -0
  33. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  34. package/dist/capabilities/session/adapters/rail-12306.js +169 -112
  35. package/dist/capabilities/session/benchmark.js +85 -149
  36. package/dist/capabilities/session/extension-bridge.js +320 -259
  37. package/dist/capabilities/session/extension-channel.js +57 -76
  38. package/dist/capabilities/session/extension-distribution.js +115 -119
  39. package/dist/capabilities/session/extract.js +31 -21
  40. package/dist/capabilities/session/golden-score.js +55 -33
  41. package/dist/capabilities/session/health-watch.js +146 -154
  42. package/dist/capabilities/session/read-guard.js +55 -47
  43. package/dist/capabilities/session/static-flight-golden.js +49 -36
  44. package/dist/capabilities/session/transport.js +54 -61
  45. package/dist/capabilities/session/wizard.js +63 -93
  46. package/dist/capabilities/session-consent.js +143 -47
  47. package/dist/capabilities/session-login.js +95 -126
  48. package/dist/capabilities/session-search.js +391 -253
  49. package/dist/capabilities/sponsor-plugin.js +341 -0
  50. package/dist/capabilities/visa-policy.js +87 -73
  51. package/dist/capabilities/weather.js +163 -235
  52. package/dist/scripts/action-cache-tests.js +24 -59
  53. package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
  54. package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
  55. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  56. package/dist/scripts/agent-reach-tests.js +26 -18
  57. package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
  58. package/dist/scripts/anything-tests.js +58 -62
  59. package/dist/scripts/artifact-client-contract-tests.js +69 -0
  60. package/dist/scripts/artifacts-capability-tests.js +235 -0
  61. package/dist/scripts/async-collect.js +26 -15
  62. package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
  63. package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
  64. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
  65. package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
  66. package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
  67. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
  68. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
  69. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
  70. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
  71. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
  72. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
  73. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
  74. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
  75. package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
  76. package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
  77. package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
  78. package/dist/scripts/booking-executor-tests.js +80 -0
  79. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  80. package/dist/scripts/booking-saga-tests.js +61 -123
  81. package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
  82. package/dist/scripts/bootstrap-tests.js +1383 -128
  83. package/dist/scripts/build-changelog.js +89 -130
  84. package/dist/scripts/build-metrics-report.js +135 -143
  85. package/dist/scripts/changelog-tests.js +35 -82
  86. package/dist/scripts/channel-probe-tests.js +44 -116
  87. package/dist/scripts/channel-probe.js +84 -167
  88. package/dist/scripts/channel-registry-tests.js +97 -185
  89. package/dist/scripts/companion-tests.js +25 -70
  90. package/dist/scripts/dida-cookie-probe.js +87 -0
  91. package/dist/scripts/dida-sw-probe.js +114 -0
  92. package/dist/scripts/diff-test.js +17 -12
  93. package/dist/scripts/doctor-tests.js +428 -120
  94. package/dist/scripts/dsh-artifact-e2e.js +150 -0
  95. package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
  96. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  97. package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
  98. package/dist/scripts/dsh-runtime-closure.js +75 -63
  99. package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
  100. package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
  101. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  102. package/dist/scripts/effect-tests.js +165 -552
  103. package/dist/scripts/engine-run.js +1 -3
  104. package/dist/scripts/engine-tests.js +16 -10
  105. package/dist/scripts/evaluation-cadence-tests.js +69 -242
  106. package/dist/scripts/evaluation-contract-tests.js +193 -499
  107. package/dist/scripts/extension-distribution-cli.js +19 -15
  108. package/dist/scripts/extension-distribution-tests.js +98 -211
  109. package/dist/scripts/extension-tests.js +366 -389
  110. package/dist/scripts/fact-gate-tests.js +725 -263
  111. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  112. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  113. package/dist/scripts/flight-malformed-tests.js +159 -0
  114. package/dist/scripts/flyai-tests.js +325 -68
  115. package/dist/scripts/g5-guard-tests.js +131 -0
  116. package/dist/scripts/g5-guard.js +189 -0
  117. package/dist/scripts/gen-lunar-anchors.js +106 -0
  118. package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
  119. package/dist/scripts/ground-transfer-tests.js +956 -0
  120. package/dist/scripts/hbcli-e2e-tests.js +71 -90
  121. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  122. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  123. package/dist/scripts/hbcli-tests.js +41 -115
  124. package/dist/scripts/health-watch-cli.js +42 -28
  125. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  126. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  127. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  128. package/dist/scripts/i18n-tests.js +16 -9
  129. package/dist/scripts/incident-tests.js +281 -87
  130. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  131. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  132. package/dist/scripts/issue-271-liveness-tests.js +547 -0
  133. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  134. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  135. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  136. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  137. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  138. package/dist/scripts/journey-tests.js +22 -27
  139. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  140. package/dist/scripts/kernel-manifest-gate.js +126 -0
  141. package/dist/scripts/kernel-manifest-tests.js +212 -0
  142. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  143. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  144. package/dist/scripts/kernel-manifest.js +369 -0
  145. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  146. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  147. package/dist/scripts/ledger-tests.js +325 -340
  148. package/dist/scripts/ledger-workflow-crash.js +10 -20
  149. package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
  150. package/dist/scripts/memory-capture-tests.js +242 -61
  151. package/dist/scripts/memory-decay-tests.js +18 -52
  152. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  153. package/dist/scripts/memory-lifecycle.js +196 -0
  154. package/dist/scripts/memory-metrics.js +24 -12
  155. package/dist/scripts/memory-value-report.js +474 -222
  156. package/dist/scripts/memory-value-tests.js +328 -0
  157. package/dist/scripts/metrics-report-tests.js +62 -199
  158. package/dist/scripts/model-override-e2e.js +66 -105
  159. package/dist/scripts/nightly-evidence-tests.js +64 -89
  160. package/dist/scripts/nightly-evidence.js +108 -111
  161. package/dist/scripts/nudge-digest.js +39 -28
  162. package/dist/scripts/onboarding-tests.js +63 -88
  163. package/dist/scripts/opensky-check.js +18 -13
  164. package/dist/scripts/opensky-tests.js +28 -28
  165. package/dist/scripts/persona-surface-guard-tests.js +37 -12
  166. package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
  167. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  168. package/dist/scripts/price-drift-tests.js +126 -227
  169. package/dist/scripts/price-drift-watch.js +209 -253
  170. package/dist/scripts/probe-poi-tests.js +52 -45
  171. package/dist/scripts/product-metrics.js +187 -273
  172. package/dist/scripts/publish-preverify.js +47 -41
  173. package/dist/scripts/realtime-pricing-tests.js +37 -76
  174. package/dist/scripts/replay-async.js +20 -18
  175. package/dist/scripts/replay-real.js +16 -18
  176. package/dist/scripts/replay.js +52 -82
  177. package/dist/scripts/session-attach-diagnose.js +16 -12
  178. package/dist/scripts/session-attach-poc.js +34 -37
  179. package/dist/scripts/session-benchmark.js +66 -147
  180. package/dist/scripts/session-dida-live-e2e.js +151 -0
  181. package/dist/scripts/session-extract-tests.js +14 -35
  182. package/dist/scripts/session-login.js +14 -13
  183. package/dist/scripts/session-tests.js +545 -583
  184. package/dist/scripts/sf-live-benchmark.js +92 -110
  185. package/dist/scripts/sf-live-cli-tests.js +2 -9
  186. package/dist/scripts/sf-soft-score-tests.js +19 -60
  187. package/dist/scripts/sf-summary-tests.js +149 -0
  188. package/dist/scripts/sf-summary.js +466 -84
  189. package/dist/scripts/skeleton-check.js +16 -14
  190. package/dist/scripts/skeleton-integration-test.js +11 -10
  191. package/dist/scripts/skills-contract-tests.js +51 -46
  192. package/dist/scripts/smoke-session-gate-tests.js +12 -20
  193. package/dist/scripts/smoke.js +541 -422
  194. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  195. package/dist/scripts/state-cli-tests.js +226 -101
  196. package/dist/scripts/state-cli.js +549 -212
  197. package/dist/scripts/static-golden-tests.js +46 -180
  198. package/dist/scripts/time-eval-tests.js +210 -192
  199. package/dist/scripts/travel-timeline-tests.js +29 -93
  200. package/dist/scripts/turn-handoff-collect-tests.js +46 -93
  201. package/dist/scripts/turn-handoff-collect.js +71 -99
  202. package/dist/scripts/turn-policy-tests.js +28 -29
  203. package/dist/scripts/typed-contract-canary.js +77 -162
  204. package/dist/scripts/tz-resolver-sanity.js +50 -0
  205. package/dist/scripts/tz-resolver-tests.js +584 -0
  206. package/dist/scripts/unified-tests.js +23 -19
  207. package/dist/scripts/visa-policy-tests.js +31 -44
  208. package/dist/scripts/weather-tests.js +121 -543
  209. package/dist/scripts/wish-channel-gate-tests.js +29 -86
  210. package/dist/scripts/write-gate-tests.js +522 -0
  211. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  212. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  213. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  214. package/dist/scripts/z3-race-tests.js +18 -12
  215. package/dist/src/artifact-gate.js +652 -221
  216. package/dist/src/backend/kernel.js +91 -0
  217. package/dist/src/backend/modules/booking-copilot.js +55 -0
  218. package/dist/src/backend/modules/booking-executor.js +124 -0
  219. package/dist/src/backend/modules/session-search.js +194 -0
  220. package/dist/src/benchmark-agent-conformance.js +296 -262
  221. package/dist/src/benchmark-environment-bridge.js +298 -413
  222. package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
  223. package/dist/src/benchmark-tool-isolation.js +94 -67
  224. package/dist/src/bookable-facts.js +299 -174
  225. package/dist/src/booking-saga.js +64 -120
  226. package/dist/src/booking-surface/availability-policy.js +443 -576
  227. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  228. package/dist/src/booking-surface/contracts.js +24 -80
  229. package/dist/src/booking-surface/dsh-planner.js +510 -281
  230. package/dist/src/booking-surface/dsh-plugin.js +17 -1
  231. package/dist/src/booking-surface/error-codes.js +82 -10
  232. package/dist/src/booking-surface/index.js +0 -3
  233. package/dist/src/booking-surface/profile.js +10 -54
  234. package/dist/src/booking-surface/recovery-chain.js +367 -0
  235. package/dist/src/booking-surface/runtime.js +765 -891
  236. package/dist/src/booking-surface/server.js +200 -251
  237. package/dist/src/booking-surface/startup.js +58 -54
  238. package/dist/src/booking-surface/validation.js +150 -218
  239. package/dist/src/bridge.js +20 -13
  240. package/dist/src/companions.js +48 -74
  241. package/dist/src/contracts.js +21 -43
  242. package/dist/src/dsh-llm.js +82 -120
  243. package/dist/src/engine.js +253 -239
  244. package/dist/src/evaluation-cadence.js +72 -209
  245. package/dist/src/evaluation-contracts.js +220 -843
  246. package/dist/src/flight-pack-adapter.js +54 -0
  247. package/dist/src/flight-pack-contract.js +2 -0
  248. package/dist/src/gotry-backend.js +70 -0
  249. package/dist/src/hotel-date-gate.js +118 -0
  250. package/dist/src/i18n.js +18 -7
  251. package/dist/src/index.js +1425 -1629
  252. package/dist/src/journey.js +132 -81
  253. package/dist/src/ledger-repair-apply.js +538 -0
  254. package/dist/src/ledger-repair-plan.js +463 -0
  255. package/dist/src/loop.js +257 -166
  256. package/dist/src/memory-capture.js +180 -32
  257. package/dist/src/memory-decay.js +30 -14
  258. package/dist/src/memory-lifecycle.js +1310 -0
  259. package/dist/src/memory-utility.js +27 -40
  260. package/dist/src/mock-llm.js +36 -61
  261. package/dist/src/model.js +81 -36
  262. package/dist/src/realtime-pricing.js +63 -72
  263. package/dist/src/slot-spec.js +84 -106
  264. package/dist/src/state-ledger.js +310 -471
  265. package/dist/src/subagent-job-id-guard.js +58 -0
  266. package/dist/src/time-anchor.js +231 -33
  267. package/dist/src/tool-packet.js +20 -8
  268. package/dist/src/travel-slots.js +58 -35
  269. package/dist/src/travel-timeline.js +44 -63
  270. package/dist/src/turn-deadline.js +121 -127
  271. package/dist/src/turn-policy.js +61 -89
  272. package/dist/src/tz-resolver.js +143 -0
  273. package/dist/src/unified.js +504 -381
  274. package/dist/src/wish-pool.js +39 -14
  275. package/dist/src/write-gate.js +495 -0
  276. package/dist/src/z3-shared.js +273 -7
  277. package/extension/README.md +4 -3
  278. package/extension/background.js +125 -14
  279. package/extension/content-bridge.js +23 -1
  280. package/extension/content-main.js +15 -4
  281. package/extension/manifest.json +16 -8
  282. package/package.json +251 -220
  283. package/ts/capabilities/agent-reach.ts +4 -4
  284. package/ts/capabilities/anything.ts +1 -1
  285. package/ts/capabilities/artifacts.ts +46 -9
  286. package/ts/capabilities/flyai.ts +156 -37
  287. package/ts/capabilities/hbcli.ts +1 -1
  288. package/ts/capabilities/incident-log.ts +88 -59
  289. package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
  290. package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
  291. package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
  292. package/ts/capabilities/session/extension-bridge.ts +226 -101
  293. package/ts/capabilities/session/extension-channel.ts +22 -9
  294. package/ts/capabilities/session/wizard.ts +2 -2
  295. package/ts/capabilities/session-consent.ts +98 -14
  296. package/ts/capabilities/session-login.ts +1 -0
  297. package/ts/capabilities/session-search.ts +315 -31
  298. package/ts/dsh-runtime/vendor/README.md +8 -4
  299. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
  300. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
  301. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
  302. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
  303. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
  304. package/ts/package.json +236 -16
  305. package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
  306. package/ts/scripts/state-cli.ts +418 -51
  307. package/ts/src/artifact-gate.ts +561 -15
  308. package/ts/src/benchmark-environment-bridge.ts +37 -20
  309. package/ts/src/bookable-facts.ts +183 -7
  310. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  311. package/ts/src/booking-surface/dsh-planner.ts +240 -32
  312. package/ts/src/booking-surface/dsh-plugin.js +17 -1
  313. package/ts/src/booking-surface/error-codes.ts +75 -0
  314. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  315. package/ts/src/booking-surface/server.ts +84 -51
  316. package/ts/src/booking-surface/validation.ts +2 -0
  317. package/ts/src/contracts.ts +12 -2
  318. package/ts/src/dsh-llm.ts +4 -8
  319. package/ts/src/index.ts +609 -90
  320. package/ts/src/loop.ts +116 -7
  321. package/ts/src/memory-capture.ts +227 -3
  322. package/ts/src/mock-llm.ts +4 -8
  323. package/ts/src/model.ts +88 -2
  324. package/ts/src/state-ledger.ts +47 -29
  325. package/ts/src/subagent-job-id-guard.ts +72 -0
  326. package/ts/src/time-anchor.ts +217 -13
  327. package/ts/src/unified.ts +316 -54
@@ -1,95 +1,76 @@
1
+ /**
2
+ * 扩展车道客户端(RFC §2.2 通道 C,2026-08-29 PRIMARY;issue #21 传输层方案 B)。
3
+ *
4
+ * 三种 job 封装:search(被动嗅探 batchSearch)/ open-login(登录入口置前台)/
5
+ * cookie-names(票据 cookie **名字**,协议面不存在值字段)。verdict 映射纯函数
6
+ * `classifyBridgeFailure` 供回归覆盖:
7
+ * - extension-not-connected → 'needs-extension'(waiting no-spend,一次性安装指引;
8
+ * **不静默回退 CDP**——回退即重新引入逐连接权限框,正是本通道要消灭的摩擦);
9
+ * - bridge-unavailable(端口池全占等环境故障)→ 'error'(非用户门)。
10
+ * 检索页无回包不是车道失败:与 CDP 车道同语义(到点无 hints 命中 ⇒ miss,页标题命挑战 ⇒ challenged)。
11
+ */
1
12
  import { getOrCreateSessionBridge, needsExtensionSummary } from './extension-bridge.js';
13
+ /**
14
+ * needs-extension 用户门文案(D-24 自适应,issue #117):按本地通道是否落位自动跳过
15
+ * 开发者模式/本地通道指引——商店版用户只看到商店一键装与「已装即可」提示。
16
+ * 旧静态常量已退役:静态文案无法感知安装状态,正是 D-24 残量的病灶。
17
+ */
2
18
  export function needsExtensionHint() {
3
19
  return needsExtensionSummary();
4
20
  }
21
+ /** 桥故障 → 用户门 verdict 纯函数(测试锚点):只有「扩展未连接」是用户门(waiting),其余降级 error */
5
22
  export function classifyBridgeFailure(kind) {
6
23
  return kind === 'extension-not-connected' ? 'needs-extension' : 'error';
7
24
  }
8
25
  function bridgeReady(b) {
9
26
  return b.ok === true;
10
27
  }
11
- export async function extensionCookieNames(q) {
12
- const bridge = await getOrCreateSessionBridge();
13
- if (!bridgeReady(bridge)) return {
14
- ok: false,
15
- kind: 'bridge-unavailable',
16
- summary: bridge.summary
17
- };
18
- const outcome = await bridge.bridge.submit({
19
- kind: 'cookie-names',
20
- site: q.site,
21
- timeoutMs: q.timeoutMs ?? 8_000
22
- }, {
23
- timeoutMs: q.timeoutMs ?? 8_000
24
- });
25
- if (!outcome.ok) return {
26
- ok: false,
27
- kind: outcome.reason,
28
- summary: outcome.summary
29
- };
30
- const names = Array.isArray(outcome.result.names) ? outcome.result.names.filter((n)=>typeof n === 'string') : [];
31
- return {
32
- ok: true,
33
- tickets: names
34
- };
28
+ /** 桥解析(2026-09-11):gotry-backend 服务形态注入进程内队列;桌面形态缺省走 loopback 懒单例 */
29
+ async function resolveBridge(injected) {
30
+ if (injected)
31
+ return { ok: true, bridge: injected };
32
+ return getOrCreateSessionBridge();
35
33
  }
36
- export async function extensionOpenLogin(q) {
37
- const bridge = await getOrCreateSessionBridge();
38
- if (!bridgeReady(bridge)) return {
39
- ok: false,
40
- kind: 'bridge-unavailable',
41
- summary: bridge.summary
42
- };
43
- const outcome = await bridge.bridge.submit({
44
- kind: 'open-login',
45
- site: q.site,
46
- url: q.url
47
- }, {
48
- timeoutMs: q.timeoutMs ?? 15_000
49
- });
50
- if (!outcome.ok) return {
51
- ok: false,
52
- kind: outcome.reason,
53
- summary: outcome.summary
54
- };
55
- if (!outcome.result.opened) return {
56
- ok: false,
57
- kind: 'job-error',
58
- summary: outcome.result.error ?? '登录入口打开失败'
59
- };
60
- return {
61
- ok: true,
62
- opened: true
63
- };
34
+ /** cookie-names job:票据 cookie 名存在性(名字级;协议不含值) */
35
+ export async function extensionCookieNames(q, bridgeHandle) {
36
+ const bridge = await resolveBridge(bridgeHandle);
37
+ if (!bridgeReady(bridge))
38
+ return { ok: false, kind: 'bridge-unavailable', summary: bridge.summary };
39
+ const outcome = await bridge.bridge.submit({ kind: 'cookie-names', site: q.site, timeoutMs: q.timeoutMs ?? 8_000 }, { timeoutMs: q.timeoutMs ?? 8_000 });
40
+ if (!outcome.ok)
41
+ return { ok: false, kind: outcome.reason, summary: outcome.summary };
42
+ const names = Array.isArray(outcome.result.names) ? outcome.result.names.filter((n) => typeof n === 'string') : [];
43
+ // 红线自证:隧道面只有名字(extension-tests §红线断言协议面无值字段)
44
+ return { ok: true, tickets: names };
64
45
  }
65
- export async function extensionSearchJob(q) {
66
- const bridge = await getOrCreateSessionBridge();
67
- if (!bridgeReady(bridge)) return {
68
- ok: false,
69
- kind: 'bridge-unavailable',
70
- summary: bridge.summary
71
- };
46
+ /** 登录入口置前台打开(#34 纪律:标签留给用户,扩展侧不 close) */
47
+ export async function extensionOpenLogin(q, bridgeHandle) {
48
+ const bridge = await resolveBridge(bridgeHandle);
49
+ if (!bridgeReady(bridge))
50
+ return { ok: false, kind: 'bridge-unavailable', summary: bridge.summary };
51
+ const outcome = await bridge.bridge.submit({ kind: 'open-login', site: q.site, url: q.url }, { timeoutMs: q.timeoutMs ?? 15_000 });
52
+ if (!outcome.ok)
53
+ return { ok: false, kind: outcome.reason, summary: outcome.summary };
54
+ if (!outcome.result.opened)
55
+ return { ok: false, kind: 'job-error', summary: outcome.result.error ?? '登录入口打开失败' };
56
+ return { ok: true, opened: true };
57
+ }
58
+ /** 检索 job:后台标签打开 entry,等 content hook 的嗅探回包;页无响应=timedOut(非车道失败) */
59
+ export async function extensionSearchJob(q, bridgeHandle) {
60
+ const bridge = await resolveBridge(bridgeHandle);
61
+ if (!bridgeReady(bridge))
62
+ return { ok: false, kind: 'bridge-unavailable', summary: bridge.summary };
72
63
  const timeoutMs = q.timeoutMs ?? 30_000;
73
- const outcome = await bridge.bridge.submit({
74
- kind: 'search',
75
- site: q.site,
76
- url: q.url,
77
- timeoutMs
78
- }, {
79
- timeoutMs: timeoutMs + 10_000
80
- });
81
- if (!outcome.ok) return {
82
- ok: false,
83
- kind: outcome.reason,
84
- summary: outcome.summary
85
- };
64
+ const outcome = await bridge.bridge.submit({ kind: 'search', site: q.site, url: q.url, timeoutMs, ...(q.multiCollect ? { multiCollect: true } : {}) }, { timeoutMs: timeoutMs + 10_000 });
65
+ if (!outcome.ok)
66
+ return { ok: false, kind: outcome.reason, summary: outcome.summary };
67
+ const bodies = outcome.result.bodies;
86
68
  return {
87
69
  ok: true,
88
70
  body: typeof outcome.result.body === 'string' ? outcome.result.body : '',
71
+ url: typeof outcome.result.url === 'string' ? outcome.result.url : '',
89
72
  title: typeof outcome.result.title === 'string' ? outcome.result.title : '',
90
- timedOut: outcome.result.timeout === true
73
+ timedOut: outcome.result.timeout === true,
74
+ ...(bodies && typeof bodies === 'object' ? { bodies } : {}),
91
75
  };
92
76
  }
93
-
94
-
95
- //# sourceURL=ts/capabilities/session/extension-channel.ts
@@ -1,35 +1,65 @@
1
+ /**
2
+ * 扩展产物分发(issue #21 分发通道,ADR-21;founder 2026-08-30「产物下载和安装也得做成更好的
3
+ * 用户体验,可以用 github 作为分发渠道」)。
4
+ *
5
+ * 平台硬约束(诚实前置):Chrome 禁止普通用户从任意 URL 安装打包 CRX——GitHub 只能改善
6
+ * 「下载」(版本化/回滚/与 npm 发版节奏解耦),改善不了「安装」;「开发者模式加载已解压」
7
+ * 那 3 次点击只有上架 Chrome Web Store 才能消掉(商店版自动更新,见 extension-webstore-submission.md)。
8
+ * manifest 固定 key ⇒ 双通道是同一个扩展 ID(olpgkofjhhiiiahdkkbcninhjmegghfe),端口池/
9
+ * host_permissions 不随通道漂移——本模块用 key 钉住这一不变量。
10
+ *
11
+ * 单一职责:① 产物源解析(bundled 默认离线确定性 | github 显式 opt-in);
12
+ * ② GitHub Releases 下载链:dist-manifest → tar.gz → SHA256 校验 → tar 解压 → key 钉扎校验
13
+ * → 版本比较 → 原子交换 ~/.gotry/extension;
14
+ * ③ 失败语义:github 源任何失败(无网/404/校验不过/key 漂移)→ 结构化 fallback bundled,
15
+ * 绝不挡 setup(安装外部产物永远不挡 gotry 本体——bootstrap 契约)。
16
+ *
17
+ * 信任边界(诚实声明):TLS 传输 + 同源 dist-manifest 哈希完整性——防截断/损坏/误传资产,
18
+ * 不防仓库本身被攻陷(无独立 pinning;独立 pin 需把哈希烘进 npm 包,与「扩展更新与 npm
19
+ * 发版解耦」目标冲突,明确不做)。
20
+ *
21
+ * 零新 npm 依赖:fetch 走 Node ≥22 全局;解压走平台原生 `tar -xzf`(macOS/Linux/Windows10+
22
+ * 自带);SHA256 走 node:crypto。spawn/fetch 可注入(离线测试)。
23
+ */
1
24
  import { spawn } from 'node:child_process';
2
25
  import { createHash } from 'node:crypto';
3
26
  import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
27
  import { join } from 'node:path';
28
+ /** Releases 稳定资产名(每版覆盖同名上传;latest/download 永久链只认稳定名)。
29
+ * 与 scripts/package-extension.mjs 的资产名必须逐字一致——extension-distribution-tests 防漂移。 */
5
30
  export const DIST_ASSET_TARBALL = 'gotry-session-bridge.tar.gz';
6
31
  export const DIST_ASSET_STORE_ZIP = 'gotry-session-bridge-store.zip';
7
32
  export const DIST_ASSET_MANIFEST = 'extension-dist-manifest.json';
8
33
  export const DEFAULT_RELEASE_BASE = 'https://github.com/Danceiny/gotry/releases';
34
+ /** 纯函数:三段数字版本比较(a<b 负 / 相等 0 / a>b 正;非数字段按 0 处理,长度差补 0) */
9
35
  export function compareVersions(a, b) {
10
- const pa = String(a).split('.').map((s)=>Number.parseInt(s, 10) || 0);
11
- const pb = String(b).split('.').map((s)=>Number.parseInt(s, 10) || 0);
36
+ const pa = String(a).split('.').map((s) => Number.parseInt(s, 10) || 0);
37
+ const pb = String(b).split('.').map((s) => Number.parseInt(s, 10) || 0);
12
38
  const n = Math.max(pa.length, pb.length);
13
- for(let i = 0; i < n; i += 1){
39
+ for (let i = 0; i < n; i += 1) {
14
40
  const d = (pa[i] ?? 0) - (pb[i] ?? 0);
15
- if (d !== 0) return d;
41
+ if (d !== 0)
42
+ return d;
16
43
  }
17
44
  return 0;
18
45
  }
46
+ /** 纯函数:Releases latest/download 永久链(稳定资产名) */
19
47
  export function distAssetUrls(releaseBase = DEFAULT_RELEASE_BASE) {
20
48
  const base = releaseBase.replace(/\/+$/, '');
21
49
  return {
22
50
  tarball: `${base}/latest/download/${DIST_ASSET_TARBALL}`,
23
51
  storeZip: `${base}/latest/download/${DIST_ASSET_STORE_ZIP}`,
24
- manifest: `${base}/latest/download/${DIST_ASSET_MANIFEST}`
52
+ manifest: `${base}/latest/download/${DIST_ASSET_MANIFEST}`,
25
53
  };
26
54
  }
27
55
  const SHA256_RE = /^[0-9a-f]{64}$/;
56
+ /** 纯函数:dist-manifest 解析,fail-closed(坏 JSON/缺字段/资产名漂移/哈希非 64-hex 一律拒绝) */
28
57
  export function parseDistManifest(raw) {
29
58
  let obj;
30
59
  try {
31
60
  obj = JSON.parse(raw);
32
- } catch {
61
+ }
62
+ catch {
33
63
  throw new Error('dist-manifest 不是合法 JSON');
34
64
  }
35
65
  const version = obj.version;
@@ -37,198 +67,164 @@ export function parseDistManifest(raw) {
37
67
  const tarballSha256 = obj.tarballSha256;
38
68
  const zip = obj.zip;
39
69
  const zipSha256 = obj.zipSha256;
40
- if (typeof version !== 'string' || !/^\d+(\.\d+){1,3}$/.test(version)) throw new Error('dist-manifest.version 缺失或非 1-4 段点分整数');
41
- if (tarball !== DIST_ASSET_TARBALL) throw new Error(`dist-manifest.tarball 资产名漂移(期望 ${DIST_ASSET_TARBALL})`);
42
- if (zip !== DIST_ASSET_STORE_ZIP) throw new Error(`dist-manifest.zip 资产名漂移(期望 ${DIST_ASSET_STORE_ZIP})`);
43
- if (typeof tarballSha256 !== 'string' || !SHA256_RE.test(tarballSha256)) throw new Error('dist-manifest.tarballSha256 64-hex');
44
- if (typeof zipSha256 !== 'string' || !SHA256_RE.test(zipSha256)) throw new Error('dist-manifest.zipSha256 非 64-hex');
70
+ // Chrome manifest version 形态:1-4 段点分整数(扩展版本跟随 gotry 主版本
71
+ // 0.0.1-rc.N 四段投影 0.0.1.N,展示名走 version_name 原样)
72
+ if (typeof version !== 'string' || !/^\d+(\.\d+){1,3}$/.test(version))
73
+ throw new Error('dist-manifest.version 缺失或非 1-4 段点分整数');
74
+ if (tarball !== DIST_ASSET_TARBALL)
75
+ throw new Error(`dist-manifest.tarball 资产名漂移(期望 ${DIST_ASSET_TARBALL})`);
76
+ if (zip !== DIST_ASSET_STORE_ZIP)
77
+ throw new Error(`dist-manifest.zip 资产名漂移(期望 ${DIST_ASSET_STORE_ZIP})`);
78
+ if (typeof tarballSha256 !== 'string' || !SHA256_RE.test(tarballSha256))
79
+ throw new Error('dist-manifest.tarballSha256 非 64-hex');
80
+ if (typeof zipSha256 !== 'string' || !SHA256_RE.test(zipSha256))
81
+ throw new Error('dist-manifest.zipSha256 非 64-hex');
45
82
  return {
46
83
  version,
47
84
  tarball,
48
85
  tarballSha256,
49
86
  zip,
50
87
  zipSha256,
51
- builtFromCommit: typeof obj.builtFromCommit === 'string' ? obj.builtFromCommit : undefined
88
+ builtFromCommit: typeof obj.builtFromCommit === 'string' ? obj.builtFromCommit : undefined,
52
89
  };
53
90
  }
54
91
  export function sha256Hex(bytes) {
55
92
  return createHash('sha256').update(bytes).digest('hex');
56
93
  }
94
+ /** 读取扩展 manifest 的 key(固定 key ⇒ 固定扩展 ID;分发通道 ID 不变量的锚点) */
57
95
  export function readExtensionKey(extensionDir) {
58
96
  const p = join(extensionDir, 'manifest.json');
59
- if (!existsSync(p)) return undefined;
97
+ if (!existsSync(p))
98
+ return undefined;
60
99
  try {
61
100
  const m = JSON.parse(readFileSync(p, 'utf8'));
62
101
  return typeof m.key === 'string' ? m.key : undefined;
63
- } catch {
102
+ }
103
+ catch {
64
104
  return undefined;
65
105
  }
66
106
  }
67
107
  export function readExtensionVersion(extensionDir) {
68
108
  const p = join(extensionDir, 'manifest.json');
69
- if (!existsSync(p)) return undefined;
109
+ if (!existsSync(p))
110
+ return undefined;
70
111
  try {
71
112
  const m = JSON.parse(readFileSync(p, 'utf8'));
72
113
  return typeof m.version === 'string' ? m.version : undefined;
73
- } catch {
114
+ }
115
+ catch {
74
116
  return undefined;
75
117
  }
76
118
  }
77
119
  function runTar(args, spawnFn, timeoutMs) {
78
- return new Promise((resolve)=>{
79
- const child = spawnFn('tar', args, {
80
- stdio: 'ignore'
81
- });
120
+ return new Promise((resolve) => {
121
+ const child = spawnFn('tar', args, { stdio: 'ignore' });
82
122
  let done = false;
83
- const timer = setTimeout(()=>{
123
+ const timer = setTimeout(() => {
84
124
  if (!done) {
85
125
  done = true;
86
126
  try {
87
127
  child.kill('SIGKILL');
88
- } catch {}
89
- resolve({
90
- ok: false,
91
- error: `tar 超时(>${timeoutMs}ms)`
92
- });
128
+ }
129
+ catch {
130
+ /* ignore */
131
+ }
132
+ resolve({ ok: false, error: `tar 超时(>${timeoutMs}ms)` });
93
133
  }
94
134
  }, timeoutMs);
95
- child.on('error', (e)=>{
135
+ child.on('error', (e) => {
96
136
  if (!done) {
97
137
  done = true;
98
138
  clearTimeout(timer);
99
- resolve({
100
- ok: false,
101
- error: `tar 不可用:${e.message}`
102
- });
139
+ resolve({ ok: false, error: `tar 不可用:${e.message}` });
103
140
  }
104
141
  });
105
- child.on('close', (code)=>{
142
+ child.on('close', (code) => {
106
143
  if (!done) {
107
144
  done = true;
108
145
  clearTimeout(timer);
109
- resolve(code === 0 ? {
110
- ok: true
111
- } : {
112
- ok: false,
113
- error: `tar exit ${code}`
114
- });
146
+ resolve(code === 0 ? { ok: true } : { ok: false, error: `tar exit ${code}` });
115
147
  }
116
148
  });
117
149
  });
118
150
  }
119
151
  async function fetchBytes(url, fetchFn, timeoutMs) {
120
152
  try {
121
- const res = await fetchFn(url, {
122
- signal: AbortSignal.timeout(timeoutMs)
123
- });
124
- if (!res.ok) return {
125
- ok: false,
126
- error: `HTTP ${res.status} ${url}`
127
- };
153
+ const res = await fetchFn(url, { signal: AbortSignal.timeout(timeoutMs) });
154
+ if (!res.ok)
155
+ return { ok: false, error: `HTTP ${res.status} ${url}` };
128
156
  const buf = new Uint8Array(await res.arrayBuffer());
129
- return {
130
- ok: true,
131
- bytes: buf
132
- };
133
- } catch (e) {
134
- return {
135
- ok: false,
136
- error: `下载失败 ${e.message}`
137
- };
157
+ return { ok: true, bytes: buf };
158
+ }
159
+ catch (e) {
160
+ return { ok: false, error: `下载失败 ${e.message}` };
138
161
  }
139
162
  }
163
+ /**
164
+ * GitHub Releases → ~/.gotry/extension 安装链。
165
+ * 任何失败都返回 { ok:false, action:'fallback-bundled' }——调用方显式降级 bundled,
166
+ * 本函数不抛出(bootstrap 契约:安装外部产物永远不挡 gotry 本体)。
167
+ */
140
168
  export async function installExtensionFromGithub(opts) {
141
169
  const timeoutMs = opts.timeoutMs ?? 60_000;
142
170
  const fetchFn = opts.fetch ?? fetch;
143
171
  const spawnFn = opts.spawnFn ?? spawn;
144
172
  const urls = distAssetUrls(opts.releaseBase);
145
- const fail = (error)=>({
146
- ok: false,
147
- action: 'fallback-bundled',
148
- error
149
- });
173
+ const fail = (error) => ({ ok: false, action: 'fallback-bundled', error });
150
174
  const mRes = await fetchBytes(urls.manifest, fetchFn, timeoutMs);
151
- if (!mRes.ok) return fail(mRes.error);
175
+ if (!mRes.ok)
176
+ return fail(mRes.error);
152
177
  let manifest;
153
178
  try {
154
179
  manifest = parseDistManifest(new TextDecoder().decode(mRes.bytes));
155
- } catch (e) {
180
+ }
181
+ catch (e) {
156
182
  return fail(`dist-manifest 校验失败:${e.message}`);
157
183
  }
158
184
  const previousVersion = readExtensionVersion(opts.destDir);
159
185
  if (previousVersion != null && compareVersions(manifest.version, previousVersion) <= 0) {
160
- return {
161
- ok: true,
162
- action: 'up-to-date',
163
- version: manifest.version,
164
- previousVersion
165
- };
186
+ return { ok: true, action: 'up-to-date', version: manifest.version, previousVersion };
166
187
  }
167
188
  if (opts.checkOnly) {
168
- return {
169
- ok: true,
170
- action: 'up-to-date',
171
- version: manifest.version,
172
- previousVersion
173
- };
189
+ return { ok: true, action: 'up-to-date', version: manifest.version, previousVersion }; // check-only 语义:报告远端版本,不落盘
174
190
  }
175
191
  const tRes = await fetchBytes(urls.tarball, fetchFn, timeoutMs);
176
- if (!tRes.ok) return fail(tRes.error);
192
+ if (!tRes.ok)
193
+ return fail(tRes.error);
177
194
  const actual = sha256Hex(tRes.bytes);
178
- if (actual !== manifest.tarballSha256) return fail(`SHA256 不符(期望 ${manifest.tarballSha256.slice(0, 12)}… 实得 ${actual.slice(0, 12)}…)`);
195
+ if (actual !== manifest.tarballSha256)
196
+ return fail(`SHA256 不符(期望 ${manifest.tarballSha256.slice(0, 12)}… 实得 ${actual.slice(0, 12)}…)`);
197
+ // 落盘 → tar 解压 → key 钉扎 → 原子交换;incoming 目录同盘保证 rename 原子性
179
198
  const incoming = `${opts.destDir}.incoming-${process.pid}-${Date.now()}`;
180
199
  const tarPath = `${incoming}.tar.gz`;
181
200
  try {
182
- rmSync(incoming, {
183
- recursive: true,
184
- force: true
185
- });
186
- mkdirSync(incoming, {
187
- recursive: true
188
- });
201
+ rmSync(incoming, { recursive: true, force: true });
202
+ mkdirSync(incoming, { recursive: true });
189
203
  writeFileSync(tarPath, tRes.bytes);
190
- const ext = await runTar([
191
- '-xzf',
192
- tarPath,
193
- '-C',
194
- incoming,
195
- '--strip-components',
196
- '1'
197
- ], spawnFn, timeoutMs);
198
- if (!ext.ok) return fail(ext.error);
204
+ // 包内含顶层目录 gotry-session-bridge/;--strip-components 落到 incoming 根
205
+ const ext = await runTar(['-xzf', tarPath, '-C', incoming, '--strip-components', '1'], spawnFn, timeoutMs);
206
+ if (!ext.ok)
207
+ return fail(ext.error);
199
208
  const incomingKey = readExtensionKey(incoming);
200
- if (incomingKey == null) return fail('解压产物缺 manifest.json/key(非法包)');
209
+ if (incomingKey == null)
210
+ return fail('解压产物缺 manifest.json/key(非法包)');
201
211
  if (opts.pinnedSourceDir) {
202
212
  const pinnedKey = readExtensionKey(opts.pinnedSourceDir);
203
- if (pinnedKey != null && incomingKey !== pinnedKey) return fail('扩展 key 漂移(通道产物与 bundled 不同 ID——拒绝安装,防端口池/host 权限漂移)');
213
+ if (pinnedKey != null && incomingKey !== pinnedKey)
214
+ return fail('扩展 key 漂移(通道产物与 bundled 不同 ID——拒绝安装,防端口池/host 权限漂移)');
204
215
  }
205
216
  const incomingVersion = readExtensionVersion(incoming);
206
- if (incomingVersion !== manifest.version) return fail(`解压产物版本(${incomingVersion})与 dist-manifest(${manifest.version})不符`);
207
- rmSync(opts.destDir, {
208
- recursive: true,
209
- force: true
210
- });
211
- cpSync(incoming, opts.destDir, {
212
- recursive: true
213
- });
214
- return {
215
- ok: true,
216
- action: 'installed',
217
- version: manifest.version,
218
- previousVersion
219
- };
220
- } catch (e) {
217
+ if (incomingVersion !== manifest.version)
218
+ return fail(`解压产物版本(${incomingVersion})与 dist-manifest(${manifest.version})不符`);
219
+ rmSync(opts.destDir, { recursive: true, force: true });
220
+ cpSync(incoming, opts.destDir, { recursive: true });
221
+ return { ok: true, action: 'installed', version: manifest.version, previousVersion };
222
+ }
223
+ catch (e) {
221
224
  return fail(`落位失败 ${e.message}`);
222
- } finally{
223
- rmSync(incoming, {
224
- recursive: true,
225
- force: true
226
- });
227
- rmSync(tarPath, {
228
- force: true
229
- });
225
+ }
226
+ finally {
227
+ rmSync(incoming, { recursive: true, force: true });
228
+ rmSync(tarPath, { force: true });
230
229
  }
231
230
  }
232
-
233
-
234
- //# sourceURL=ts/capabilities/session/extension-distribution.ts
@@ -1,40 +1,50 @@
1
+ /**
2
+ * a11y 快照兜底抽取器(RFC §2.3 第②层:站点无干净 XHR JSON 时,从可访问性树快照抽结构化条目)。
3
+ * 纯函数:输入 Playwright ariaSnapshot/domSnapshot 文本,输出归一化条目(role/name/ref),
4
+ * 供确定性规则或 LLM 按 schema 消费;不做任何页面交互。
5
+ *
6
+ * 辑律(ADR-12 同款边界):抽取只认结构(行首 role 与缩进),不做开放式语义猜测;
7
+ * 页面文本是不可信输入(RFC §3.5)——本层只归形状,语义判定在上游 schema 处。
8
+ */
9
+ /**
10
+ * Playwright aria snapshot 行形如:
11
+ * `- button "搜索" [ref=s1e5]`
12
+ * `- heading "上海到丽江" [level=1]`
13
+ * 提取 role/name;无 name 的行(name='')保留占位(计数/结构比对仍可用)。
14
+ */
1
15
  export function extractA11yEntries(snapshotText, opts = {}) {
2
16
  const cap = opts.maxEntries ?? 500;
3
17
  const out = [];
4
18
  const lines = snapshotText.split('\n');
5
- for(let i = 0; i < lines.length && out.length < cap; i++){
19
+ for (let i = 0; i < lines.length && out.length < cap; i++) {
6
20
  const m = /^[\s*-]*\s*([a-zA-Z][\w-]*)(?:\s+"((?:[^"\\]|\\.)*)")?/.exec(lines[i] ?? '');
7
- if (!m) continue;
21
+ if (!m)
22
+ continue;
8
23
  const name = (m[2] ?? '').replace(/\\"/g, '"').slice(0, 200);
9
- out.push({
10
- role: m[1].toLowerCase(),
11
- name,
12
- line: i
13
- });
24
+ out.push({ role: m[1].toLowerCase(), name, line: i });
14
25
  }
15
26
  return out;
16
27
  }
28
+ /** DOM 提交件过滤入口(RFC §3.3-②):从条目集中剔除命中提交黑名单的可交互件,返回保留集 */
17
29
  export function filterSubmitEntries(entries, isSubmit) {
18
- const interactive = new Set([
19
- 'button',
20
- 'link',
21
- 'menuitem',
22
- 'option'
23
- ]);
24
- return entries.filter((e)=>!(interactive.has(e.role) && isSubmit(e.name)));
30
+ const interactive = new Set(['button', 'link', 'menuitem', 'option']);
31
+ return entries.filter((e) => !(interactive.has(e.role) && isSubmit(e.name)));
25
32
  }
33
+ /** 名称相似度粗筛(自愈定位的候选排序底座;0=不同串,1=包含) */
26
34
  export function nameAffinity(a, b) {
27
35
  const x = a.trim();
28
36
  const y = b.trim();
29
- if (!x || !y) return 0;
30
- if (x === y) return 1;
31
- if (x.includes(y) || y.includes(x)) return 0.8;
37
+ if (!x || !y)
38
+ return 0;
39
+ if (x === y)
40
+ return 1;
41
+ if (x.includes(y) || y.includes(x))
42
+ return 0.8;
32
43
  const sa = new Set(x);
33
44
  const sb = new Set(y);
34
45
  let common = 0;
35
- for (const ch of sa)if (sb.has(ch)) common += 1;
46
+ for (const ch of sa)
47
+ if (sb.has(ch))
48
+ common += 1;
36
49
  return common / Math.max(sa.size, sb.size);
37
50
  }
38
-
39
-
40
- //# sourceURL=ts/capabilities/session/extract.ts