@danceiny/gotry 0.0.1-rc.16 → 0.0.1-rc.18
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 +161 -116
- package/README.zh-CN.md +173 -144
- package/bin/gotry-booking-copilot.js +53 -0
- package/bin/gotry-bootstrap.js +113 -66
- package/bin/gotry-inner.js +436 -70
- package/bin/gotry-runtime-resolution.d.ts +27 -0
- package/bin/gotry-runtime-resolution.js +50 -0
- package/bin/gotry.js +1 -1
- package/cordis.gotry-patch.yml +5 -1
- package/dist/capabilities/agent-reach-deep.js +1 -1
- package/dist/capabilities/agent-reach.js +1 -1
- package/dist/capabilities/anything.js +1 -1
- package/dist/capabilities/artifacts.js +1 -1
- package/dist/capabilities/effect.js +1 -1
- package/dist/capabilities/fact-log.js +1 -1
- package/dist/capabilities/flyai.js +20 -6
- package/dist/capabilities/hbcli.js +1 -1
- package/dist/capabilities/incident-log.js +1 -1
- package/dist/capabilities/model-override.js +18 -0
- package/dist/capabilities/opensky.js +1 -1
- package/dist/capabilities/resilience.js +1 -1
- package/dist/capabilities/session/action-cache.js +1 -1
- package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
- package/dist/capabilities/session/adapters/meituan-local.js +1 -1
- package/dist/capabilities/session/benchmark.js +1 -1
- package/dist/capabilities/session/extension-bridge.js +19 -6
- package/dist/capabilities/session/extension-channel.js +3 -3
- package/dist/capabilities/session/extension-distribution.js +234 -0
- package/dist/capabilities/session/extract.js +1 -1
- package/dist/capabilities/session/golden-score.js +92 -0
- package/dist/capabilities/session/health-watch.js +1 -1
- package/dist/capabilities/session/read-guard.js +1 -1
- package/dist/capabilities/session/static-flight-golden.js +137 -0
- package/dist/capabilities/session/transport.js +1 -1
- package/dist/capabilities/session/wizard.js +21 -251
- package/dist/capabilities/session-consent.js +1 -1
- package/dist/capabilities/session-login.js +11 -5
- package/dist/capabilities/session-search.js +46 -4
- package/dist/capabilities/weather.js +168 -46
- package/dist/data/session-golden-20.json +25 -0
- package/dist/data/sf-golden-manifest.json +102 -0
- package/dist/data/sf-static-routes.json +91 -0
- package/dist/scripts/action-cache-tests.js +1 -1
- package/dist/scripts/agent-planning-budget-e2e.js +227 -0
- package/dist/scripts/agent-planning-budget-tests.js +173 -0
- package/dist/scripts/agent-reach-deep-tests.js +1 -1
- package/dist/scripts/agent-reach-tests.js +1 -1
- package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
- package/dist/scripts/anything-tests.js +1 -1
- package/dist/scripts/async-collect.js +1 -1
- package/dist/scripts/benchmark-environment-bridge-e2e.js +981 -0
- package/dist/scripts/benchmark-environment-bridge-tests.js +2544 -0
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +335 -0
- package/dist/scripts/booking-copilot-availability-policy-v2-tests.js +1355 -0
- package/dist/scripts/booking-copilot-bin-proof-tests.js +89 -0
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +113 -0
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +356 -0
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +557 -0
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +140 -0
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +202 -0
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +102 -0
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +374 -0
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +447 -0
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +205 -0
- package/dist/scripts/booking-copilot-server-proof-tests.js +317 -0
- package/dist/scripts/booking-copilot-startup-proof-tests.js +283 -0
- package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +3935 -0
- package/dist/scripts/booking-saga-tests.js +1 -1
- package/dist/scripts/booking-surface-contract-proof-tests.js +393 -0
- package/dist/scripts/booking-surface-v2-contract-proof-tests.js +1174 -0
- package/dist/scripts/bootstrap-tests.js +45 -39
- package/dist/scripts/build-changelog.js +1 -1
- package/dist/scripts/changelog-tests.js +1 -1
- package/dist/scripts/companion-tests.js +1 -1
- package/dist/scripts/diff-test.js +1 -1
- package/dist/scripts/dsh-runtime-closure-tests.js +225 -0
- package/dist/scripts/dsh-runtime-closure.js +150 -0
- package/dist/scripts/effect-tests.js +1 -1
- package/dist/scripts/engine-run.js +1 -1
- package/dist/scripts/engine-tests.js +1 -1
- package/dist/scripts/evaluation-cadence-tests.js +347 -0
- package/dist/scripts/evaluation-contract-tests.js +574 -0
- package/dist/scripts/extension-distribution-cli.js +35 -0
- package/dist/scripts/extension-distribution-tests.js +341 -0
- package/dist/scripts/extension-tests.js +128 -23
- package/dist/scripts/fact-gate-tests.js +1 -1
- package/dist/scripts/flyai-tests.js +1 -1
- package/dist/scripts/hbcli-e2e-tests.js +1 -1
- package/dist/scripts/hbcli-tests.js +1 -1
- package/dist/scripts/health-watch-cli.js +1 -1
- package/dist/scripts/i18n-tests.js +1 -1
- package/dist/scripts/incident-tests.js +1 -1
- package/dist/scripts/journey-tests.js +1 -1
- package/dist/scripts/ledger-tests.js +1 -1
- package/dist/scripts/ledger-workflow-crash.js +1 -1
- package/dist/scripts/memory-capture-tests.js +1 -1
- package/dist/scripts/memory-decay-tests.js +1 -1
- package/dist/scripts/memory-metrics.js +1 -1
- package/dist/scripts/memory-value-report.js +1 -1
- package/dist/scripts/model-override-e2e.js +176 -0
- package/dist/scripts/nightly-evidence-tests.js +1 -1
- package/dist/scripts/nightly-evidence.js +1 -1
- package/dist/scripts/nudge-digest.js +1 -1
- package/dist/scripts/onboarding-tests.js +21 -53
- package/dist/scripts/opensky-check.js +1 -1
- package/dist/scripts/opensky-tests.js +1 -1
- package/dist/scripts/pnpm-dsh-closure-proof.js +20 -0
- package/dist/scripts/price-drift-tests.js +1 -1
- package/dist/scripts/price-drift-watch.js +1 -1
- package/dist/scripts/probe-poi-tests.js +1 -1
- package/dist/scripts/product-metrics.js +1 -1
- package/dist/scripts/publish-preverify.js +40 -4
- package/dist/scripts/realtime-pricing-tests.js +1 -1
- package/dist/scripts/replay-async.js +1 -1
- package/dist/scripts/replay-real.js +1 -1
- package/dist/scripts/replay.js +1 -1
- package/dist/scripts/session-attach-diagnose.js +1 -1
- package/dist/scripts/session-attach-poc.js +1 -1
- package/dist/scripts/session-benchmark.js +1 -1
- package/dist/scripts/session-extract-tests.js +1 -1
- package/dist/scripts/session-login.js +1 -1
- package/dist/scripts/session-tests.js +70 -20
- package/dist/scripts/sf-live-benchmark.js +338 -0
- package/dist/scripts/sf-live-cli-tests.js +21 -0
- package/dist/scripts/sf-soft-score-tests.js +108 -0
- package/dist/scripts/sf-summary.js +93 -0
- package/dist/scripts/skeleton-check.js +1 -1
- package/dist/scripts/skeleton-integration-test.js +1 -1
- package/dist/scripts/skills-contract-tests.js +1 -1
- package/dist/scripts/smoke-session-gate-tests.js +29 -0
- package/dist/scripts/smoke.js +79 -33
- package/dist/scripts/state-cli-tests.js +1 -1
- package/dist/scripts/state-cli.js +1 -1
- package/dist/scripts/static-golden-tests.js +299 -0
- package/dist/scripts/time-eval-tests.js +1 -1
- package/dist/scripts/travel-timeline-tests.js +1 -1
- package/dist/scripts/unified-tests.js +1 -1
- package/dist/scripts/weather-tests.js +694 -44
- package/dist/scripts/z3-race-tests.js +1 -1
- package/dist/src/artifact-gate.js +1 -1
- package/dist/src/benchmark-agent-conformance.js +370 -0
- package/dist/src/benchmark-environment-bridge.js +384 -0
- package/dist/src/benchmark-headless-child-diagnostics.js +173 -0
- package/dist/src/benchmark-tool-isolation.js +124 -0
- package/dist/src/bookable-facts.js +1 -1
- package/dist/src/booking-saga.js +1 -1
- package/dist/src/booking-surface/availability-policy-v2.js +830 -0
- package/dist/src/booking-surface/canonical-schema.js +113 -0
- package/dist/src/booking-surface/contracts-v2.js +89 -0
- package/dist/src/booking-surface/contracts.js +46 -0
- package/dist/src/booking-surface/dsh-planner.js +453 -0
- package/dist/src/booking-surface/dsh-plugin.js +93 -0
- package/dist/src/booking-surface/error-codes.js +94 -0
- package/dist/src/booking-surface/index.js +15 -0
- package/dist/src/booking-surface/profile.js +68 -0
- package/dist/src/booking-surface/runtime-v2.js +1771 -0
- package/dist/src/booking-surface/runtime.js +351 -0
- package/dist/src/booking-surface/server-v2.js +334 -0
- package/dist/src/booking-surface/server.js +302 -0
- package/dist/src/booking-surface/startup.js +159 -0
- package/dist/src/booking-surface/validation-v2.js +319 -0
- package/dist/src/booking-surface/validation.js +809 -0
- package/dist/src/bridge.js +1 -1
- package/dist/src/companions.js +1 -1
- package/dist/src/contracts.js +1 -1
- package/dist/src/dsh-llm.js +1 -1
- package/dist/src/engine.js +1 -1
- package/dist/src/evaluation-cadence.js +234 -0
- package/dist/src/evaluation-contracts.js +906 -0
- package/dist/src/i18n.js +1 -1
- package/dist/src/index.js +50 -19
- package/dist/src/journey.js +1 -1
- package/dist/src/loop.js +1 -1
- package/dist/src/memory-capture.js +1 -1
- package/dist/src/memory-decay.js +1 -1
- package/dist/src/memory-utility.js +1 -1
- package/dist/src/mock-llm.js +1 -1
- package/dist/src/model.js +1 -1
- package/dist/src/realtime-pricing.js +1 -1
- package/dist/src/slot-spec.js +1 -1
- package/dist/src/state-ledger.js +2 -1
- package/dist/src/time-anchor.js +1 -1
- package/dist/src/tool-budget.js +136 -0
- package/dist/src/tool-packet.js +1 -1
- package/dist/src/travel-slots.js +1 -1
- package/dist/src/travel-timeline.js +1 -1
- package/dist/src/unified.js +1 -1
- package/dist/src/wish-pool.js +1 -1
- package/dist/src/z3-shared.js +1 -1
- package/extension/README.md +31 -7
- package/package.json +286 -11
- package/schemas/booking.surface.v1.schema.json +927 -0
- package/schemas/booking.surface.v2.schema.json +61 -0
- package/ts/capabilities/flyai.ts +16 -3
- package/ts/capabilities/session/extension-bridge.ts +37 -14
- package/ts/capabilities/session/extension-channel.ts +6 -3
- package/ts/capabilities/session/extension-distribution.ts +264 -0
- package/ts/capabilities/session/golden-score.ts +139 -0
- package/ts/capabilities/session/health-watch.ts +1 -1
- package/ts/capabilities/session/static-flight-golden.ts +209 -0
- package/ts/capabilities/session/wizard.ts +34 -176
- package/ts/capabilities/session-login.ts +15 -5
- package/ts/capabilities/session-search.ts +40 -3
- package/ts/capabilities/weather.ts +141 -52
- package/ts/package.json +3 -3
- package/ts/src/benchmark-agent-conformance.ts +448 -0
- package/ts/src/benchmark-environment-bridge.ts +348 -0
- package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
- package/ts/src/benchmark-tool-isolation.ts +166 -0
- package/ts/src/booking-surface/availability-policy-v2.ts +523 -0
- package/ts/src/booking-surface/canonical-schema.js +113 -0
- package/ts/src/booking-surface/contracts-v2.ts +118 -0
- package/ts/src/booking-surface/contracts.ts +380 -0
- package/ts/src/booking-surface/dsh-planner.ts +452 -0
- package/ts/src/booking-surface/dsh-plugin.js +93 -0
- package/ts/src/booking-surface/error-codes.ts +101 -0
- package/ts/src/booking-surface/index.ts +12 -0
- package/ts/src/booking-surface/profile.ts +42 -0
- package/ts/src/booking-surface/runtime-v2.ts +1466 -0
- package/ts/src/booking-surface/runtime.ts +483 -0
- package/ts/src/booking-surface/server-v2.ts +247 -0
- package/ts/src/booking-surface/server.ts +324 -0
- package/ts/src/booking-surface/startup.ts +196 -0
- package/ts/src/booking-surface/validation-v2.ts +205 -0
- package/ts/src/booking-surface/validation.ts +453 -0
- package/ts/src/index.ts +64 -11
- package/ts/src/state-ledger.ts +1 -0
- package/ts/src/tool-budget.ts +165 -0
- package/dist/scripts/wizard-bootstrap.js +0 -32
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
3
|
+
import { createServer } from 'node:http';
|
|
4
|
+
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { join, dirname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { DEFAULT_RELEASE_BASE, DIST_ASSET_MANIFEST, DIST_ASSET_STORE_ZIP, DIST_ASSET_TARBALL, compareVersions, distAssetUrls, installExtensionFromGithub, parseDistManifest, readExtensionKey, sha256Hex } from '../capabilities/session/extension-distribution.js';
|
|
9
|
+
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
10
|
+
const EXT_SRC = join(repoRoot, 'extension');
|
|
11
|
+
const EXT_FILES = [
|
|
12
|
+
'manifest.json',
|
|
13
|
+
'background.js',
|
|
14
|
+
'content-main.js',
|
|
15
|
+
'content-bridge.js',
|
|
16
|
+
'README.md'
|
|
17
|
+
];
|
|
18
|
+
let passed = 0;
|
|
19
|
+
function ok(cond, label) {
|
|
20
|
+
if (!cond) {
|
|
21
|
+
console.error(` ✗ ${label}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
passed += 1;
|
|
25
|
+
console.log(` ok - ${label}`);
|
|
26
|
+
}
|
|
27
|
+
{
|
|
28
|
+
const urls = distAssetUrls();
|
|
29
|
+
ok(urls.tarball === `${DEFAULT_RELEASE_BASE}/latest/download/${DIST_ASSET_TARBALL}`, `tarball URL 形态(${urls.tarball})`);
|
|
30
|
+
ok(urls.manifest.endsWith(`/latest/download/${DIST_ASSET_MANIFEST}`), 'manifest URL 形态(latest/download 永久链)');
|
|
31
|
+
ok(distAssetUrls('https://x.example/releases/').tarball === 'https://x.example/releases/latest/download/gotry-session-bridge.tar.gz', 'releaseBase 尾斜杠归一');
|
|
32
|
+
const pkgSrc = readFileSync(join(repoRoot, 'scripts', 'package-extension.mjs'), 'utf8');
|
|
33
|
+
ok(pkgSrc.includes(`const ASSET_TARBALL = '${DIST_ASSET_TARBALL}'`), 'package-extension.mjs tarball 资产名一致');
|
|
34
|
+
ok(pkgSrc.includes(`const ASSET_STORE_ZIP = '${DIST_ASSET_STORE_ZIP}'`), 'package-extension.mjs store zip 资产名一致');
|
|
35
|
+
ok(pkgSrc.includes(`const ASSET_MANIFEST = '${DIST_ASSET_MANIFEST}'`), 'package-extension.mjs dist-manifest 资产名一致');
|
|
36
|
+
ok(pkgSrc.includes("const STAGE = join(OUT, 'gotry-session-bridge')"), '打包根目录名固定 gotry-session-bridge/(下载端 strip-components 依赖)');
|
|
37
|
+
ok(pkgSrc.includes('delete m.key'), 'store zip 变体剥离 manifest key(2026-08-30 商店首传实测拒绝 key;tar.gz 保留 key 保通道 ID 不变量)');
|
|
38
|
+
}{
|
|
39
|
+
const good = JSON.stringify({
|
|
40
|
+
version: '0.1.0',
|
|
41
|
+
tarball: DIST_ASSET_TARBALL,
|
|
42
|
+
tarballSha256: 'a'.repeat(64),
|
|
43
|
+
zip: DIST_ASSET_STORE_ZIP,
|
|
44
|
+
zipSha256: 'b'.repeat(64)
|
|
45
|
+
});
|
|
46
|
+
ok(parseDistManifest(good).version === '0.1.0', '合法 dist-manifest 解析');
|
|
47
|
+
const rejects = [
|
|
48
|
+
[
|
|
49
|
+
'{bad json',
|
|
50
|
+
'坏 JSON'
|
|
51
|
+
],
|
|
52
|
+
[
|
|
53
|
+
JSON.stringify({
|
|
54
|
+
version: 'x',
|
|
55
|
+
tarball: DIST_ASSET_TARBALL,
|
|
56
|
+
tarballSha256: 'a'.repeat(64),
|
|
57
|
+
zip: DIST_ASSET_STORE_ZIP,
|
|
58
|
+
zipSha256: 'b'.repeat(64)
|
|
59
|
+
}),
|
|
60
|
+
'version 非 x.y.z'
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
JSON.stringify({
|
|
64
|
+
tarball: DIST_ASSET_TARBALL,
|
|
65
|
+
tarballSha256: 'a'.repeat(64),
|
|
66
|
+
zip: DIST_ASSET_STORE_ZIP,
|
|
67
|
+
zipSha256: 'b'.repeat(64)
|
|
68
|
+
}),
|
|
69
|
+
'缺 version'
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
JSON.stringify({
|
|
73
|
+
version: '0.1.0',
|
|
74
|
+
tarball: 'other-name.tar.gz',
|
|
75
|
+
tarballSha256: 'a'.repeat(64),
|
|
76
|
+
zip: DIST_ASSET_STORE_ZIP,
|
|
77
|
+
zipSha256: 'b'.repeat(64)
|
|
78
|
+
}),
|
|
79
|
+
'tarball 资产名漂移'
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
JSON.stringify({
|
|
83
|
+
version: '0.1.0',
|
|
84
|
+
tarball: DIST_ASSET_TARBALL,
|
|
85
|
+
tarballSha256: 'zz',
|
|
86
|
+
zip: DIST_ASSET_STORE_ZIP,
|
|
87
|
+
zipSha256: 'b'.repeat(64)
|
|
88
|
+
}),
|
|
89
|
+
'tarballSha256 非 64-hex'
|
|
90
|
+
]
|
|
91
|
+
];
|
|
92
|
+
for (const [raw, label] of rejects){
|
|
93
|
+
let threw = false;
|
|
94
|
+
try {
|
|
95
|
+
parseDistManifest(raw);
|
|
96
|
+
} catch {
|
|
97
|
+
threw = true;
|
|
98
|
+
}
|
|
99
|
+
ok(threw, `fail-closed:${label}`);
|
|
100
|
+
}
|
|
101
|
+
}{
|
|
102
|
+
ok(compareVersions('0.1.0', '0.1.1') < 0, '0.1.0 < 0.1.1');
|
|
103
|
+
ok(compareVersions('0.2.0', '0.1.9') > 0, '0.2.0 > 0.1.9');
|
|
104
|
+
ok(compareVersions('1.0.0', '0.9.9') > 0, '1.0.0 > 0.9.9');
|
|
105
|
+
ok(compareVersions('0.1.0', '0.1.0') === 0, '相等为 0');
|
|
106
|
+
ok(compareVersions('0.1', '0.1.0') === 0, '缺段按 0 补齐');
|
|
107
|
+
}async function startLoopback(manifestJson, tarball) {
|
|
108
|
+
let hits = 0;
|
|
109
|
+
const server = createServer((req, res)=>{
|
|
110
|
+
if (req.url?.endsWith(DIST_ASSET_MANIFEST)) {
|
|
111
|
+
res.writeHead(200, {
|
|
112
|
+
'content-type': 'application/json'
|
|
113
|
+
});
|
|
114
|
+
res.end(manifestJson);
|
|
115
|
+
} else if (req.url?.endsWith(DIST_ASSET_TARBALL)) {
|
|
116
|
+
hits += 1;
|
|
117
|
+
res.writeHead(200, {
|
|
118
|
+
'content-type': 'application/gzip'
|
|
119
|
+
});
|
|
120
|
+
res.end(tarball);
|
|
121
|
+
} else {
|
|
122
|
+
res.writeHead(404);
|
|
123
|
+
res.end('not found');
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
await new Promise((r)=>server.listen(0, '127.0.0.1', r));
|
|
127
|
+
const addr = server.address();
|
|
128
|
+
const base = `http://127.0.0.1:${typeof addr === 'object' && addr ? addr.port : 0}/releases`;
|
|
129
|
+
return {
|
|
130
|
+
base,
|
|
131
|
+
manifestJson,
|
|
132
|
+
tarball,
|
|
133
|
+
tarballHits: ()=>hits,
|
|
134
|
+
close: ()=>new Promise((r)=>server.close(()=>r()))
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function buildTarball(sourceDir, versionOverride) {
|
|
138
|
+
const stage = mkdtempSync(join(tmpdir(), 'extdist-stage-'));
|
|
139
|
+
const root = join(stage, 'gotry-session-bridge');
|
|
140
|
+
mkdirSync(root);
|
|
141
|
+
for (const f of EXT_FILES)copyFileSync(join(sourceDir, f), join(root, f));
|
|
142
|
+
if (versionOverride != null) {
|
|
143
|
+
const m = JSON.parse(readFileSync(join(root, 'manifest.json'), 'utf8'));
|
|
144
|
+
writeFileSync(join(root, 'manifest.json'), JSON.stringify({
|
|
145
|
+
...m,
|
|
146
|
+
version: versionOverride
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
const tarPath = join(stage, DIST_ASSET_TARBALL);
|
|
150
|
+
execFileSync('tar', [
|
|
151
|
+
'-czf',
|
|
152
|
+
tarPath,
|
|
153
|
+
'-C',
|
|
154
|
+
stage,
|
|
155
|
+
'gotry-session-bridge'
|
|
156
|
+
]);
|
|
157
|
+
const tarball = readFileSync(tarPath);
|
|
158
|
+
rmSync(stage, {
|
|
159
|
+
recursive: true,
|
|
160
|
+
force: true
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
tarball,
|
|
164
|
+
sha: sha256Hex(tarball)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function distManifestJson(version, sha) {
|
|
168
|
+
return JSON.stringify({
|
|
169
|
+
version,
|
|
170
|
+
tarball: DIST_ASSET_TARBALL,
|
|
171
|
+
tarballSha256: sha,
|
|
172
|
+
zip: DIST_ASSET_STORE_ZIP,
|
|
173
|
+
zipSha256: 'c'.repeat(64),
|
|
174
|
+
builtFromCommit: 'test'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
{
|
|
178
|
+
const work = mkdtempSync(join(tmpdir(), 'extdist-e2e-'));
|
|
179
|
+
const { tarball, sha } = buildTarball(EXT_SRC, '9.9.9');
|
|
180
|
+
const srcVersion = JSON.parse(readFileSync(join(EXT_SRC, 'manifest.json'), 'utf8'));
|
|
181
|
+
const remoteVersion = '9.9.9';
|
|
182
|
+
{
|
|
183
|
+
const dest = join(work, 'dest1');
|
|
184
|
+
const lb = await startLoopback(distManifestJson(remoteVersion, sha), tarball);
|
|
185
|
+
const r = await installExtensionFromGithub({
|
|
186
|
+
destDir: dest,
|
|
187
|
+
pinnedSourceDir: EXT_SRC,
|
|
188
|
+
releaseBase: lb.base
|
|
189
|
+
});
|
|
190
|
+
lb.close();
|
|
191
|
+
ok(r.ok && r.action === 'installed' && r.version === remoteVersion, `installed 全链(action=${r.action},v=${r.version},err=${r.error ?? '无'})`);
|
|
192
|
+
ok(readExtensionKey(dest) === srcVersion.key, '落位产物 key 与 bundled 逐字一致(同扩展 ID)');
|
|
193
|
+
ok(JSON.parse(readFileSync(join(dest, 'manifest.json'), 'utf8')).version === remoteVersion, '落位产物为远端版本');
|
|
194
|
+
for (const f of EXT_FILES)ok(existsSync(join(dest, f)), `文件就位:${f}`);
|
|
195
|
+
}
|
|
196
|
+
{
|
|
197
|
+
const dest = join(work, 'dest2');
|
|
198
|
+
const lb = await startLoopback(distManifestJson(remoteVersion, '0'.repeat(64)), tarball);
|
|
199
|
+
const r = await installExtensionFromGithub({
|
|
200
|
+
destDir: dest,
|
|
201
|
+
pinnedSourceDir: EXT_SRC,
|
|
202
|
+
releaseBase: lb.base
|
|
203
|
+
});
|
|
204
|
+
lb.close();
|
|
205
|
+
ok(!r.ok && r.action === 'fallback-bundled' && /SHA256/.test(r.error ?? ''), `SHA256 不符拒绝(${r.error})`);
|
|
206
|
+
ok(!existsSync(dest), '失败路径零落盘');
|
|
207
|
+
}
|
|
208
|
+
{
|
|
209
|
+
const dest = join(work, 'dest3');
|
|
210
|
+
const lb = await startLoopback(distManifestJson(remoteVersion, sha), tarball);
|
|
211
|
+
lb.close();
|
|
212
|
+
const r = await installExtensionFromGithub({
|
|
213
|
+
destDir: dest,
|
|
214
|
+
releaseBase: lb.base
|
|
215
|
+
});
|
|
216
|
+
ok(!r.ok && r.action === 'fallback-bundled', `无网降级 fallback-bundled(${(r.error ?? '').slice(0, 40)})`);
|
|
217
|
+
}
|
|
218
|
+
{
|
|
219
|
+
const rogue = mkdtempSync(join(work, 'rogue-'));
|
|
220
|
+
const rogueManifest = {
|
|
221
|
+
...srcVersion,
|
|
222
|
+
version: remoteVersion,
|
|
223
|
+
key: `ROGUE-${srcVersion.key}`
|
|
224
|
+
};
|
|
225
|
+
writeFileSync(join(rogue, 'manifest.json'), JSON.stringify(rogueManifest));
|
|
226
|
+
for (const f of EXT_FILES.filter((x)=>x !== 'manifest.json'))copyFileSync(join(EXT_SRC, f), join(rogue, f));
|
|
227
|
+
const { tarball: rTar, sha: rSha } = buildTarball(rogue);
|
|
228
|
+
const dest = join(work, 'dest4');
|
|
229
|
+
const lb = await startLoopback(distManifestJson(remoteVersion, rSha), rTar);
|
|
230
|
+
const r = await installExtensionFromGithub({
|
|
231
|
+
destDir: dest,
|
|
232
|
+
pinnedSourceDir: EXT_SRC,
|
|
233
|
+
releaseBase: lb.base
|
|
234
|
+
});
|
|
235
|
+
lb.close();
|
|
236
|
+
ok(!r.ok && /key 漂移/.test(r.error ?? ''), `key 漂移拒绝(${r.error})`);
|
|
237
|
+
ok(!existsSync(dest), '漂移路径零落盘');
|
|
238
|
+
rmSync(rogue, {
|
|
239
|
+
recursive: true,
|
|
240
|
+
force: true
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
{
|
|
244
|
+
const dest = join(work, 'dest5');
|
|
245
|
+
mkdirSync(dest, {
|
|
246
|
+
recursive: true
|
|
247
|
+
});
|
|
248
|
+
writeFileSync(join(dest, 'manifest.json'), JSON.stringify({
|
|
249
|
+
...srcVersion,
|
|
250
|
+
version: remoteVersion
|
|
251
|
+
}));
|
|
252
|
+
const lb = await startLoopback(distManifestJson(remoteVersion, sha), tarball);
|
|
253
|
+
const r = await installExtensionFromGithub({
|
|
254
|
+
destDir: dest,
|
|
255
|
+
pinnedSourceDir: EXT_SRC,
|
|
256
|
+
releaseBase: lb.base
|
|
257
|
+
});
|
|
258
|
+
const hits = lb.tarballHits();
|
|
259
|
+
lb.close();
|
|
260
|
+
ok(r.ok && r.action === 'up-to-date' && hits === 0, `up-to-date 跳过(tarball 下载 ${hits} 次)`);
|
|
261
|
+
}
|
|
262
|
+
{
|
|
263
|
+
const dest = join(work, 'dest6');
|
|
264
|
+
const lb = await startLoopback(distManifestJson(remoteVersion, sha), tarball);
|
|
265
|
+
const r = await installExtensionFromGithub({
|
|
266
|
+
destDir: dest,
|
|
267
|
+
releaseBase: lb.base,
|
|
268
|
+
checkOnly: true
|
|
269
|
+
});
|
|
270
|
+
lb.close();
|
|
271
|
+
ok(r.ok && r.version === remoteVersion && !existsSync(dest), 'check-only 报告远端版本且零落盘');
|
|
272
|
+
}
|
|
273
|
+
rmSync(work, {
|
|
274
|
+
recursive: true,
|
|
275
|
+
force: true
|
|
276
|
+
});
|
|
277
|
+
}{
|
|
278
|
+
const { tarball, sha } = buildTarball(EXT_SRC, '9.9.9');
|
|
279
|
+
const srcVersion = JSON.parse(readFileSync(join(EXT_SRC, 'manifest.json'), 'utf8'));
|
|
280
|
+
const lb = await startLoopback(distManifestJson('9.9.9', sha), tarball);
|
|
281
|
+
const dest = mkdtempSync(join(tmpdir(), 'extdist-cli-'));
|
|
282
|
+
const cli = join(repoRoot, 'ts', 'scripts', 'extension-distribution-cli.ts');
|
|
283
|
+
const run = (args)=>new Promise((resolve)=>{
|
|
284
|
+
const child = spawn('npx', [
|
|
285
|
+
'--yes',
|
|
286
|
+
'tsx',
|
|
287
|
+
cli,
|
|
288
|
+
...args
|
|
289
|
+
], {
|
|
290
|
+
cwd: join(repoRoot, 'ts'),
|
|
291
|
+
stdio: [
|
|
292
|
+
'ignore',
|
|
293
|
+
'pipe',
|
|
294
|
+
'inherit'
|
|
295
|
+
]
|
|
296
|
+
});
|
|
297
|
+
let buf = '';
|
|
298
|
+
child.stdout.on('data', (c)=>{
|
|
299
|
+
buf += c.toString('utf8');
|
|
300
|
+
});
|
|
301
|
+
child.on('close', (code)=>{
|
|
302
|
+
const line = buf.trim().split('\n').pop() ?? '';
|
|
303
|
+
let json = null;
|
|
304
|
+
if (line.startsWith('{')) {
|
|
305
|
+
try {
|
|
306
|
+
json = JSON.parse(line);
|
|
307
|
+
} catch {}
|
|
308
|
+
}
|
|
309
|
+
resolve({
|
|
310
|
+
code: code ?? -1,
|
|
311
|
+
json
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
const r1 = await run([
|
|
316
|
+
'--dest',
|
|
317
|
+
dest,
|
|
318
|
+
'--source-dir',
|
|
319
|
+
EXT_SRC,
|
|
320
|
+
'--release-base',
|
|
321
|
+
lb.base
|
|
322
|
+
]);
|
|
323
|
+
ok(r1.code === 0 && r1.json?.ok === true && r1.json?.action === 'installed', `CLI installed 单行 JSON + exit 0(action=${String(r1.json?.action)})`);
|
|
324
|
+
const r2 = await run([
|
|
325
|
+
'--dest',
|
|
326
|
+
dest,
|
|
327
|
+
'--source-dir',
|
|
328
|
+
EXT_SRC,
|
|
329
|
+
'--release-base',
|
|
330
|
+
'http://127.0.0.1:1/releases'
|
|
331
|
+
]);
|
|
332
|
+
ok(r2.code === 2 && r2.json?.ok === false && r2.json?.action === 'fallback-bundled', `CLI 失败 exit 2 + fallback-bundled JSON`);
|
|
333
|
+
lb.close();
|
|
334
|
+
rmSync(dest, {
|
|
335
|
+
recursive: true,
|
|
336
|
+
force: true
|
|
337
|
+
});
|
|
338
|
+
}console.log(`EXTENSION DISTRIBUTION: ${passed} pass, 0 fail`);
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
//# sourceURL=ts/scripts/extension-distribution-tests.ts
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
+
import { fork } from 'node:child_process';
|
|
2
3
|
import { createHash } from 'node:crypto';
|
|
3
4
|
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
4
5
|
import { tmpdir } from 'node:os';
|
|
5
6
|
import { join } from 'node:path';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import { BRIDGE_PORTS, EXTENSION_ID, EXTENSION_ORIGIN, createSessionBridge, __resetSessionBridgeForTest, __setSessionBridgeForTest } from '../capabilities/session/extension-bridge.js';
|
|
8
|
+
import { BRIDGE_PORTS, EXTENSION_ID, EXTENSION_ID_STORE, EXTENSION_ORIGIN, EXTENSION_ORIGIN_STORE, EXTENSION_ORIGINS, EXTENSION_STORE_URL, createSessionBridge, __resetSessionBridgeForTest, __setSessionBridgeForTest } from '../capabilities/session/extension-bridge.js';
|
|
8
9
|
import { classifyBridgeFailure, extensionCookieNames, extensionSearchJob } from '../capabilities/session/extension-channel.js';
|
|
9
10
|
import { appendExtensionAudit, resolveTransportMode, sessionFlightSearch } from '../capabilities/session-search.js';
|
|
10
11
|
import { sessionLogin } from '../capabilities/session-login.js';
|
|
11
12
|
import { LOGIN_COOKIE_NAMES, NETWORK_HINTS, SITE_DOMAIN } from '../capabilities/session/adapters/ctrip-flight.js';
|
|
12
13
|
import { evaluateDoubleSource } from '../capabilities/session/benchmark.js';
|
|
13
14
|
const EXT_DIR = fileURLToPath(new URL('../../extension/', import.meta.url));
|
|
15
|
+
const UNREF_CHILD = fileURLToPath(new URL('./fixtures/extension-bridge-unref-child.mjs', import.meta.url));
|
|
14
16
|
const read = (f)=>readFileSync(join(EXT_DIR, f), 'utf8');
|
|
15
17
|
let passed = 0;
|
|
16
18
|
async function check(label, assertion) {
|
|
@@ -20,8 +22,7 @@ async function check(label, assertion) {
|
|
|
20
22
|
}
|
|
21
23
|
const mustBridge = async (ports)=>{
|
|
22
24
|
const created = await createSessionBridge({
|
|
23
|
-
ports
|
|
24
|
-
extensionOrigin: EXTENSION_ORIGIN
|
|
25
|
+
ports
|
|
25
26
|
});
|
|
26
27
|
if (!created.ok) throw new Error(created.summary);
|
|
27
28
|
return created.bridge;
|
|
@@ -61,15 +62,89 @@ async function heartbeat(port) {
|
|
|
61
62
|
}
|
|
62
63
|
});
|
|
63
64
|
}
|
|
65
|
+
async function waitForParkedCount(port, expected, timeoutMs = 1_000) {
|
|
66
|
+
const deadline = Date.now() + timeoutMs;
|
|
67
|
+
while(Date.now() < deadline){
|
|
68
|
+
const r = await fetch(`http://127.0.0.1:${port}/status`);
|
|
69
|
+
const body = await r.json();
|
|
70
|
+
if (body.parked === expected) return;
|
|
71
|
+
await new Promise((resolve)=>setTimeout(resolve, 10));
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`/status 未在 ${timeoutMs}ms 内报告 parked=${expected}`);
|
|
74
|
+
}
|
|
75
|
+
async function assertParkedClientDoesNotPinHost() {
|
|
76
|
+
const child = fork(UNREF_CHILD, [], {
|
|
77
|
+
execArgv: process.execArgv,
|
|
78
|
+
stdio: [
|
|
79
|
+
'ignore',
|
|
80
|
+
'ignore',
|
|
81
|
+
'pipe',
|
|
82
|
+
'ipc'
|
|
83
|
+
]
|
|
84
|
+
});
|
|
85
|
+
let stderr = '';
|
|
86
|
+
child.stderr?.on('data', (chunk)=>{
|
|
87
|
+
stderr += chunk.toString('utf8');
|
|
88
|
+
});
|
|
89
|
+
let parkedRequest = null;
|
|
90
|
+
try {
|
|
91
|
+
const port = await new Promise((resolve, reject)=>{
|
|
92
|
+
const timer = setTimeout(()=>reject(new Error('子进程扩展桥未在 2s 内监听')), 2_000);
|
|
93
|
+
child.once('message', (message)=>{
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
const data = message;
|
|
96
|
+
if (data.kind === 'listening' && typeof data.port === 'number') resolve(data.port);
|
|
97
|
+
else reject(new Error(data.summary ?? `子进程消息异常:${JSON.stringify(data)}`));
|
|
98
|
+
});
|
|
99
|
+
child.once('exit', (code, signal)=>{
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
reject(new Error(`子进程过早退出 code=${code} signal=${signal} ${stderr}`));
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
parkedRequest = claimOnce(port, null).catch(()=>({
|
|
105
|
+
job: null
|
|
106
|
+
}));
|
|
107
|
+
await waitForParkedCount(port, 1);
|
|
108
|
+
const exitP = new Promise((resolve, reject)=>{
|
|
109
|
+
const timer = setTimeout(()=>{
|
|
110
|
+
child.kill();
|
|
111
|
+
reject(new Error('默认桥被 parked /jobs 长轮询钉住超过 1500ms'));
|
|
112
|
+
}, 1_500);
|
|
113
|
+
child.once('exit', (code, signal)=>{
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
resolve({
|
|
116
|
+
code,
|
|
117
|
+
signal
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
child.send({
|
|
122
|
+
kind: 'release'
|
|
123
|
+
});
|
|
124
|
+
const exited = await exitP;
|
|
125
|
+
assert.equal(exited.code, 0, `子进程应自然退出:${stderr}`);
|
|
126
|
+
assert.equal(exited.signal, null);
|
|
127
|
+
} finally{
|
|
128
|
+
if (child.exitCode === null && child.signalCode === null) child.kill();
|
|
129
|
+
if (parkedRequest) await parkedRequest;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
64
132
|
const stripRe = (s)=>s.replace(/\\/g, '');
|
|
65
133
|
async function main() {
|
|
66
134
|
console.log('§38 扩展传输车道合同(extension bridge,全离线)');
|
|
67
135
|
const manifest = JSON.parse(read('manifest.json'));
|
|
68
136
|
const derivedId = createHash('sha256').update(Buffer.from(manifest.key, 'base64')).digest().subarray(0, 16).toString('hex').replace(/[0-9a-f]/g, (c)=>'abcdefghijklmnop'[Number.parseInt(c, 16)]);
|
|
69
|
-
await check('manifest key 派生扩展 ID = Node 侧固定 EXTENSION_ID(origin 白名单锚点)', ()=>{
|
|
137
|
+
await check('manifest key 派生扩展 ID = Node 侧固定 EXTENSION_ID(unpacked 通道 origin 白名单锚点)', ()=>{
|
|
70
138
|
assert.equal(derivedId, EXTENSION_ID);
|
|
71
139
|
assert.equal(EXTENSION_ORIGIN, `chrome-extension://${EXTENSION_ID}`);
|
|
72
140
|
});
|
|
141
|
+
await check('双通道白名单:商店版 ID(CWS 重签,不认 manifest key)与 unpacked 固定 ID 同信,商店 URL 锚定 item ID', ()=>{
|
|
142
|
+
assert.equal(EXTENSION_ORIGIN_STORE, `chrome-extension://${EXTENSION_ID_STORE}`);
|
|
143
|
+
assert.ok(EXTENSION_ORIGINS.includes(EXTENSION_ORIGIN));
|
|
144
|
+
assert.ok(EXTENSION_ORIGINS.includes(EXTENSION_ORIGIN_STORE));
|
|
145
|
+
assert.equal(EXTENSION_ORIGINS.length, 2);
|
|
146
|
+
assert.ok(EXTENSION_STORE_URL.includes(EXTENSION_ID_STORE));
|
|
147
|
+
});
|
|
73
148
|
await check('manifest 合同:MV3 + 最小权限(cookies/alarms;无 debugger/tabs)', ()=>{
|
|
74
149
|
assert.equal(manifest.manifest_version, 3);
|
|
75
150
|
assert.deepEqual([
|
|
@@ -157,6 +232,7 @@ async function main() {
|
|
|
157
232
|
assert.equal(body.ok, true);
|
|
158
233
|
assert.equal(body.service, 'gotry-session-bridge');
|
|
159
234
|
});
|
|
235
|
+
await check('默认桥:parked 扩展长轮询不得钉住宿主 CLI 进程', assertParkedClientDoesNotPinHost);
|
|
160
236
|
await check('origin 白名单:邪恶源(网页跨域必带)POST /jobs 与 /results 一律 403', async ()=>{
|
|
161
237
|
for (const path of [
|
|
162
238
|
'/jobs',
|
|
@@ -173,6 +249,26 @@ async function main() {
|
|
|
173
249
|
assert.equal(r.status, 403, path);
|
|
174
250
|
}
|
|
175
251
|
});
|
|
252
|
+
await check('origin 白名单:商店版扩展源(CWS 重签 ID,2026-09-02 上架)POST 放行且计入心跳', async ()=>{
|
|
253
|
+
const storeLane = await mustBridge([
|
|
254
|
+
0
|
|
255
|
+
]);
|
|
256
|
+
try {
|
|
257
|
+
assert.equal(storeLane.extensionConnected(), false);
|
|
258
|
+
const r = await fetch(`http://127.0.0.1:${storeLane.port}/results/whatever`, {
|
|
259
|
+
method: 'POST',
|
|
260
|
+
headers: {
|
|
261
|
+
'content-type': 'application/json',
|
|
262
|
+
origin: EXTENSION_ORIGIN_STORE
|
|
263
|
+
},
|
|
264
|
+
body: '{}'
|
|
265
|
+
});
|
|
266
|
+
assert.equal(r.status, 200, '商店版扩展源不得吃 403(否则商店通道全断)');
|
|
267
|
+
assert.equal(storeLane.extensionConnected(), true, '白名单源的任何请求都刷新心跳');
|
|
268
|
+
} finally{
|
|
269
|
+
await storeLane.close();
|
|
270
|
+
}
|
|
271
|
+
});
|
|
176
272
|
await check('心跳判定:初始未连接;合法请求(health/长轮询)后视为在线', async ()=>{
|
|
177
273
|
assert.equal(b.extensionConnected(), false);
|
|
178
274
|
await heartbeat(port);
|
|
@@ -211,31 +307,40 @@ async function main() {
|
|
|
211
307
|
assert.ok(!/\.value/.test(backgroundJs), 'background.js 不得触碰 cookie.value');
|
|
212
308
|
});
|
|
213
309
|
await check('长轮询幂等:并发取活只有一个客户端领到 job;后到者空手(快速中止,不挂进程)', async ()=>{
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
timeoutMs: 5_000
|
|
218
|
-
}, {
|
|
219
|
-
timeoutMs: 5_000
|
|
220
|
-
});
|
|
221
|
-
const firstP = claimOnce(port, (job)=>({
|
|
310
|
+
const firstAc = new AbortController();
|
|
311
|
+
const secondAc = new AbortController();
|
|
312
|
+
const firstP = claimOnce(port, ()=>({
|
|
222
313
|
ok: true,
|
|
223
314
|
kind: 'cookie-names',
|
|
224
315
|
names: [
|
|
225
316
|
'cticket'
|
|
226
317
|
]
|
|
227
|
-
}))
|
|
228
|
-
const ac = new AbortController();
|
|
229
|
-
setTimeout(()=>ac.abort(), 300);
|
|
230
|
-
const secondP = claimOnce(port, null, ac.signal).catch(()=>({
|
|
318
|
+
}), firstAc.signal).catch(()=>({
|
|
231
319
|
job: null
|
|
232
320
|
}));
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
321
|
+
let secondP = null;
|
|
322
|
+
try {
|
|
323
|
+
await waitForParkedCount(port, 1);
|
|
324
|
+
secondP = claimOnce(port, null, secondAc.signal).catch(()=>({
|
|
325
|
+
job: null
|
|
326
|
+
}));
|
|
327
|
+
await waitForParkedCount(port, 2);
|
|
328
|
+
const submitP = b.submit({
|
|
329
|
+
kind: 'cookie-names',
|
|
330
|
+
site: 'ctrip-flight',
|
|
331
|
+
timeoutMs: 5_000
|
|
332
|
+
}, {
|
|
333
|
+
timeoutMs: 5_000
|
|
334
|
+
});
|
|
335
|
+
const first = await firstP;
|
|
336
|
+
assert.ok(first.job, '先进入 parked 队列的取活者必须领到 job');
|
|
337
|
+
const outcome = await submitP;
|
|
338
|
+
assert.ok(outcome.ok);
|
|
339
|
+
} finally{
|
|
340
|
+
firstAc.abort();
|
|
341
|
+
secondAc.abort();
|
|
342
|
+
if (secondP) await secondP;
|
|
343
|
+
}
|
|
239
344
|
});
|
|
240
345
|
await check('超时语义:扩展在线但不回包 → timeout(降级 error,不伪装成功)', async ()=>{
|
|
241
346
|
const outcome = await b.submit({
|
|
@@ -465,4 +570,4 @@ main().catch((e)=>{
|
|
|
465
570
|
});
|
|
466
571
|
|
|
467
572
|
|
|
468
|
-
//# sourceURL
|
|
573
|
+
//# sourceURL=ts/scripts/extension-tests.ts
|
|
@@ -144,4 +144,4 @@ console.log(`6. ENOENT 人话化 OK:${r6.summary}`);
|
|
|
144
144
|
console.log('HBCLI TESTS: 8/8 OK (happy / error / no-binary / fallback-filter / fallback-no-match / v0.3.0 旗标回归 / ENOENT 人话化 / 候选路径)');
|
|
145
145
|
|
|
146
146
|
|
|
147
|
-
//# sourceURL
|
|
147
|
+
//# sourceURL=ts/scripts/hbcli-tests.ts
|
|
@@ -42,4 +42,4 @@ assert.ok(r3.suggestions.some((sg)=>sg.relax === 'total:budget'));
|
|
|
42
42
|
console.log(`TS JOURNEY TESTS: 5/5 OK(五段全链可行,¥${r1.money_cny},与 Python oracle 结构一致)`);
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
//# sourceURL
|
|
45
|
+
//# sourceURL=ts/scripts/journey-tests.ts
|
|
@@ -80,4 +80,4 @@ pass('动机层零衰减(构造性):本模块无作用于 motivation-profile 的
|
|
|
80
80
|
console.log(`\nMEMORY DECAY TESTS: ${n}/5 OK(memory-design P3 分级窗口/单调/地板/上界/动机零衰减)`);
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
//# sourceURL
|
|
83
|
+
//# sourceURL=ts/scripts/memory-decay-tests.ts
|
|
@@ -21,4 +21,4 @@ console.log(`经验回流率基线 = ${verifiedWishes}/${recalledWishes} = ${ref
|
|
|
21
21
|
if (pool.length === 0) console.log('(wish pool 为空——首访用户,指标从首条憧憬入池开始积累)');
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
//# sourceURL
|
|
24
|
+
//# sourceURL=ts/scripts/memory-metrics.ts
|
|
@@ -376,4 +376,4 @@ const invokedPath = process.argv[1] ? pathToFileURL(resolve(process.argv[1])).hr
|
|
|
376
376
|
if (import.meta.url === invokedPath) main();
|
|
377
377
|
|
|
378
378
|
|
|
379
|
-
//# sourceURL
|
|
379
|
+
//# sourceURL=ts/scripts/memory-value-report.ts
|