@danceiny/gotry 0.2.0-rc.31 → 0.2.0-rc.32

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 (54) hide show
  1. package/README.md +2 -2
  2. package/README.zh-CN.md +2 -2
  3. package/bin/gotry-bootstrap.js +63 -53
  4. package/bin/gotry-hbcli-release.d.ts +3 -0
  5. package/bin/gotry-hbcli-release.js +4 -0
  6. package/client/client.js +66 -21
  7. package/dist/capabilities/doctor.js +7 -8
  8. package/dist/capabilities/effect.js +45 -12
  9. package/dist/capabilities/flyai-config.js +12 -10
  10. package/dist/capabilities/flyai.js +87 -56
  11. package/dist/capabilities/hbcli-config.js +156 -0
  12. package/dist/capabilities/hbcli.js +29 -2
  13. package/dist/capabilities/resilience.js +6 -6
  14. package/dist/capabilities/session/adapters/ctrip-flight.js +42 -7
  15. package/dist/capabilities/session/adapters/ctrip-hotel.js +52 -3
  16. package/dist/capabilities/session/ctrip-city.js +186 -0
  17. package/dist/capabilities/session/extension-bridge.js +8 -1
  18. package/dist/capabilities/session/gui-flight.js +737 -0
  19. package/dist/capabilities/session/gui-hotel.js +413 -0
  20. package/dist/capabilities/session-search.js +98 -11
  21. package/dist/scripts/bootstrap-tests.js +23 -6
  22. package/dist/scripts/ctrip-city-tests.js +80 -0
  23. package/dist/scripts/effect-tests.js +602 -504
  24. package/dist/scripts/extension-tests.js +14 -8
  25. package/dist/scripts/flight-malformed-tests.js +1 -1
  26. package/dist/scripts/flyai-setup-tests.js +19 -4
  27. package/dist/scripts/flyai-tests.js +768 -653
  28. package/dist/scripts/gotry-web-api-tests.js +5 -0
  29. package/dist/scripts/gotry-web-client-tests.js +20 -1
  30. package/dist/scripts/gui-flight-live.js +27 -0
  31. package/dist/scripts/hbcli-tests.js +1 -1
  32. package/dist/scripts/hbcli-upgrade-tests.js +107 -0
  33. package/dist/scripts/hbcli-web-setup-tests.js +114 -0
  34. package/dist/scripts/hotel-date-gate-tests.js +1 -1
  35. package/dist/scripts/hotel-result-tests.js +169 -0
  36. package/dist/scripts/issue-271-liveness-tests.js +1 -1
  37. package/dist/scripts/session-tests.js +177 -6
  38. package/dist/src/gotry-web-api.js +64 -2
  39. package/dist/src/index.js +40 -18
  40. package/extension/manifest.json +2 -2
  41. package/package.json +2 -1
  42. package/ts/capabilities/flyai-config.ts +12 -10
  43. package/ts/capabilities/flyai.ts +91 -50
  44. package/ts/capabilities/hbcli-config.ts +128 -0
  45. package/ts/capabilities/hbcli.ts +30 -3
  46. package/ts/capabilities/session/adapters/ctrip-flight.ts +43 -7
  47. package/ts/capabilities/session/adapters/ctrip-hotel.ts +51 -4
  48. package/ts/capabilities/session/ctrip-city.ts +158 -0
  49. package/ts/capabilities/session/extension-bridge.ts +9 -2
  50. package/ts/capabilities/session/gui-flight.ts +792 -0
  51. package/ts/capabilities/session/gui-hotel.ts +451 -0
  52. package/ts/capabilities/session-search.ts +115 -15
  53. package/ts/src/gotry-web-api.ts +51 -2
  54. package/ts/src/index.ts +41 -19
