@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,45 +1,58 @@
1
+ /**
2
+ * ReadGuard:会话检索面的只读物理隔离(RFC docs/rfc/user-session-data-rfc.md §3.3)。
3
+ *
4
+ * 写不是「被禁止的行为」,是「不存在的原语」——WriteGate(L0-L4)的检索态前置:
5
+ * ① 网络层:route 拦截——命中硬支付模式(任意方法)或写方法+写URL模式 → abort + 审计落盘;
6
+ * ② DOM 层:提交类按钮文本黑名单,从快照/断言文本中剔除(模型看不到可点的提交件);
7
+ * ③ fail-closed:guard 未装上,session transport 不允许导航(openSession 装不上即整会话失败)。
8
+ *
9
+ * 审计:append-only JSONL(session-incidents.jsonl,对齐 incident-log 惯例),隔离 stateRoot。
10
+ * 注意:搜索接口多为 POST(如携程 batchSearch),因此写判定必须「方法 × URL 模式」双因子,
11
+ * 不能一刀切禁 POST——batchSearch 不含写词,放行。
12
+ */
1
13
  import { mkdirSync, appendFileSync } from 'node:fs';
2
14
  import { dirname } from 'node:path';
15
+ /** 硬支付模式:任意方法(含 GET)都视为写——进支付/收银面本身就是风险 */
3
16
  const HARD_PAY_RE = /\/(pay|payment|cashier)(\/|\?|$)/i;
17
+ /** 驼峰复合写词(OTA 常见 /submitOrder、/createOrder):段式正则漏接,单列 */
4
18
  const WRITE_COMPOUND_RE = /(submitorder|createorder|placeorder|cancelorder|refundorder|orderconfirm|orderpay|confirmpay)/i;
19
+ /** 写词 URL 模式:仅在写方法(POST/PUT/DELETE/PATCH)下触发 */
5
20
  const WRITE_URL_RE = /\/(order|orders|submit|trade|booking|book|purchase|buy|create|cancel|refund)(\/|\?|$)/i;
6
- const WRITE_METHODS = new Set([
7
- 'POST',
8
- 'PUT',
9
- 'DELETE',
10
- 'PATCH'
11
- ]);
21
+ const WRITE_METHODS = new Set(['POST', 'PUT', 'DELETE', 'PATCH']);
22
+ /** DOM 提交件文本黑名单(§3.3-②):命中即从可交互面剔除 */
12
23
  export const SUBMIT_TEXT_RE = /下单|去支付|支付|预订|订票|购买|提交订单|确认订单/;
24
+ /** 网络层写判定(纯函数,测试锚点) */
13
25
  export function classifyRequest(method, url) {
14
- if (HARD_PAY_RE.test(url) || WRITE_COMPOUND_RE.test(url)) return 'block';
15
- if (WRITE_METHODS.has(method.toUpperCase()) && WRITE_URL_RE.test(url)) return 'block';
26
+ if (HARD_PAY_RE.test(url) || WRITE_COMPOUND_RE.test(url))
27
+ return 'block';
28
+ if (WRITE_METHODS.has(method.toUpperCase()) && WRITE_URL_RE.test(url))
29
+ return 'block';
16
30
  return 'allow';
17
31
  }
32
+ /** DOM 层提交件过滤(纯函数,测试锚点):true=剔除 */
18
33
  export function isSubmitText(text) {
19
34
  return SUBMIT_TEXT_RE.test(text ?? '');
20
35
  }
