@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.
- package/README.md +102 -194
- package/README.zh-CN.md +115 -210
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-bootstrap.js +466 -35
- package/bin/gotry-inner.js +255 -36
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +2 -2
- package/client/client.js +174 -0
- package/cordis.gotry-patch.yml +70 -93
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +63 -148
- package/dist/capabilities/anything.js +89 -75
- package/dist/capabilities/artifacts.js +123 -104
- package/dist/capabilities/channel-health.js +65 -45
- package/dist/capabilities/channel-registry.js +108 -87
- package/dist/capabilities/doctor.js +357 -235
- package/dist/capabilities/effect.js +223 -309
- package/dist/capabilities/fact-log.js +28 -14
- package/dist/capabilities/flyai.js +245 -156
- package/dist/capabilities/ground-transfer.js +509 -0
- package/dist/capabilities/hbcli.js +144 -148
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/model-override.js +30 -10
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/resilience.js +50 -32
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
- package/dist/capabilities/session/adapters/dida-portal.js +206 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +169 -112
- package/dist/capabilities/session/benchmark.js +85 -149
- package/dist/capabilities/session/extension-bridge.js +320 -259
- package/dist/capabilities/session/extension-channel.js +57 -76
- package/dist/capabilities/session/extension-distribution.js +115 -119
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +55 -33
- package/dist/capabilities/session/health-watch.js +146 -154
- package/dist/capabilities/session/read-guard.js +55 -47
- package/dist/capabilities/session/static-flight-golden.js +49 -36
- package/dist/capabilities/session/transport.js +54 -61
- package/dist/capabilities/session/wizard.js +63 -93
- package/dist/capabilities/session-consent.js +143 -47
- package/dist/capabilities/session-login.js +95 -126
- package/dist/capabilities/session-search.js +391 -253
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +87 -73
- package/dist/capabilities/weather.js +163 -235
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
- package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
- package/dist/scripts/anything-tests.js +58 -62
- package/dist/scripts/artifact-client-contract-tests.js +69 -0
- package/dist/scripts/artifacts-capability-tests.js +235 -0
- package/dist/scripts/async-collect.js +26 -15
- package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
- package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
- package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
- package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
- package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
- package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +61 -123
- package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
- package/dist/scripts/bootstrap-tests.js +1383 -128
- package/dist/scripts/build-changelog.js +89 -130
- package/dist/scripts/build-metrics-report.js +135 -143
- package/dist/scripts/changelog-tests.js +35 -82
- package/dist/scripts/channel-probe-tests.js +44 -116
- package/dist/scripts/channel-probe.js +84 -167
- package/dist/scripts/channel-registry-tests.js +97 -185
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +428 -120
- package/dist/scripts/dsh-artifact-e2e.js +150 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
- package/dist/scripts/dsh-runtime-closure.js +75 -63
- package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +165 -552
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +69 -242
- package/dist/scripts/evaluation-contract-tests.js +193 -499
- package/dist/scripts/extension-distribution-cli.js +19 -15
- package/dist/scripts/extension-distribution-tests.js +98 -211
- package/dist/scripts/extension-tests.js +366 -389
- package/dist/scripts/fact-gate-tests.js +725 -263
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-tests.js +325 -68
- package/dist/scripts/g5-guard-tests.js +131 -0
- package/dist/scripts/g5-guard.js +189 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +71 -90
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +41 -115
- package/dist/scripts/health-watch-cli.js +42 -28
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +16 -9
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +547 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +325 -340
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +474 -222
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +62 -199
- package/dist/scripts/model-override-e2e.js +66 -105
- package/dist/scripts/nightly-evidence-tests.js +64 -89
- package/dist/scripts/nightly-evidence.js +108 -111
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/onboarding-tests.js +63 -88
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/persona-surface-guard-tests.js +37 -12
- package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +126 -227
- package/dist/scripts/price-drift-watch.js +209 -253
- package/dist/scripts/probe-poi-tests.js +52 -45
- package/dist/scripts/product-metrics.js +187 -273
- package/dist/scripts/publish-preverify.js +47 -41
- package/dist/scripts/realtime-pricing-tests.js +37 -76
- package/dist/scripts/replay-async.js +20 -18
- package/dist/scripts/replay-real.js +16 -18
- package/dist/scripts/replay.js +52 -82
- package/dist/scripts/session-attach-diagnose.js +16 -12
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +66 -147
- package/dist/scripts/session-dida-live-e2e.js +151 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-login.js +14 -13
- package/dist/scripts/session-tests.js +545 -583
- package/dist/scripts/sf-live-benchmark.js +92 -110
- package/dist/scripts/sf-live-cli-tests.js +2 -9
- package/dist/scripts/sf-soft-score-tests.js +19 -60
- package/dist/scripts/sf-summary-tests.js +149 -0
- package/dist/scripts/sf-summary.js +466 -84
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +11 -10
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +12 -20
- package/dist/scripts/smoke.js +541 -422
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +549 -212
- package/dist/scripts/static-golden-tests.js +46 -180
- package/dist/scripts/time-eval-tests.js +210 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +46 -93
- package/dist/scripts/turn-handoff-collect.js +71 -99
- package/dist/scripts/turn-policy-tests.js +28 -29
- package/dist/scripts/typed-contract-canary.js +77 -162
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +23 -19
- package/dist/scripts/visa-policy-tests.js +31 -44
- package/dist/scripts/weather-tests.js +121 -543
- package/dist/scripts/wish-channel-gate-tests.js +29 -86
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +18 -12
- package/dist/src/artifact-gate.js +652 -221
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +55 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +194 -0
- package/dist/src/benchmark-agent-conformance.js +296 -262
- package/dist/src/benchmark-environment-bridge.js +298 -413
- package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
- package/dist/src/benchmark-tool-isolation.js +94 -67
- package/dist/src/bookable-facts.js +299 -174
- package/dist/src/booking-saga.js +64 -120
- package/dist/src/booking-surface/availability-policy.js +443 -576
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/contracts.js +24 -80
- package/dist/src/booking-surface/dsh-planner.js +510 -281
- package/dist/src/booking-surface/dsh-plugin.js +17 -1
- package/dist/src/booking-surface/error-codes.js +82 -10
- package/dist/src/booking-surface/index.js +0 -3
- package/dist/src/booking-surface/profile.js +10 -54
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +765 -891
- package/dist/src/booking-surface/server.js +200 -251
- package/dist/src/booking-surface/startup.js +58 -54
- package/dist/src/booking-surface/validation.js +150 -218
- package/dist/src/bridge.js +20 -13
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +82 -120
- package/dist/src/engine.js +253 -239
- package/dist/src/evaluation-cadence.js +72 -209
- package/dist/src/evaluation-contracts.js +220 -843
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/gotry-backend.js +70 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +18 -7
- package/dist/src/index.js +1425 -1629
- package/dist/src/journey.js +132 -81
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +257 -166
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/realtime-pricing.js +63 -72
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +310 -471
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +121 -127
- package/dist/src/turn-policy.js +61 -89
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +504 -381
- package/dist/src/wish-pool.js +39 -14
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +273 -7
- package/extension/README.md +4 -3
- package/extension/background.js +125 -14
- package/extension/content-bridge.js +23 -1
- package/extension/content-main.js +15 -4
- package/extension/manifest.json +16 -8
- package/package.json +251 -220
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +1 -1
- package/ts/capabilities/artifacts.ts +46 -9
- package/ts/capabilities/flyai.ts +156 -37
- package/ts/capabilities/hbcli.ts +1 -1
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
- package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
- package/ts/capabilities/session/extension-bridge.ts +226 -101
- package/ts/capabilities/session/extension-channel.ts +22 -9
- package/ts/capabilities/session/wizard.ts +2 -2
- package/ts/capabilities/session-consent.ts +98 -14
- package/ts/capabilities/session-login.ts +1 -0
- package/ts/capabilities/session-search.ts +315 -31
- package/ts/dsh-runtime/vendor/README.md +8 -4
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
- package/ts/package.json +236 -16
- package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
- package/ts/scripts/state-cli.ts +418 -51
- package/ts/src/artifact-gate.ts +561 -15
- package/ts/src/benchmark-environment-bridge.ts +37 -20
- package/ts/src/bookable-facts.ts +183 -7
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/dsh-planner.ts +240 -32
- package/ts/src/booking-surface/dsh-plugin.js +17 -1
- package/ts/src/booking-surface/error-codes.ts +75 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/server.ts +84 -51
- package/ts/src/booking-surface/validation.ts +2 -0
- package/ts/src/contracts.ts +12 -2
- package/ts/src/dsh-llm.ts +4 -8
- package/ts/src/index.ts +609 -90
- package/ts/src/loop.ts +116 -7
- package/ts/src/memory-capture.ts +227 -3
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/state-ledger.ts +47 -29
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +217 -13
- 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
|
-
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
ok:
|
|
15
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
kind: 'bridge-unavailable',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
kind: 'bridge-unavailable',
|
|
70
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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)
|
|
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
|
-
}
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
if (
|
|
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))
|
|
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
|
-
}
|
|
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))
|
|
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
|
-
}
|
|
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
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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)
|
|
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
|
-
}
|
|
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)
|
|
192
|
+
if (!tRes.ok)
|
|
193
|
+
return fail(tRes.error);
|
|
177
194
|
const actual = sha256Hex(tRes.bytes);
|
|
178
|
-
if (actual !== manifest.tarballSha256)
|
|
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
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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)
|
|
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)
|
|
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)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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)
|
|
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
|
-
|
|
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)
|
|
30
|
-
|
|
31
|
-
if (x
|
|
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)
|
|
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
|