@@ -699,7 +699,7 @@ async function main() {
699
699
  __resetRateLimiterForTest();
700
700
  const cookiePoll = claimOnce(lane.port, null);
701
701
  await waitForParkedCount(lane.port, 1);
702
- const search = sessionFlightSearch({ from: '上海', to: '丽江', date: '2026-12-01', timeoutMs: 3_000 });
702
+ const search = sessionFlightSearch({ from: '上海', to: '丽江', fromCityCode: 'SHA', toCityCode: 'LJG', date: '2026-12-01', timeoutMs: 3_000 });
703
703
  const cookieClaim = await cookiePoll;
704
704
  assert.equal(cookieClaim.job?.kind, 'cookie-names');
705
705
  // 商店版先候在桥上;它不得接走另一 Origin 已验证登录态后的 search。
@@ -751,7 +751,7 @@ async function main() {
751
751
  __resetRateLimiterForTest();
752
752
  const cookiePoll = claimOnce(lane.port, null, undefined, ['ctrip-flight'], EXTENSION_ORIGIN, clientA);
753
753
  await waitForParkedCount(lane.port, 1);
754
- const search = sessionFlightSearch({ from: '上海', to: '丽江', date: '2026-12-01', timeoutMs: 3_000, auditPath: shapeAudit });
754
+ const search = sessionFlightSearch({ from: '上海', to: '丽江', fromCityCode: 'SHA', toCityCode: 'LJG', date: '2026-12-01', timeoutMs: 3_000, auditPath: shapeAudit });
755
755
  const cookieClaim = await cookiePoll;
756
756
  assert.equal(cookieClaim.job?.kind, 'cookie-names');
757
757
  otherPoll = claimOnce(lane.port, (job) => {
@@ -820,7 +820,7 @@ async function main() {
820
820
  })();
821
821
  try {
822
822
  __resetRateLimiterForTest();
823
- const result = await sessionFlightSearch({ from: '上海', to: '丽江', date: '2026-12-01', timeoutMs: 3_000 });
823
+ const result = await sessionFlightSearch({ from: '上海', to: '丽江', fromCityCode: 'SHA', toCityCode: 'LJG', date: '2026-12-01', timeoutMs: 3_000 });
824
824
  assert.equal(result.verdict, 'error');
825
825
  assert.equal(result.transportShape, 'sniff_timeout');
826
826
  assert.match(result.error ?? '', /嗅探超时|未收到.*回包/);
@@ -859,7 +859,7 @@ async function main() {
859
859
  })();
860
860
  try {
861
861
  __resetRateLimiterForTest();
862
- const result = await sessionFlightSearch({ from: '上海', to: '丽江', date: '2026-12-01', timeoutMs: 3_000 });
862
+ const result = await sessionFlightSearch({ from: '上海', to: '丽江', fromCityCode: 'SHA', toCityCode: 'LJG', date: '2026-12-01', timeoutMs: 3_000 });
863
863
  assert.equal(result.verdict, 'error');
864
864
  assert.equal(result.transportShape, failedKind === 'search' ? 'search_job_unavailable' : 'precheck_unavailable');
865
865
  assert.doesNotMatch(JSON.stringify(result), /PRIVATE_ROUTE_DATE_COOKIE_VALUE/);
@@ -882,12 +882,18 @@ async function main() {
882
882
  const blockers = [];
883
883
  try {
884
884
  for (const port of [8791, 8792, 8793, 8794, 8795]) {
885
- const s = createServer().listen(port, '127.0.0.1');
886
- await new Promise((r) => s.once('listening', () => r()));
887
- blockers.push({ close: () => new Promise((r) => s.close(() => r())) });
885
+ const s = createServer();
886
+ const owned = await new Promise((resolve, reject) => {
887
+ s.once('listening', () => resolve(true));
888
+ s.once('error', (error) => error.code === 'EADDRINUSE' ? resolve(false) : reject(error));
889
+ s.listen(port, '127.0.0.1');
890
+ });
891
+ // An active user bridge already occupies this port; never stop it.
892
+ if (owned)
893
+ blockers.push({ close: () => new Promise((r) => s.close(() => r())) });
888
894
  }
889
895
  await __resetSessionBridgeForTest();
890
- const r = await sessionFlightSearch({ from: '上海', to: '丽江', date: '2026-12-01' });
896
+ const r = await sessionFlightSearch({ from: '上海', to: '丽江', fromCityCode: 'SHA', toCityCode: 'LJG', date: '2026-12-01' });
891
897
  assert.equal(r.verdict, 'error', `端口池全占应 error(环境故障),实 ${r.verdict}:${r.error}`);
892
898
  assert.ok(r.error?.includes('端口池'), '错误面必须点明端口池环境问题');
893
899
  assert.ok(!r.error?.includes('一次性安装'), 'error 路径不应诱导用户去装扩展');
@@ -90,7 +90,7 @@ async function main() {
90
90
  const expectVerdict = async (label, body, title, expected, extra) => {
91
91
  __resetRateLimiterForTest();
92
92
  testCase = { body, title };
93
- const r = await sessionFlightSearch({ from: '上海', to: '丽江', date: '2026-12-01', timeoutMs: 5_000 });
93
+ const r = await sessionFlightSearch({ from: '上海', to: '丽江', fromCityCode: 'SHA', toCityCode: 'LJG', date: '2026-12-01', timeoutMs: 5_000 });
94
94
  await check(label, () => {
95
95
  assert.equal(r.verdict, expected, `verdict 期望 ${expected},实 ${r.verdict} (${r.error ?? ''})`);
96
96
  if (extra)
@@ -39,7 +39,7 @@ chmodSync(fakeVerifier, 0o700);
39
39
  // that the npm package exposes for `gotry setup ...` dispatch.
40
40
  const packageFixture = join(sandbox, 'package-shaped');
41
41
  mkdirSync(join(packageFixture, 'bin'), { recursive: true });
42
- for (const file of ['gotry-inner.js', 'gotry-bootstrap.js', 'gotry-flyai-verification.js', 'gotry-extension-status.js', 'gotry-process-liveness.js', 'gotry-runtime-resolution.js']) {
42
+ for (const file of ['gotry-inner.js', 'gotry-bootstrap.js', 'gotry-hbcli-release.js', 'gotry-flyai-verification.js', 'gotry-extension-status.js', 'gotry-process-liveness.js', 'gotry-runtime-resolution.js']) {
43
43
  copyFileSync(join(repoRoot, 'bin', file), join(packageFixture, 'bin', file));
44
44
  }
45
45
  writeFileSync(join(packageFixture, 'package.json'), JSON.stringify({ type: 'module', version: 'test' }) + '\n');
@@ -133,7 +133,7 @@ assert.ok(symlinkRun.stdout.includes('配置目录不安全'));
133
133
  assert.ok(!existsSync(join(symlinkTarget, 'config.json')), 'symlink target must not receive credentials');
134
134
  console.log('1c. symlink .flyai rejected without following target OK');
135
135
  // The TypeScript config writer shares the same compatibility rule; the
136
- // verification receipt writer remains separately strict on ~/.gotry.
136
+ // verification receipts use the same safe directory migration.
137
137
  const tsConfigHome = mkdtempSync(join(sandbox, 'ts-config-'));
138
138
  writeOfficialFlyaiDir(tsConfigHome, 'device-ts');
139
139
  const tsSave = saveFlyaiKey('sk-ts-config-key-1234', { homeDir: tsConfigHome, env: {} });
@@ -203,15 +203,30 @@ assert.notEqual(verifyFail.status, 0);
203
203
  assert.deepEqual(readFileSync(configPath(verifyFailHome)), verifyBefore);
204
204
  assert.ok(verifyFail.stdout.includes('配置未改动'));
205
205
  console.log('4. bad JSON + verifier failure preserve old config bytes/key OK');
206
+ // Existing doctor/runtime state must be secured without losing its contents.
207
+ const legacyHome = mkdtempSync(join(sandbox, 'legacy-receipt-'));
208
+ mkdirSync(join(legacyHome, '.gotry'), { mode: 0o755 });
209
+ chmodSync(join(legacyHome, '.gotry'), 0o755);
210
+ writeFileSync(join(legacyHome, '.gotry', 'doctor-report.md'), 'existing report');
211
+ const legacyRun = runPackageEntry(legacyHome, ['setup', 'flyai', '--stdin'], `${key}\n`);
212
+ assert.equal(legacyRun.status, 0, legacyRun.stdout + legacyRun.stderr);
213
+ assert.equal(mode(join(legacyHome, '.gotry')), 0o700);
214
+ assert.equal(mode(receiptPath(legacyHome)), 0o600);
215
+ assert.equal(readFileSync(join(legacyHome, '.gotry', 'doctor-report.md'), 'utf8'), 'existing report');
216
+ console.log('5a. existing 0755 state directory secured without losing contents OK');
206
217
  // 5. receipt write failure is visible and rolls back the newly attempted key.
207
218
  const receiptFailHome = mkdtempSync(join(sandbox, 'receipt-fail-'));
208
219
  writeConfig(receiptFailHome, { FLYAI_API_KEY: oldKey, keep: 'yes' });
209
- mkdirSync(join(receiptFailHome, '.gotry'), { mode: 0o755 });
210
- chmodSync(join(receiptFailHome, '.gotry'), 0o755);
220
+ const receiptTarget = join(receiptFailHome, 'outside-state');
221
+ mkdirSync(receiptTarget, { mode: 0o755 });
222
+ chmodSync(receiptTarget, 0o755);
223
+ symlinkSync(receiptTarget, join(receiptFailHome, '.gotry'), 'dir');
211
224
  const receiptFail = run(receiptFailHome, ['setup', 'flyai', '--stdin'], `${key}\n`);
212
225
  assert.notEqual(receiptFail.status, 0);
213
226
  assert.ok(receiptFail.stdout.includes('验证回执写入失败'));
214
227
  assert.equal(JSON.parse(readFileSync(configPath(receiptFailHome), 'utf8')).FLYAI_API_KEY, oldKey);
228
+ assert.equal(mode(receiptTarget), 0o755);
229
+ assert.ok(!existsSync(join(receiptTarget, 'flyai-verification.json')));
215
230
  console.log('5. receipt write failure reports non-success and preserves old key OK');
216
231
  // The verifier may echo its input credentials. Public diagnostics must not.
217
232
  for (const failure of ['leak-403', 'leak-exit']) {