36
+ /**
37
+ * 装上网络层守卫(fail-closed 的「closed」在调用方:openSession 装不上 guard 即失败,不给无 guard 的会话)。
38
+ * 审计落 auditPath(JSONL append);auditPath 缺省不落盘(仅内存计数)。
39
+ */
21
40
  export async function attachReadGuard(ctx, auditPath) {
22
41
  let blocked = 0;
23
42
  let total = 0;
24
- await ctx.route('**/*', async (route)=>{
43
+ await ctx.route('**/*', async (route) => {
25
44
  const req = route.request();
26
45
  total += 1;
27
46
  const verdict = classifyRequest(req.method(), req.url());
28
47
  if (verdict === 'block') {
29
48
  blocked += 1;
30
49
  if (auditPath) {
31
- const entry = {
32
- ts: new Date().toISOString(),
33
- kind: 'blocked-write-request',
34
- method: req.method(),
35
- url: req.url().slice(0, 400)
36
- };
50
+ const entry = { ts: new Date().toISOString(), kind: 'blocked-write-request', method: req.method(), url: req.url().slice(0, 400) };
37
51
  try {
38
- mkdirSync(dirname(auditPath), {
39
- recursive: true
40
- });
52
+ mkdirSync(dirname(auditPath), { recursive: true });
41
53
  appendFileSync(auditPath, JSON.stringify(entry) + '\n');
42
- } catch {}
54
+ }
55
+ catch { /* 审计失败不阻塞拦截本身 */ }
43
56
  }
44
57
  await route.abort('blockedbyclient');
45
58
  return;
@@ -47,53 +60,48 @@ export async function attachReadGuard(ctx, auditPath) {
47
60
  await route.continue();
48
61
  });
49
62
  return {
50
- blockedCount: ()=>blocked,
51
- requestCount: ()=>total
63
+ blockedCount: () => blocked,
64
+ requestCount: () => total,
52
65
  };
53
66
  }
67
+ /**
68
+ * puppeteer 版守卫:对既有页面与新建页面全部开启请求拦截,写请求 abort + 审计。
69
+ * 与 playwright 版共用 classifyRequest 判定与审计条目(单一事实源)。
70
+ */
54
71
  export async function attachReadGuardPuppeteer(browser, auditPath) {
55
72
  let blocked = 0;
56
73
  let total = 0;
57
- const wire = (page)=>{
58
- void (async ()=>{
74
+ const wire = (page) => {
75
+ void (async () => {
59
76
  try {
60
77
  await page.setRequestInterception(true);
61
- page.on('request', async (req)=>{
78
+ page.on('request', async (req) => {
62
79
  total += 1;
63
80
  const verdict = classifyRequest(req.method(), req.url());
64
81
  if (verdict === 'block') {
65
82
  blocked += 1;
66
83
  if (auditPath) {
67
- const entry = {
68
- ts: new Date().toISOString(),
69
- kind: 'blocked-write-request',
70
- method: req.method(),
71
- url: req.url().slice(0, 400)
72
- };
84
+ const entry = { ts: new Date().toISOString(), kind: 'blocked-write-request', method: req.method(), url: req.url().slice(0, 400) };
73
85
  try {
74
- mkdirSync(dirname(auditPath), {
75
- recursive: true
76
- });
86
+ mkdirSync(dirname(auditPath), { recursive: true });
77
87
  appendFileSync(auditPath, JSON.stringify(entry) + '\n');
78
- } catch {}
88
+ }
89
+ catch { /* 审计失败不阻塞拦截本身 */ }
79
90
  }
80
- await req.abort('blockedbyclient').catch(()=>{});
91
+ await req.abort('blockedbyclient').catch(() => { });
81
92
  return;
82
93
  }
83
- await req.continue().catch(()=>{});
94
+ await req.continue().catch(() => { });
84
95
  });
85
- } catch {}
96
+ }
97
+ catch { /* 已关闭的 target 容忍 */ }
86
98
  })();
87
99
  };
88
- for (const p of (await browser.pages()))wire(p);
89
- browser.on('targetcreated', (t)=>{
90
- if (t.type() === 'page') void t.page().then(wire).catch(()=>{});
100
+ for (const p of await browser.pages())
101
+ wire(p);
102
+ browser.on('targetcreated', (t) => {
103
+ if (t.type() === 'page')
104
+ void t.page().then(wire).catch(() => { });
91
105
  });
92
- return {
93
- blockedCount: ()=>blocked,
94
- requestCount: ()=>total
95
- };
106
+ return { blockedCount: () => blocked, requestCount: () => total };
96
107
  }
97
-
98
-
99
- //# sourceURL=ts/capabilities/session/read-guard.ts
@@ -1,8 +1,9 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  export function parseGoldenSource(args) {
4
- const raw = args.find((arg)=>arg.startsWith('--golden='))?.slice('--golden='.length) ?? 'manual';
5
- if (raw === 'manual' || raw === 'flyai' || raw === 'static') return raw;
4
+ const raw = args.find((arg) => arg.startsWith('--golden='))?.slice('--golden='.length) ?? 'manual';
5
+ if (raw === 'manual' || raw === 'flyai' || raw === 'static')
6
+ return raw;
6
7
  throw new Error(`不支持的 golden vendor: ${raw}`);
7
8
  }
8
9
  export function loadStaticFlightSnapshot(path = join(import.meta.dirname, '..', '..', 'data', 'sf-static-routes.json')) {
@@ -11,13 +12,10 @@ export function loadStaticFlightSnapshot(path = join(import.meta.dirname, '..',
11
12
  throw new Error(`invalid static snapshot schema_version: ${String(isRecord(parsed) ? parsed.schema_version : undefined)}`);
12
13
  }
13
14
  const source = parsed.source;
14
- if (!isRecord(source) || ![
15
- 'name',
16
- 'url',
17
- 'revision',
18
- 'license',
19
- 'retrieved_at'
20
- ].every((key)=>typeof source[key] === 'string') || !Array.isArray(source.limitations) || !source.limitations.every((item)=>typeof item === 'string')) {
15
+ if (!isRecord(source)
16
+ || !['name', 'url', 'revision', 'license', 'retrieved_at'].every((key) => typeof source[key] === 'string')
17
+ || !Array.isArray(source.limitations)
18
+ || !source.limitations.every((item) => typeof item === 'string')) {
21
19
  throw new Error('invalid static snapshot source');
22
20
  }
23
21
  if (!Array.isArray(parsed.routes) || !parsed.routes.every(isStaticFlightRoute)) {
@@ -29,15 +27,26 @@ function isRecord(value) {
29
27
  return typeof value === 'object' && value !== null && !Array.isArray(value);
30
28
  }
31
29
  function isStaticFlightRoute(value) {
32
- if (!isRecord(value) || typeof value.query_id !== 'string' || typeof value.from_city !== 'string' || typeof value.to_city !== 'string' || value.route_kind !== 'direct' && value.route_kind !== 'transfer' || !Array.isArray(value.legs) || value.legs.length === 0) return false;
33
- return value.legs.every((leg)=>isRecord(leg) && typeof leg.from_iata === 'string' && typeof leg.to_iata === 'string' && Array.isArray(leg.carrier_codes) && leg.carrier_codes.every((carrier)=>typeof carrier === 'string'));
30
+ if (!isRecord(value)
31
+ || typeof value.query_id !== 'string'
32
+ || typeof value.from_city !== 'string'
33
+ || typeof value.to_city !== 'string'
34
+ || (value.route_kind !== 'direct' && value.route_kind !== 'transfer')
35
+ || !Array.isArray(value.legs)
36
+ || value.legs.length === 0)
37
+ return false;
38
+ return value.legs.every((leg) => isRecord(leg)
39
+ && typeof leg.from_iata === 'string'
40
+ && typeof leg.to_iata === 'string'
41
+ && Array.isArray(leg.carrier_codes)
42
+ && leg.carrier_codes.every((carrier) => typeof carrier === 'string'));
34
43
  }
35
44
  function provenance(snapshot) {
36
45
  return {
37
46
  route_source: snapshot?.source.url ?? '',
38
47
  route_revision: snapshot?.source.revision ?? '',
39
48
  route_license: snapshot?.source.license ?? '',
40
- band_source: 'sf-golden-manifest.json'
49
+ band_source: 'sf-golden-manifest.json',
41
50
  };
42
51
  }
43
52
  function formatWithOffset(timestampMs, offset) {
@@ -49,23 +58,24 @@ function formatWithOffset(timestampMs, offset) {
49
58
  }
50
59
  function buildSegments(query, route, manualSegment) {
51
60
  if (route.route_kind === 'direct' && route.legs.length === 1) {
52
- return [
53
- {
61
+ return [{
54
62
  from: query.from,
55
63
  to: query.to,
56
64
  departure_at: manualSegment.departure_at,
57
65
  arrival_at: manualSegment.arrival_at,
58
- transport_number: `${route.legs[0].carrier_codes[0]}-STATIC`
59
- }
60
- ];
66
+ transport_number: `${route.legs[0].carrier_codes[0]}-STATIC`,
67
+ }];
61
68
  }
62
- if (route.route_kind !== 'transfer' || route.legs.length !== 2) return null;
69
+ if (route.route_kind !== 'transfer' || route.legs.length !== 2)
70
+ return null;
63
71
  const departureMs = Date.parse(manualSegment.departure_at);
64
72
  const arrivalMs = Date.parse(manualSegment.arrival_at);
65
- if (!Number.isFinite(departureMs) || !Number.isFinite(arrivalMs) || arrivalMs <= departureMs) return null;
73
+ if (!Number.isFinite(departureMs) || !Number.isFinite(arrivalMs) || arrivalMs <= departureMs)
74
+ return null;
66
75
  const offset = manualSegment.departure_at.match(/([+-]\d{2}:\d{2})$/)?.[1] ?? '+08:00';
67
76
  const totalMinutes = Math.floor((arrivalMs - departureMs) / 60_000);
68
- if (totalMinutes < 90) return null;
77
+ if (totalMinutes < 90)
78
+ return null;
69
79
  const layoverMinutes = Math.min(60, Math.floor(totalMinutes / 3));
70
80
  const travelMinutes = totalMinutes - layoverMinutes;
71
81
  const firstTravelMinutes = Math.floor(travelMinutes / 2);
@@ -77,32 +87,38 @@ function buildSegments(query, route, manualSegment) {
77
87
  to: middle,
78
88
  departure_at: manualSegment.departure_at,
79
89
  arrival_at: formatWithOffset(departureMs + firstTravelMinutes * 60_000, offset),
80
- transport_number: `${route.legs[0].carrier_codes[0]}-STATIC`
90
+ transport_number: `${route.legs[0].carrier_codes[0]}-STATIC`,
81
91
  },
82
92
  {
83
93
  from: middle,
84
94
  to: query.to,
85
95
  departure_at: formatWithOffset(secondDepartureMs, offset),
86
96
  arrival_at: manualSegment.arrival_at,
87
- transport_number: `${route.legs[1].carrier_codes[0]}-STATIC`
88
- }
97
+ transport_number: `${route.legs[1].carrier_codes[0]}-STATIC`,
98
+ },
89
99
  ];
90
100
  }
91
101
  export function resolveStaticGolden(input) {
92
- const route = input.snapshot?.routes.find((candidate)=>candidate.query_id === input.query.id);
102
+ const route = input.snapshot?.routes.find((candidate) => candidate.query_id === input.query.id);
93
103
  let fallbackReason;
94
104
  if (input.snapshotError) {
95
105
  fallbackReason = `static snapshot unavailable: ${input.snapshotError}`;
96
- } else if (!route) {
106
+ }
107
+ else if (!route) {
97
108
  fallbackReason = `static route missing for ${input.query.id}`;
98
- } else if (route.from_city !== input.query.from || route.to_city !== input.query.to) {
109
+ }
110
+ else if (route.from_city !== input.query.from || route.to_city !== input.query.to) {
99
111
  fallbackReason = `static route mismatch for ${input.query.id}`;
100
- } else if (route.legs.some((leg)=>leg.carrier_codes.length === 0)) {
112
+ }
113
+ else if (route.legs.some((leg) => leg.carrier_codes.length === 0)) {
101
114
  fallbackReason = `static carrier coverage missing for ${input.query.id}`;
102
115
  }
103
116
  const manualSegment = input.manualRecord.route_segments[0];
104
- const segments = !fallbackReason && route && manualSegment ? buildSegments(input.query, route, manualSegment) : null;
105
- if (!fallbackReason && !segments) fallbackReason = `static route cannot form comparable record for ${input.query.id}`;
117
+ const segments = !fallbackReason && route && manualSegment
118
+ ? buildSegments(input.query, route, manualSegment)
119
+ : null;
120
+ if (!fallbackReason && !segments)
121
+ fallbackReason = `static route cannot form comparable record for ${input.query.id}`;
106
122
  if (fallbackReason) {
107
123
  input.warn?.(`[sf-live-benchmark] static vendor failed for ${input.query.id}: ${fallbackReason}; fallback=manual-golden`);
108
124
  return {
@@ -111,7 +127,7 @@ export function resolveStaticGolden(input) {
111
127
  record: input.manualRecord,
112
128
  fallback_reason: fallbackReason,
113
129
  estimated_fields: [],
114
- provenance: provenance(input.snapshot)
130
+ provenance: provenance(input.snapshot),
115
131
  };
116
132
  }
117
133
  return {
@@ -121,17 +137,14 @@ export function resolveStaticGolden(input) {
121
137
  ...input.manualRecord,
122
138
  route_segments: segments,
123
139
  journey_type: route.route_kind,
124
- source: 'static-openflights+manual-band'
140
+ source: 'static-openflights+manual-band',
125
141
  },
126
142
  estimated_fields: [
127
143
  'route_segments[].departure_at',
128
144
  'route_segments[].arrival_at',
129
145
  'route_segments[].transport_number',
130
- 'price'
146
+ 'price',
131
147
  ],
132
- provenance: provenance(input.snapshot)
148
+ provenance: provenance(input.snapshot),
133
149
  };
134
150
  }
135
-
136
-
137
- //# sourceURL=ts/capabilities/session/static-flight-golden.ts
@@ -1,13 +1,27 @@
1
+ /**
2
+ * SessionTransport(会话检索传输层,RFC §3.2;2026-08-28 定案 puppeteer-core):
3
+ * - mode=cdp(默认):attach 用户日常 Chrome——Chrome 144+ 调试服务(chrome://inspect 开关)
4
+ * 与 playwright 不兼容(握手后 CDP 初始化悬挂,实测三轮),与 puppeteer 完全兼容(实测 2256 cookies)。
5
+ * 发现走 user-data-dir 的 DevToolsActivePort 文件(HTTP 发现面已硬化 404;Origin 头=403,安全设计)。
6
+ * close() 只 disconnect,绝不关用户浏览器。
7
+ * - mode=persistent(测试/后备):puppeteer.launch 专用 profile(测试用 mktemp 隔离匿名态)。
8
+ * fail-closed(RFC §3.3-④):guard 装不上 = 整个会话打不开——不存在「无守卫的会话」这一形态。
9
+ * 永不抛错:任何启动失败降级为 TransportFailure,由 session-search 编排层消费。
10
+ */
1
11
  import { readFileSync } from 'node:fs';
2
12
  import { homedir } from 'node:os';
3
13
  import { attachReadGuardPuppeteer } from './read-guard.js';
14
+ /**
15
+ * puppeteer-core 为可选依赖(D-14:产品运行时零硬依赖,缺则优雅降级)。
16
+ * 顶层动态 import——npm 形态若未装 puppeteer-core,openSession 返回 TransportFailure
17
+ * 而非在模块加载期炸掉整个插件(2026-08-28 发布面实测:静态 import 会拖垮 index.ts 加载链)。
18
+ */
4
19
  async function loadPuppeteer() {
5
20
  try {
6
21
  return await import('puppeteer-core');
7
- } catch {
8
- return {
9
- missing: true
10
- };
22
+ }
23
+ catch {
24
+ return { missing: true };
11
25
  }
12
26
  }
13
27
  function devtoolsWsEndpoint() {
@@ -15,102 +29,81 @@ function devtoolsWsEndpoint() {
15
29
  const udd = process.env.CHROME_USER_DATA_DIR ?? `${homedir()}/Library/Application Support/Google/Chrome`;
16
30
  const [port, wsPath] = readFileSync(`${udd}/DevToolsActivePort`, 'utf8').trim().split('\n');
17
31
  if (!port || !wsPath?.startsWith('/devtools/browser/')) {
18
- return {
19
- err: `DevToolsActivePort 内容异常: ${String(port)}/${String(wsPath)}`
20
- };
32
+ return { err: `DevToolsActivePort 内容异常: ${String(port)}/${String(wsPath)}` };
21
33
  }
22
- return {
23
- ws: `ws://127.0.0.1:${port.trim()}${wsPath.trim()}`
24
- };
25
- } catch {
26
- return {
27
- err: '日常 Chrome 未开调试端口。开启方法:Chrome 打开 chrome://inspect/#remote-debugging → 打开开关(Chrome 144+)'
28
- };
34
+ return { ws: `ws://127.0.0.1:${port.trim()}${wsPath.trim()}` };
35
+ }
36
+ catch {
37
+ return { err: '日常 Chrome 未开调试端口。开启方法:Chrome 打开 chrome://inspect/#remote-debugging → 打开开关(Chrome 144+)' };
29
38
  }
30
39
  }
31
40
  export async function openSession(opts = {}) {
32
41
  const pp = await loadPuppeteer();
33
42
  if ('missing' in pp) {
34
- return {
35
- ok: false,
36
- summary: 'puppeteer-core 未安装(可选依赖):会话检索不可用,官方通道 gotry_flyai_search 仍可用。安装:npm i -D puppeteer-core'
37
- };
43
+ return { ok: false, summary: 'puppeteer-core 未安装(可选依赖):会话检索不可用,官方通道 gotry_flyai_search 仍可用。安装:npm i -D puppeteer-core' };
38
44
  }
39
45
  const puppeteer = pp.default;
40
- const useCdp = opts.mode === 'cdp' || opts.mode !== 'persistent' && !opts.profileDir;
46
+ const useCdp = opts.mode === 'cdp' || (opts.mode !== 'persistent' && !opts.profileDir);
41
47
  let browser;
42
48
  let isCdp = false;
43
49
  if (useCdp) {
44
50
  const d = devtoolsWsEndpoint();
45
- if ('err' in d) return {
46
- ok: false,
47
- summary: d.err
48
- };
51
+ if ('err' in d)
52
+ return { ok: false, summary: d.err };
49
53
  try {
50
- browser = await puppeteer.connect({
51
- browserWSEndpoint: d.ws,
52
- defaultViewport: null
53
- });
54
+ browser = await puppeteer.connect({ browserWSEndpoint: d.ws, defaultViewport: null });
54
55
  isCdp = true;
55
- } catch (e) {
56
+ }
57
+ catch (e) {
56
58
  const msg = e instanceof Error ? e.message : e?.message ?? JSON.stringify(e).slice(0, 200);
57
- return {
58
- ok: false,
59
- summary: `cdp attach 失败:${String(msg).split('\n')[0]}`
60
- };
59
+ return { ok: false, summary: `cdp attach 失败:${String(msg).split('\n')[0]}` };
61
60
  }
62
- } else {
61
+ }
62
+ else {
63
63
  const { tmpdir } = await import('node:os');
64
64
  const profileDir = opts.profileDir ?? `${tmpdir()}/gotry-session-profile`;
65
65
  try {
66
- browser = await puppeteer.launch({
67
- channel: 'chrome',
68
- headless: opts.headless ?? true,
69
- userDataDir: profileDir
70
- });
71
- } catch (e) {
72
- return {
73
- ok: false,
74
- summary: `chrome launch failed: ${e instanceof Error ? e.message.split('\n')[0] : String(e)}`
75
- };
66
+ browser = await puppeteer.launch({ channel: 'chrome', headless: opts.headless ?? true, userDataDir: profileDir });
67
+ }
68
+ catch (e) {
69
+ return { ok: false, summary: `chrome launch failed: ${e instanceof Error ? e.message.split('\n')[0] : String(e)}` };
76
70
  }
77
71
  }
72
+ // fail-closed:guard 装不上即断开返回失败——不给无守卫的检索会话形态
73
+ // (唯一豁免:登录 bootstrap 显式 guard:false,见 TransportOptions 注释)
78
74
  let guard;
79
75
  if (opts.guard !== false) {
80
76
  try {
81
77
  guard = await attachReadGuardPuppeteer(browser, opts.auditPath);
82
- } catch (e) {
83
- await (isCdp ? browser.disconnect() : browser.close()).catch(()=>{});
84
- return {
85
- ok: false,
86
- summary: `read-guard attach failed: ${e instanceof Error ? e.message.split('\n')[0] : String(e)}`
87
- };
78
+ }
79
+ catch (e) {
80
+ await (isCdp ? browser.disconnect() : browser.close()).catch(() => { });
81
+ return { ok: false, summary: `read-guard attach failed: ${e instanceof Error ? e.message.split('\n')[0] : String(e)}` };
88
82
  }
89
83
  }
84
+ // 标签页纪律:ownPage=自己开的新页(newPage:true)——用它,收尾按 closeOwnPage 关;
85
+ // 否则沿用既有首页(仅 persistent 兼容形态;cdp 检索/登录一律 newPage)
90
86
  let ownPage = false;
91
87
  let page;
92
88
  if (opts.newPage) {
93
89
  page = await browser.newPage();
94
90
  ownPage = true;
95
- } else {
91
+ }
92
+ else {
96
93
  page = (await browser.pages())[0] ?? await browser.newPage();
97
94
  }
98
95
  return {
99
96
  ok: true,
100
97
  browser,
101
98
  page,
102
- guard: guard ?? {
103
- blockedCount: ()=>0,
104
- requestCount: ()=>0
105
- },
106
- close: async ()=>{
99
+ guard: guard ?? { blockedCount: () => 0, requestCount: () => 0 },
100
+ close: async () => {
101
+ // 自己开的标签页按 closeOwnPage 收尾(登录引导保持 false——登录页留给用户);
102
+ // cdp:只断开连接,绝不关用户浏览器;persistent:关自己拉起的实例
107
103
  if (ownPage && (opts.closeOwnPage ?? true)) {
108
- await page.close().catch(()=>{});
104
+ await page.close().catch(() => { });
109
105
  }
110
- await (isCdp ? browser.disconnect() : browser.close()).catch(()=>{});
111
- }
106
+ await (isCdp ? browser.disconnect() : browser.close()).catch(() => { });
107
+ },
112
108
  };
113
109
  }
114
-
115
-
116
- //# sourceURL=ts/capabilities/session/transport.ts