@danceiny/gotry 0.0.1-rc.18 → 0.0.1-rc.20
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 +42 -16
- package/README.zh-CN.md +37 -15
- package/bin/gotry-bootstrap.js +309 -30
- package/bin/gotry-inner.js +74 -23
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +7 -8
- package/cordis.gotry-patch.yml +22 -8
- package/data/stations-12306-verify.json +524 -0
- package/dist/capabilities/agent-reach.js +4 -4
- package/dist/capabilities/anything.js +62 -29
- package/dist/capabilities/channel-health.js +103 -0
- package/dist/capabilities/channel-registry.js +213 -0
- package/dist/capabilities/doctor.js +307 -0
- package/dist/capabilities/effect.js +178 -5
- package/dist/capabilities/flyai.js +13 -0
- package/dist/capabilities/hbcli.js +28 -2
- package/dist/capabilities/session/adapters/ctrip-hotel.js +207 -0
- package/dist/capabilities/session/adapters/rail-12306.js +285 -0
- package/dist/capabilities/session/extension-bridge.js +14 -1
- package/dist/capabilities/session/extension-channel.js +4 -2
- package/dist/capabilities/session/extension-distribution.js +1 -1
- package/dist/capabilities/session-search.js +311 -0
- package/dist/capabilities/visa-policy.js +126 -0
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +305 -0
- package/dist/scripts/agent-planning-turn-deadline-tests.js +281 -0
- package/dist/scripts/anything-tests.js +70 -21
- package/dist/scripts/benchmark-environment-bridge-e2e.js +139 -37
- package/dist/scripts/benchmark-environment-bridge-tests.js +1099 -306
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +26 -26
- package/dist/scripts/{booking-copilot-availability-policy-v2-tests.js → booking-copilot-availability-policy-tests.js} +115 -115
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +41 -1
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +75 -120
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +370 -61
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +2 -2
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +7 -3
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +15 -9
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +19 -11
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +16 -52
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +3953 -155
- package/dist/scripts/booking-copilot-server-proof-tests.js +58 -23
- package/dist/scripts/booking-copilot-startup-proof-tests.js +10 -119
- package/dist/scripts/booking-surface-contract-proof-tests.js +1154 -326
- package/dist/scripts/bootstrap-tests.js +113 -2
- package/dist/scripts/build-metrics-report.js +339 -0
- package/dist/scripts/channel-probe-tests.js +148 -0
- package/dist/scripts/channel-probe.js +252 -0
- package/dist/scripts/channel-registry-tests.js +262 -0
- package/dist/scripts/doctor-tests.js +175 -0
- package/dist/scripts/effect-tests.js +274 -1
- package/dist/scripts/extension-tests.js +129 -11
- package/dist/scripts/fact-gate-tests.js +91 -2
- package/dist/scripts/flyai-tests.js +17 -1
- package/dist/scripts/hbcli-e2e-tests.js +1 -3
- package/dist/scripts/hbcli-tests.js +38 -1
- package/dist/scripts/metrics-report-tests.js +288 -0
- package/dist/scripts/persona-surface-guard-tests.js +19 -0
- package/dist/scripts/session-tests.js +401 -1
- package/dist/scripts/smoke.js +100 -83
- package/dist/scripts/turn-handoff-collect-tests.js +216 -0
- package/dist/scripts/turn-handoff-collect.js +189 -0
- package/dist/scripts/turn-policy-tests.js +56 -0
- package/dist/scripts/typed-contract-canary.js +261 -0
- package/dist/scripts/visa-policy-tests.js +75 -0
- package/dist/scripts/wish-channel-gate-tests.js +111 -0
- package/dist/src/artifact-gate.js +91 -3
- package/dist/src/benchmark-agent-conformance.js +39 -16
- package/dist/src/benchmark-environment-bridge.js +316 -52
- package/dist/src/bookable-facts.js +62 -3
- package/dist/src/booking-surface/{availability-policy-v2.js → availability-policy.js} +89 -89
- package/dist/src/booking-surface/canonical-schema.js +8 -22
- package/dist/src/booking-surface/contracts.js +70 -19
- package/dist/src/booking-surface/dsh-planner.js +310 -177
- package/dist/src/booking-surface/dsh-plugin.js +3 -3
- package/dist/src/booking-surface/index.js +1 -5
- package/dist/src/booking-surface/profile.js +1 -1
- package/dist/src/booking-surface/runtime.js +1682 -247
- package/dist/src/booking-surface/server.js +371 -185
- package/dist/src/booking-surface/startup.js +17 -27
- package/dist/src/booking-surface/validation.js +270 -760
- package/dist/src/index.js +739 -150
- package/dist/src/turn-deadline.js +292 -0
- package/dist/src/turn-policy.js +146 -0
- package/dist/src/wish-pool.js +5 -0
- package/extension/background.js +23 -4
- package/extension/content-main.js +47 -9
- package/extension/manifest.json +27 -9
- package/package.json +7 -26
- package/schemas/booking.surface.schema.json +2593 -0
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +89 -38
- package/ts/capabilities/flyai.ts +21 -3
- package/ts/capabilities/hbcli.ts +53 -4
- package/ts/capabilities/session/action-cache.ts +1 -1
- package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +293 -0
- package/ts/capabilities/session/extension-bridge.ts +32 -2
- package/ts/capabilities/session/extension-channel.ts +9 -8
- package/ts/capabilities/session/extension-distribution.ts +3 -1
- package/ts/capabilities/session/health-watch.ts +1 -1
- package/ts/capabilities/session/read-guard.ts +1 -1
- package/ts/capabilities/session/wizard.ts +1 -1
- package/ts/capabilities/session-search.ts +323 -1
- package/ts/dsh-runtime/vendor/README.md +60 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +200 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +202 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +216 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +371 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +56 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +126 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +109 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +27 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +170 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +53 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +20 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +91 -0
- package/ts/package.json +9 -0
- package/ts/scripts/turn-handoff-collect.ts +178 -0
- package/ts/src/artifact-gate.ts +82 -2
- package/ts/src/benchmark-agent-conformance.ts +54 -15
- package/ts/src/benchmark-environment-bridge.ts +234 -46
- package/ts/src/bookable-facts.ts +109 -5
- package/ts/src/booking-saga.ts +1 -1
- package/ts/src/booking-surface/{availability-policy-v2.ts → availability-policy.ts} +124 -125
- package/ts/src/booking-surface/canonical-schema.js +8 -22
- package/ts/src/booking-surface/contracts.ts +278 -239
- package/ts/src/booking-surface/dsh-planner.ts +333 -194
- package/ts/src/booking-surface/dsh-plugin.js +3 -3
- package/ts/src/booking-surface/index.ts +1 -5
- package/ts/src/booking-surface/profile.ts +11 -11
- package/ts/src/booking-surface/runtime.ts +1351 -351
- package/ts/src/booking-surface/server.ts +282 -175
- package/ts/src/booking-surface/startup.ts +36 -48
- package/ts/src/booking-surface/validation.ts +194 -442
- package/ts/src/contracts.ts +1 -1
- package/ts/src/index.ts +492 -172
- package/ts/src/loop.ts +1 -1
- package/ts/src/state-ledger.ts +1 -1
- package/ts/src/turn-deadline.ts +365 -0
- package/ts/src/turn-policy.ts +154 -0
- package/ts/src/wish-pool.ts +17 -4
- package/dist/scripts/agent-planning-budget-e2e.js +0 -227
- package/dist/scripts/agent-planning-budget-tests.js +0 -173
- package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +0 -3935
- package/dist/scripts/booking-surface-v2-contract-proof-tests.js +0 -1174
- package/dist/src/booking-surface/contracts-v2.js +0 -89
- package/dist/src/booking-surface/runtime-v2.js +0 -1771
- package/dist/src/booking-surface/server-v2.js +0 -334
- package/dist/src/booking-surface/validation-v2.js +0 -319
- package/dist/src/tool-budget.js +0 -136
- package/schemas/booking.surface.v1.schema.json +0 -927
- package/schemas/booking.surface.v2.schema.json +0 -61
- package/ts/src/booking-surface/contracts-v2.ts +0 -118
- package/ts/src/booking-surface/runtime-v2.ts +0 -1466
- package/ts/src/booking-surface/server-v2.ts +0 -247
- package/ts/src/booking-surface/validation-v2.ts +0 -205
- package/ts/src/tool-budget.ts +0 -165
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
const HOTEL_CITY_CODES = {
|
|
2
|
+
上海: '2',
|
|
3
|
+
北京: '1',
|
|
4
|
+
广州: '32',
|
|
5
|
+
深圳: '30',
|
|
6
|
+
成都: '28',
|
|
7
|
+
杭州: '17',
|
|
8
|
+
南京: '12',
|
|
9
|
+
苏州: '14',
|
|
10
|
+
无锡: '13',
|
|
11
|
+
扬州: '15',
|
|
12
|
+
镇江: '16',
|
|
13
|
+
青岛: '7',
|
|
14
|
+
厦门: '25',
|
|
15
|
+
珠海: '31',
|
|
16
|
+
三亚: '43',
|
|
17
|
+
海口: '42',
|
|
18
|
+
桂林: '33',
|
|
19
|
+
昆明: '34',
|
|
20
|
+
大理: '36',
|
|
21
|
+
丽江: '37',
|
|
22
|
+
西双版纳: '35',
|
|
23
|
+
贵阳: '38',
|
|
24
|
+
拉萨: '41',
|
|
25
|
+
乌鲁木齐: '39',
|
|
26
|
+
太原: '105',
|
|
27
|
+
长春: '158',
|
|
28
|
+
南通: '82',
|
|
29
|
+
敦煌: '11',
|
|
30
|
+
舟山: '19',
|
|
31
|
+
张家界: '27',
|
|
32
|
+
黄山市: '23',
|
|
33
|
+
九江: '24',
|
|
34
|
+
武夷山: '26',
|
|
35
|
+
迪拜: '220'
|
|
36
|
+
};
|
|
37
|
+
export function buildHotelEntryUrl(q) {
|
|
38
|
+
const unresolved = [];
|
|
39
|
+
const cityId = String(q.cityId ?? '').trim() || HOTEL_CITY_CODES[q.to.trim()];
|
|
40
|
+
if (!cityId) unresolved.push(q.to);
|
|
41
|
+
if (unresolved.length > 0) {
|
|
42
|
+
return {
|
|
43
|
+
ok: false,
|
|
44
|
+
unresolved
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const params = new URLSearchParams({
|
|
48
|
+
city: cityId
|
|
49
|
+
});
|
|
50
|
+
if (q.checkIn) params.set('checkin', q.checkIn);
|
|
51
|
+
if (q.checkOut) params.set('checkout', q.checkOut);
|
|
52
|
+
if (q.adults && q.adults > 0) params.set('adult', String(q.adults));
|
|
53
|
+
return {
|
|
54
|
+
ok: true,
|
|
55
|
+
url: `https://hotels.ctrip.com/hotels/list?${params.toString()}`
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export const HOTEL_NETWORK_HINTS = [
|
|
59
|
+
/hotels\.ctrip\.com\/(hotels\/api|domestic\/pc\/api)/i,
|
|
60
|
+
/GetHotelListBySOA|GetHotelListByCity|HotelSearch|hotelsearch/i,
|
|
61
|
+
/restapi\/soa2\/\d+\/fetchHotelList/i
|
|
62
|
+
];
|
|
63
|
+
export function looksLikeHotelListBody(body) {
|
|
64
|
+
if (!body || body.length > 2_000_000) return false;
|
|
65
|
+
return /"hotelList"|"hotelMatchInfos"|"hotelName"/.test(body);
|
|
66
|
+
}
|
|
67
|
+
export const HOTEL_SITE_HOST = 'hotels.ctrip.com';
|
|
68
|
+
function hotelSignature(o) {
|
|
69
|
+
const name = o.hotelName ?? o.name ?? o.hotelNameEn;
|
|
70
|
+
if (typeof name !== 'string' || !name.trim()) return null;
|
|
71
|
+
let price;
|
|
72
|
+
let priceRaw;
|
|
73
|
+
if (typeof o.price === 'number' && o.price > 0) price = o.price;
|
|
74
|
+
else if (typeof o.price === 'string' && o.price.trim()) priceRaw = o.price.trim();
|
|
75
|
+
const probe = (v, seen = new Set())=>{
|
|
76
|
+
if (v == null || typeof v !== 'object') return;
|
|
77
|
+
if (seen.has(v)) return;
|
|
78
|
+
seen.add(v);
|
|
79
|
+
for (const [k, child] of Object.entries(v)){
|
|
80
|
+
if (/^(avgPrice|totalPrice|price|amount|total|startPrice)$/i.test(k) && typeof child === 'number' && child > 0 && price === undefined) price = child;
|
|
81
|
+
else if (/^(price|priceDisplay|amountText)$/i.test(k) && typeof child === 'string' && child.trim() && priceRaw === undefined) priceRaw = child.trim();
|
|
82
|
+
else probe(child, seen);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
probe(o.priceInfo);
|
|
86
|
+
const flatPrice = o.totalPrice ?? o.avgPrice ?? o.minPrice;
|
|
87
|
+
if (typeof flatPrice === 'number' && flatPrice > 0 && price === undefined) price = flatPrice;
|
|
88
|
+
const flatRaw = o.priceText ?? o.priceDisplay;
|
|
89
|
+
if (typeof flatRaw === 'string' && flatRaw.trim() && priceRaw === undefined) priceRaw = flatRaw.trim();
|
|
90
|
+
if (price === undefined && priceRaw === undefined) return null;
|
|
91
|
+
const starRaw = o.star ?? o.level ?? o.hotelStar;
|
|
92
|
+
const star = typeof starRaw === 'number' && starRaw > 0 ? starRaw : Number.isFinite(Number(starRaw)) && Number(starRaw) > 0 ? Number(starRaw) : undefined;
|
|
93
|
+
const scoreRaw = o.score ?? o.commentScore ?? o.reviewScore;
|
|
94
|
+
const score = scoreRaw == null ? undefined : String(scoreRaw);
|
|
95
|
+
const pos = o.position;
|
|
96
|
+
const address = (typeof pos?.address === 'string' ? pos.address : undefined) ?? (typeof o.address === 'string' ? o.address : undefined);
|
|
97
|
+
const hotelId = (typeof o.hotelId === 'number' || typeof o.hotelId === 'string' ? String(o.hotelId) : undefined) ?? (typeof o.hotelIdStr === 'string' ? o.hotelIdStr : undefined);
|
|
98
|
+
return {
|
|
99
|
+
name: name.trim(),
|
|
100
|
+
price,
|
|
101
|
+
priceRaw,
|
|
102
|
+
star,
|
|
103
|
+
score,
|
|
104
|
+
address,
|
|
105
|
+
hotelId
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function ctripStructuredCandidate(item) {
|
|
109
|
+
const info = item.hotelInfo;
|
|
110
|
+
const summary = info?.summary ?? item;
|
|
111
|
+
const nameInfo = summary.nameInfo;
|
|
112
|
+
const names = nameInfo?.names;
|
|
113
|
+
const nameRaw = nameInfo?.name ?? (Array.isArray(names) && names.length > 0 ? names[0] : undefined) ?? summary.hotelName ?? summary.name;
|
|
114
|
+
if (typeof nameRaw !== 'string' || !nameRaw.trim()) return null;
|
|
115
|
+
let price;
|
|
116
|
+
let priceRaw;
|
|
117
|
+
let encrypted = false;
|
|
118
|
+
const rooms = info?.roomInfo ?? item.roomInfo;
|
|
119
|
+
if (Array.isArray(rooms)) {
|
|
120
|
+
for (const room of rooms){
|
|
121
|
+
if (room == null || typeof room !== 'object') continue;
|
|
122
|
+
const pi = room.priceInfo;
|
|
123
|
+
if (!pi) continue;
|
|
124
|
+
if (typeof pi.price === 'number' && pi.price > 0) price = price === undefined ? pi.price : Math.min(price, pi.price);
|
|
125
|
+
if (typeof pi.price === 'string' && pi.price.trim() && !priceRaw) priceRaw = pi.price.trim();
|
|
126
|
+
if (typeof pi.displayPrice === 'string' && pi.displayPrice.trim() && !priceRaw) priceRaw = pi.displayPrice.trim();
|
|
127
|
+
if (pi.priceToken != null && pi.price === undefined) encrypted = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (price === undefined && !priceRaw) {
|
|
131
|
+
const priceProbe = (v, depth, seen)=>{
|
|
132
|
+
if (price !== undefined || depth > 6 || v == null || typeof v !== 'object' || seen.has(v)) return;
|
|
133
|
+
seen.add(v);
|
|
134
|
+
for (const [k, child] of Object.entries(v)){
|
|
135
|
+
if (/^(price|avgPrice|totalPrice|minPrice|amount)$/i.test(k) && typeof child === 'number' && child > 0) {
|
|
136
|
+
price = child;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (/^(price|displayPrice|amountText)$/i.test(k) && typeof child === 'string' && child.trim()) {
|
|
140
|
+
priceRaw = priceRaw ?? child.trim();
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
priceProbe(child, depth + 1, seen);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
priceProbe(item, 0, new Set());
|
|
147
|
+
}
|
|
148
|
+
if (price === undefined && !priceRaw && !encrypted) return null;
|
|
149
|
+
const starOf = summary.hotelStar;
|
|
150
|
+
const starRaw = starOf?.star ?? summary.star;
|
|
151
|
+
const star = typeof starRaw === 'number' && starRaw > 0 ? starRaw : undefined;
|
|
152
|
+
const scoreRaw = (info?.commentInfo ?? summary.commentInfo)?.commentScore;
|
|
153
|
+
const pos = summary.positionInfo;
|
|
154
|
+
const address = (typeof pos?.address === 'string' ? pos.address : undefined) ?? (typeof summary.address === 'string' ? summary.address : undefined);
|
|
155
|
+
const hotelId = summary.hotelId != null ? String(summary.hotelId) : undefined;
|
|
156
|
+
return {
|
|
157
|
+
name: nameRaw.trim(),
|
|
158
|
+
price,
|
|
159
|
+
priceRaw,
|
|
160
|
+
star,
|
|
161
|
+
score: scoreRaw == null ? undefined : String(scoreRaw),
|
|
162
|
+
address,
|
|
163
|
+
hotelId
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export function parseCtripHotelList(body, opts = {}) {
|
|
167
|
+
let raw;
|
|
168
|
+
try {
|
|
169
|
+
raw = JSON.parse(body);
|
|
170
|
+
} catch {
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
const out = [];
|
|
174
|
+
const maxItems = opts.maxItems ?? 20;
|
|
175
|
+
const walk = (node, depth, seen)=>{
|
|
176
|
+
if (out.length >= maxItems || depth > 12 || node == null || typeof node !== 'object' || seen.has(node)) return;
|
|
177
|
+
if (Array.isArray(node)) {
|
|
178
|
+
for (const item of node){
|
|
179
|
+
if (out.length >= maxItems) return;
|
|
180
|
+
if (item == null || typeof item !== 'object' || Array.isArray(item)) continue;
|
|
181
|
+
const cand = hotelSignature(item) ?? ctripStructuredCandidate(item);
|
|
182
|
+
if (!cand) continue;
|
|
183
|
+
out.push({
|
|
184
|
+
name: cand.name,
|
|
185
|
+
price: cand.price ?? 0,
|
|
186
|
+
priceRaw: cand.priceRaw,
|
|
187
|
+
star: cand.star,
|
|
188
|
+
score: cand.score,
|
|
189
|
+
address: cand.address,
|
|
190
|
+
hotelId: cand.hotelId,
|
|
191
|
+
jumpUrl: cand.hotelId ? `https://hotels.ctrip.com/hotel/${cand.hotelId}` : undefined
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
seen.add(node);
|
|
197
|
+
for (const child of Object.values(node)){
|
|
198
|
+
walk(child, depth + 1, seen);
|
|
199
|
+
if (out.length >= maxItems) return;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
walk(raw, 0, new Set());
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
//# sourceURL=ts/capabilities/session/adapters/ctrip-hotel.ts
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
export const STATION_TELECODES = {
|
|
2
|
+
"三亚": 'SEQ',
|
|
3
|
+
"上海": 'SHH',
|
|
4
|
+
"上饶": 'SRG',
|
|
5
|
+
"东莞": 'RTQ',
|
|
6
|
+
"中卫": 'ZWJ',
|
|
7
|
+
"中山": 'ZSQ',
|
|
8
|
+
"丽江": 'LHM',
|
|
9
|
+
"乌鲁木齐": 'WAR',
|
|
10
|
+
"乐山": 'IVW',
|
|
11
|
+
"九江": 'JJG',
|
|
12
|
+
"伊宁": 'YMR',
|
|
13
|
+
"佛山": 'FSQ',
|
|
14
|
+
"保山": 'BAM',
|
|
15
|
+
"六盘水": 'UMW',
|
|
16
|
+
"兰州": 'LZJ',
|
|
17
|
+
"包头": 'BTC',
|
|
18
|
+
"北京": 'BJP',
|
|
19
|
+
"北海": 'BHZ',
|
|
20
|
+
"十堰": 'SNN',
|
|
21
|
+
"南京": 'NJH',
|
|
22
|
+
"南充": 'NCW',
|
|
23
|
+
"南宁": 'NNZ',
|
|
24
|
+
"南昌": 'NCG',
|
|
25
|
+
"南通": 'NUH',
|
|
26
|
+
"南阳": 'NFF',
|
|
27
|
+
"厦门": 'XMS',
|
|
28
|
+
"台州": 'TEU',
|
|
29
|
+
"合肥": 'HFH',
|
|
30
|
+
"吉林": 'JLL',
|
|
31
|
+
"吐鲁番": 'TFR',
|
|
32
|
+
"呼和浩特": 'HHC',
|
|
33
|
+
"咸阳": 'XYY',
|
|
34
|
+
"哈密": 'HMR',
|
|
35
|
+
"哈尔滨": 'HBB',
|
|
36
|
+
"喀什": 'KSR',
|
|
37
|
+
"嘉兴": 'JXH',
|
|
38
|
+
"嘉峪关": 'JGJ',
|
|
39
|
+
"大同": 'DTV',
|
|
40
|
+
"大庆": 'DZX',
|
|
41
|
+
"大理": 'DKM',
|
|
42
|
+
"大连": 'DLT',
|
|
43
|
+
"天水": 'TSJ',
|
|
44
|
+
"天津": 'TJP',
|
|
45
|
+
"太原": 'TYV',
|
|
46
|
+
"宁波": 'NGH',
|
|
47
|
+
"安顺": 'ASW',
|
|
48
|
+
"宜宾": 'YKE',
|
|
49
|
+
"宜昌": 'YCN',
|
|
50
|
+
"宝鸡": 'BJY',
|
|
51
|
+
"岳阳": 'YYQ',
|
|
52
|
+
"常州": 'CZH',
|
|
53
|
+
"常德": 'VGQ',
|
|
54
|
+
"广州": 'GZQ',
|
|
55
|
+
"库尔勒": 'KLR',
|
|
56
|
+
"延安": 'YWY',
|
|
57
|
+
"开封": 'KFF',
|
|
58
|
+
"张掖": 'ZYJ',
|
|
59
|
+
"徐州": 'XCH',
|
|
60
|
+
"德阳": 'DYW',
|
|
61
|
+
"惠州": 'HCQ',
|
|
62
|
+
"成都": 'CDW',
|
|
63
|
+
"扬州": 'YLH',
|
|
64
|
+
"承德": 'CDP',
|
|
65
|
+
"抚顺": 'FET',
|
|
66
|
+
"拉萨": 'LSO',
|
|
67
|
+
"揭阳": 'JYA',
|
|
68
|
+
"新乡": 'XXF',
|
|
69
|
+
"无锡": 'WGH',
|
|
70
|
+
"日喀则": 'RKO',
|
|
71
|
+
"昆明": 'KMM',
|
|
72
|
+
"普洱": 'PEM',
|
|
73
|
+
"景德镇": 'JCG',
|
|
74
|
+
"曲靖": 'QJM',
|
|
75
|
+
"杭州": 'HZH',
|
|
76
|
+
"林芝": 'LZO',
|
|
77
|
+
"柳州": 'LZZ',
|
|
78
|
+
"株洲": 'ZZQ',
|
|
79
|
+
"格尔木": 'GRO',
|
|
80
|
+
"桂林": 'GLZ',
|
|
81
|
+
"梧州": 'WZZ',
|
|
82
|
+
"武汉": 'WHN',
|
|
83
|
+
"汉中": 'HOY',
|
|
84
|
+
"汕头": 'OTQ',
|
|
85
|
+
"沈阳": 'SYT',
|
|
86
|
+
"泉州": 'QYS',
|
|
87
|
+
"泸州": 'LUE',
|
|
88
|
+
"洛阳": 'LYF',
|
|
89
|
+
"济南": 'JNK',
|
|
90
|
+
"深圳": 'SZQ',
|
|
91
|
+
"温州": 'RZH',
|
|
92
|
+
"湛江": 'ZJZ',
|
|
93
|
+
"漳州": 'ZUS',
|
|
94
|
+
"潮州": 'CKQ',
|
|
95
|
+
"牡丹江": 'MDB',
|
|
96
|
+
"珠海": 'ZHQ',
|
|
97
|
+
"盐城": 'AFH',
|
|
98
|
+
"石家庄": 'SJP',
|
|
99
|
+
"福州": 'FZS',
|
|
100
|
+
"秦皇岛": 'QTP',
|
|
101
|
+
"绍兴": 'SOH',
|
|
102
|
+
"绵阳": 'MYW',
|
|
103
|
+
"芜湖": 'WHH',
|
|
104
|
+
"苏州": 'SZH',
|
|
105
|
+
"茂名": 'MDQ',
|
|
106
|
+
"莆田": 'PTS',
|
|
107
|
+
"衡阳": 'HYQ',
|
|
108
|
+
"襄阳": 'XFN',
|
|
109
|
+
"西宁": 'XNO',
|
|
110
|
+
"西安": 'XAY',
|
|
111
|
+
"西昌": 'ECW',
|
|
112
|
+
"贵阳": 'GIW',
|
|
113
|
+
"赣州": 'GZG',
|
|
114
|
+
"达州": 'RXW',
|
|
115
|
+
"遵义": 'ZYE',
|
|
116
|
+
"郑州": 'ZZF',
|
|
117
|
+
"重庆": 'CUW',
|
|
118
|
+
"金华": 'JBH',
|
|
119
|
+
"银川": 'YIJ',
|
|
120
|
+
"镇江": 'ZJH',
|
|
121
|
+
"长春": 'CCT',
|
|
122
|
+
"长沙": 'CSQ',
|
|
123
|
+
"阜阳": 'FYH',
|
|
124
|
+
"青岛": 'QDK',
|
|
125
|
+
"鞍山": 'AST',
|
|
126
|
+
"韶关": 'SNQ',
|
|
127
|
+
"香格里拉": 'EUM',
|
|
128
|
+
"香港": 'XJA',
|
|
129
|
+
"齐齐哈尔": 'QHX',
|
|
130
|
+
"龙岩": 'LYS'
|
|
131
|
+
};
|
|
132
|
+
export function buildTrainEntryUrl(q) {
|
|
133
|
+
const unresolved = [];
|
|
134
|
+
const fromTc = (q.fromStationTelecode ?? '').trim().toUpperCase() || STATION_TELECODES[q.from.trim()];
|
|
135
|
+
const toTc = (q.toStationTelecode ?? '').trim().toUpperCase() || STATION_TELECODES[q.to.trim()];
|
|
136
|
+
if (!fromTc) unresolved.push(q.from);
|
|
137
|
+
if (!toTc) unresolved.push(q.to);
|
|
138
|
+
if (unresolved.length > 0 || !/^\d{4}-\d{2}-\d{2}$/.test(q.date)) {
|
|
139
|
+
return {
|
|
140
|
+
ok: false,
|
|
141
|
+
unresolved: unresolved.length > 0 ? unresolved : [
|
|
142
|
+
q.date
|
|
143
|
+
]
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const fs = encodeURIComponent(`${q.from.trim()},${fromTc}`);
|
|
147
|
+
const ts = encodeURIComponent(`${q.to.trim()},${toTc}`);
|
|
148
|
+
return {
|
|
149
|
+
ok: true,
|
|
150
|
+
url: `https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=${fs}&ts=${ts}&date=${q.date}&flag=N,N,Y`
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
export const TRAIN_NETWORK_HINTS = [
|
|
154
|
+
/leftTicket\/query/i
|
|
155
|
+
];
|
|
156
|
+
export const TRAIN_SITE_HOST = 'kyfw.12306.cn';
|
|
157
|
+
const SEAT_BUCKETS = [
|
|
158
|
+
{
|
|
159
|
+
index: 20,
|
|
160
|
+
field: 'gg_num',
|
|
161
|
+
label: '其他(通勤)'
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
index: 21,
|
|
165
|
+
field: 'gr_num',
|
|
166
|
+
label: '高级软卧'
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
index: 22,
|
|
170
|
+
field: 'qt_num',
|
|
171
|
+
label: '其他'
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
index: 23,
|
|
175
|
+
field: 'rw_num',
|
|
176
|
+
label: '软卧(一等卧)'
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
index: 24,
|
|
180
|
+
field: 'rz_num',
|
|
181
|
+
label: '软座'
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
index: 25,
|
|
185
|
+
field: 'tz_num',
|
|
186
|
+
label: '特等座'
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
index: 26,
|
|
190
|
+
field: 'wz_num',
|
|
191
|
+
label: '无座'
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
index: 27,
|
|
195
|
+
field: 'yb_num',
|
|
196
|
+
label: 'yp_b(上游席别)'
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
index: 28,
|
|
200
|
+
field: 'yw_num',
|
|
201
|
+
label: '硬卧(二等卧)'
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
index: 29,
|
|
205
|
+
field: 'yz_num',
|
|
206
|
+
label: '硬座'
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
index: 30,
|
|
210
|
+
field: 'ze_num',
|
|
211
|
+
label: '二等座'
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
index: 31,
|
|
215
|
+
field: 'zy_num',
|
|
216
|
+
label: '一等座'
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
index: 32,
|
|
220
|
+
field: 'swz_num',
|
|
221
|
+
label: '商务座'
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
index: 33,
|
|
225
|
+
field: 'srrb_num',
|
|
226
|
+
label: '动卧'
|
|
227
|
+
}
|
|
228
|
+
];
|
|
229
|
+
const TRAIN_CODE_RE = /^[GDCZTKYLSF]\d{1,5}[A-Z]?$/i;
|
|
230
|
+
const HHMM_RE = /^\d{2}:\d{2}$/;
|
|
231
|
+
function lishiToMin(s) {
|
|
232
|
+
const m = /^(\d{1,3}):(\d{2})$/.exec((s ?? '').trim());
|
|
233
|
+
if (!m) return 0;
|
|
234
|
+
return Number(m[1]) * 60 + Number(m[2]);
|
|
235
|
+
}
|
|
236
|
+
function rowToOption(row, stationMap, entryUrl) {
|
|
237
|
+
const f = row.split('|');
|
|
238
|
+
if (f.length < 34) return null;
|
|
239
|
+
const trainCode = (f[3] ?? '').trim();
|
|
240
|
+
const depTime = (f[8] ?? '').trim();
|
|
241
|
+
const arrTime = (f[9] ?? '').trim();
|
|
242
|
+
if (!TRAIN_CODE_RE.test(trainCode) || !HHMM_RE.test(depTime) || !HHMM_RE.test(arrTime)) return null;
|
|
243
|
+
const fromStation = stationMap?.[f[6] ?? ''] ?? f[6] ?? '';
|
|
244
|
+
const toStation = stationMap?.[f[7] ?? ''] ?? f[7] ?? '';
|
|
245
|
+
const seats = {};
|
|
246
|
+
for (const b of SEAT_BUCKETS){
|
|
247
|
+
const v = (f[b.index] ?? '').trim();
|
|
248
|
+
if (v && v !== '--') seats[b.label] = v;
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
trainCode,
|
|
252
|
+
fromStation,
|
|
253
|
+
toStation,
|
|
254
|
+
depTime,
|
|
255
|
+
arrTime,
|
|
256
|
+
durationMin: lishiToMin(f[10]),
|
|
257
|
+
canWebBuy: (f[11] ?? '').trim(),
|
|
258
|
+
seats,
|
|
259
|
+
jumpUrl: entryUrl
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
export function parseLeftTicketQuery(body, entryUrl, opts = {}) {
|
|
263
|
+
let raw;
|
|
264
|
+
try {
|
|
265
|
+
raw = JSON.parse(body);
|
|
266
|
+
} catch {
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
const rows = raw.data?.result;
|
|
270
|
+
if (!Array.isArray(rows)) return [];
|
|
271
|
+
const map = raw.data?.map && typeof raw.data.map === 'object' ? raw.data.map : undefined;
|
|
272
|
+
const out = [];
|
|
273
|
+
for (const r of rows){
|
|
274
|
+
if (typeof r !== 'string') continue;
|
|
275
|
+
const opt = rowToOption(r, map, entryUrl);
|
|
276
|
+
if (opt) {
|
|
277
|
+
out.push(opt);
|
|
278
|
+
if (out.length >= (opts.maxItems ?? 30)) break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
//# sourceURL=ts/capabilities/session/adapters/rail-12306.ts
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
2
5
|
import { createServer } from 'node:http';
|
|
3
6
|
export const BRIDGE_PORTS = [
|
|
4
7
|
8791,
|
|
@@ -17,6 +20,16 @@ export const EXTENSION_ORIGINS = [
|
|
|
17
20
|
EXTENSION_ORIGIN,
|
|
18
21
|
EXTENSION_ORIGIN_STORE
|
|
19
22
|
];
|
|
23
|
+
export function localExtensionInstalled(home = homedir()) {
|
|
24
|
+
return existsSync(join(home, '.gotry', 'extension', 'manifest.json'));
|
|
25
|
+
}
|
|
26
|
+
export function needsExtensionSummary(opts = {}) {
|
|
27
|
+
const local = opts.localInstalled ?? localExtensionInstalled(opts.home);
|
|
28
|
+
if (local) {
|
|
29
|
+
return 'GoTry Session Bridge 扩展未连接(未安装或已停用)。推荐 Chrome 应用商店一键装(自动更新) ' + `${EXTENSION_STORE_URL} ;或 npx gotry setup 落位后 chrome://extensions 开发者模式「加载已解压的扩展程序」指向 ~/.gotry/extension`;
|
|
30
|
+
}
|
|
31
|
+
return 'GoTry Session Bridge 扩展未连接。若你已从 Chrome 商店安装(自动更新):打开 Chrome 并确认扩展已启用即可自动连接,无需再装。' + `若尚未安装:应用商店一键装 ${EXTENSION_STORE_URL} (装完零弹窗,浏览器自己当安装器)`;
|
|
32
|
+
}
|
|
20
33
|
export const EXTENSION_CONNECTED_WINDOW_MS = 45_000;
|
|
21
34
|
export const JOBS_LONG_POLL_MS = 20_000;
|
|
22
35
|
export const DEFAULT_EXTENSION_WAIT_MS = 6_000;
|
|
@@ -267,7 +280,7 @@ export async function createSessionBridge(opts = {}) {
|
|
|
267
280
|
settle({
|
|
268
281
|
ok: false,
|
|
269
282
|
reason: 'extension-not-connected',
|
|
270
|
-
summary:
|
|
283
|
+
summary: needsExtensionSummary()
|
|
271
284
|
});
|
|
272
285
|
}
|
|
273
286
|
}, 250);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { getOrCreateSessionBridge,
|
|
2
|
-
export
|
|
1
|
+
import { getOrCreateSessionBridge, needsExtensionSummary } from './extension-bridge.js';
|
|
2
|
+
export function needsExtensionHint() {
|
|
3
|
+
return needsExtensionSummary();
|
|
4
|
+
}
|
|
3
5
|
export function classifyBridgeFailure(kind) {
|
|
4
6
|
return kind === 'extension-not-connected' ? 'needs-extension' : 'error';
|
|
5
7
|
}
|
|
@@ -37,7 +37,7 @@ export function parseDistManifest(raw) {
|
|
|
37
37
|
const tarballSha256 = obj.tarballSha256;
|
|
38
38
|
const zip = obj.zip;
|
|
39
39
|
const zipSha256 = obj.zipSha256;
|
|
40
|
-
if (typeof version !== 'string' || !/^\d
|
|
40
|
+
if (typeof version !== 'string' || !/^\d+(\.\d+){1,3}$/.test(version)) throw new Error('dist-manifest.version 缺失或非 1-4 段点分整数');
|
|
41
41
|
if (tarball !== DIST_ASSET_TARBALL) throw new Error(`dist-manifest.tarball 资产名漂移(期望 ${DIST_ASSET_TARBALL})`);
|
|
42
42
|
if (zip !== DIST_ASSET_STORE_ZIP) throw new Error(`dist-manifest.zip 资产名漂移(期望 ${DIST_ASSET_STORE_ZIP})`);
|
|
43
43
|
if (typeof tarballSha256 !== 'string' || !SHA256_RE.test(tarballSha256)) throw new Error('dist-manifest.tarballSha256 非 64-hex');
|