@danceiny/gotry 0.0.1-rc.15 → 0.0.1-rc.16
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 +10 -6
- package/README.zh-CN.md +10 -6
- package/bin/gotry-bootstrap.js +294 -5
- package/cordis.gotry-patch.yml +10 -0
- package/data/airline-airports.json +39 -0
- 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 +293 -0
- package/dist/capabilities/fact-log.js +41 -0
- package/dist/capabilities/flyai.js +1 -1
- package/dist/capabilities/hbcli.js +43 -1
- package/dist/capabilities/incident-log.js +1 -1
- package/dist/capabilities/opensky.js +1 -1
- package/dist/capabilities/resilience.js +90 -0
- 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 +2 -1
- package/dist/capabilities/session/extension-bridge.js +345 -0
- package/dist/capabilities/session/extension-channel.js +93 -0
- package/dist/capabilities/session/extract.js +1 -1
- package/dist/capabilities/session/health-watch.js +216 -0
- package/dist/capabilities/session/read-guard.js +1 -1
- package/dist/capabilities/session/transport.js +1 -1
- package/dist/capabilities/session/wizard.js +377 -0
- package/dist/capabilities/session-consent.js +1 -1
- package/dist/capabilities/session-login.js +77 -1
- package/dist/capabilities/session-search.js +69 -2
- package/dist/capabilities/weather.js +1 -1
- package/dist/scripts/action-cache-tests.js +1 -1
- 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/booking-saga-tests.js +1 -1
- package/dist/scripts/bootstrap-tests.js +39 -3
- package/dist/scripts/build-changelog.js +279 -0
- package/dist/scripts/changelog-tests.js +165 -0
- package/dist/scripts/companion-tests.js +1 -1
- package/dist/scripts/diff-test.js +1 -1
- package/dist/scripts/effect-tests.js +474 -0
- package/dist/scripts/engine-run.js +1 -1
- package/dist/scripts/engine-tests.js +1 -1
- package/dist/scripts/extension-tests.js +468 -0
- package/dist/scripts/fact-gate-tests.js +318 -0
- package/dist/scripts/flyai-tests.js +1 -1
- package/dist/scripts/hbcli-e2e-tests.js +180 -0
- package/dist/scripts/hbcli-tests.js +1 -1
- package/dist/scripts/health-watch-cli.js +51 -0
- 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/nightly-evidence-tests.js +67 -4
- package/dist/scripts/nightly-evidence.js +59 -3
- package/dist/scripts/nudge-digest.js +1 -1
- package/dist/scripts/onboarding-tests.js +218 -0
- package/dist/scripts/opensky-check.js +1 -1
- package/dist/scripts/opensky-tests.js +1 -1
- package/dist/scripts/price-drift-tests.js +441 -0
- package/dist/scripts/price-drift-watch.js +491 -0
- package/dist/scripts/probe-poi-tests.js +1 -1
- package/dist/scripts/product-metrics.js +1 -1
- package/dist/scripts/publish-preverify.js +1 -1
- 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 +1 -1
- 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.js +76 -1
- package/dist/scripts/state-cli-tests.js +1 -1
- package/dist/scripts/state-cli.js +1 -1
- 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 +1 -1
- package/dist/scripts/wizard-bootstrap.js +32 -0
- package/dist/scripts/z3-race-tests.js +1 -1
- package/dist/src/artifact-gate.js +310 -0
- package/dist/src/bookable-facts.js +349 -0
- package/dist/src/booking-saga.js +1 -1
- 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/i18n.js +1 -1
- package/dist/src/index.js +189 -46
- 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 +14 -7
- package/dist/src/slot-spec.js +1 -1
- package/dist/src/state-ledger.js +1 -1
- package/dist/src/time-anchor.js +1 -1
- 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 +26 -0
- package/extension/background.js +160 -0
- package/extension/content-bridge.js +41 -0
- package/extension/content-main.js +62 -0
- package/extension/manifest.json +32 -0
- package/package.json +6 -2
- package/ts/capabilities/fact-log.ts +54 -0
- package/ts/capabilities/hbcli.ts +46 -0
- package/ts/capabilities/session/benchmark.ts +4 -1
- package/ts/capabilities/session/extension-bridge.ts +354 -0
- package/ts/capabilities/session/extension-channel.ts +92 -0
- package/ts/capabilities/session/health-watch.ts +257 -0
- package/ts/capabilities/session/wizard.ts +282 -0
- package/ts/capabilities/session-login.ts +53 -2
- package/ts/capabilities/session-search.ts +68 -3
- package/ts/src/artifact-gate.ts +365 -0
- package/ts/src/bookable-facts.ts +557 -0
- package/ts/src/index.ts +102 -8
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'node:fs';
|
|
3
|
+
import { homedir, platform } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
const DEFAULT_SOURCE_DIR_CANDIDATES = [
|
|
6
|
+
join(process.cwd(), 'extension'),
|
|
7
|
+
join(process.cwd(), 'dist', 'extension'),
|
|
8
|
+
join(process.cwd(), 'ts', '..', 'extension')
|
|
9
|
+
];
|
|
10
|
+
function resolveExtensionSourceDir() {
|
|
11
|
+
for (const candidate of DEFAULT_SOURCE_DIR_CANDIDATES){
|
|
12
|
+
if (existsSync(join(candidate, 'manifest.json'))) return candidate;
|
|
13
|
+
}
|
|
14
|
+
return DEFAULT_SOURCE_DIR_CANDIDATES[0];
|
|
15
|
+
}
|
|
16
|
+
export function defaultExtensionDir() {
|
|
17
|
+
return join(homedir(), '.gotry', 'extension');
|
|
18
|
+
}
|
|
19
|
+
function detectPlatform(force) {
|
|
20
|
+
if (force) return force;
|
|
21
|
+
if (process.env.GOTRY_ONBOARDING_HEADLESS === '1') return 'headless';
|
|
22
|
+
const p = platform();
|
|
23
|
+
if (p === 'darwin' || p === 'linux' || p === 'win32') {
|
|
24
|
+
if (p !== 'darwin' && !process.env.DISPLAY) return 'headless';
|
|
25
|
+
return p;
|
|
26
|
+
}
|
|
27
|
+
return 'headless';
|
|
28
|
+
}
|
|
29
|
+
function ensureExtensionFiles(source, target, dryRun) {
|
|
30
|
+
if (dryRun) return {
|
|
31
|
+
step: 'ensure-extension-files',
|
|
32
|
+
status: 'skip',
|
|
33
|
+
summary: '[dry-run] skip 扩展落位'
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
if (!existsSync(source) || !existsSync(join(source, 'manifest.json'))) {
|
|
37
|
+
return {
|
|
38
|
+
step: 'ensure-extension-files',
|
|
39
|
+
status: 'fail',
|
|
40
|
+
summary: `源扩展目录缺失 manifest.json: ${source}(先跑 npx gotry setup 落位)`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
mkdirSync(target, {
|
|
44
|
+
recursive: true
|
|
45
|
+
});
|
|
46
|
+
const required = [
|
|
47
|
+
'manifest.json',
|
|
48
|
+
'background.js',
|
|
49
|
+
'content-main.js',
|
|
50
|
+
'content-bridge.js'
|
|
51
|
+
];
|
|
52
|
+
for (const f of required){
|
|
53
|
+
const src = join(source, f);
|
|
54
|
+
if (!existsSync(src)) return {
|
|
55
|
+
step: 'ensure-extension-files',
|
|
56
|
+
status: 'fail',
|
|
57
|
+
summary: `源扩展缺 ${f}(重新 npm install 或 gotry setup)`
|
|
58
|
+
};
|
|
59
|
+
copyFileSync(src, join(target, f));
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
step: 'ensure-extension-files',
|
|
63
|
+
status: 'ok',
|
|
64
|
+
summary: `扩展已落位 ${target}(manifest+3 文件)`
|
|
65
|
+
};
|
|
66
|
+
} catch (e) {
|
|
67
|
+
return {
|
|
68
|
+
step: 'ensure-extension-files',
|
|
69
|
+
status: 'fail',
|
|
70
|
+
summary: `扩展落位失败: ${e instanceof Error ? e.message.slice(0, 120) : String(e)}`
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function openChromeExtensions(sp, plat, dryRun) {
|
|
75
|
+
if (dryRun) return {
|
|
76
|
+
step: 'open-chrome-extensions',
|
|
77
|
+
status: 'skip',
|
|
78
|
+
summary: '[dry-run] skip 打开 chrome://extensions'
|
|
79
|
+
};
|
|
80
|
+
if (plat === 'headless') return {
|
|
81
|
+
step: 'open-chrome-extensions',
|
|
82
|
+
status: 'skip',
|
|
83
|
+
summary: 'headless 环境跳过 GUI;请在你桌面 Chrome 手动打开 chrome://extensions'
|
|
84
|
+
};
|
|
85
|
+
const cmd = (()=>{
|
|
86
|
+
if (plat === 'darwin') return {
|
|
87
|
+
bin: 'open',
|
|
88
|
+
args: [
|
|
89
|
+
'-a',
|
|
90
|
+
'Google Chrome',
|
|
91
|
+
'chrome://extensions'
|
|
92
|
+
]
|
|
93
|
+
};
|
|
94
|
+
if (plat === 'linux') return {
|
|
95
|
+
bin: 'xdg-open',
|
|
96
|
+
args: [
|
|
97
|
+
'chrome://extensions'
|
|
98
|
+
]
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
bin: 'cmd',
|
|
102
|
+
args: [
|
|
103
|
+
'/c',
|
|
104
|
+
'start',
|
|
105
|
+
'chrome',
|
|
106
|
+
'chrome://extensions'
|
|
107
|
+
]
|
|
108
|
+
};
|
|
109
|
+
})();
|
|
110
|
+
try {
|
|
111
|
+
const child = sp(cmd.bin, cmd.args, {
|
|
112
|
+
detached: true,
|
|
113
|
+
stdio: 'ignore'
|
|
114
|
+
});
|
|
115
|
+
child.unref?.();
|
|
116
|
+
return {
|
|
117
|
+
step: 'open-chrome-extensions',
|
|
118
|
+
status: 'ok',
|
|
119
|
+
summary: `已打开 chrome://extensions(${plat})`
|
|
120
|
+
};
|
|
121
|
+
} catch (e) {
|
|
122
|
+
return {
|
|
123
|
+
step: 'open-chrome-extensions',
|
|
124
|
+
status: 'fail',
|
|
125
|
+
summary: `自动打开失败: ${e instanceof Error ? e.message.slice(0, 80) : String(e)};请手开 chrome://extensions`
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function clipboardExtensionPath(sp, plat, path, dryRun) {
|
|
130
|
+
if (dryRun) return {
|
|
131
|
+
step: 'clipboard-extension-path',
|
|
132
|
+
status: 'skip',
|
|
133
|
+
summary: '[dry-run] skip 剪贴板复制'
|
|
134
|
+
};
|
|
135
|
+
if (plat === 'headless') return {
|
|
136
|
+
step: 'clipboard-extension-path',
|
|
137
|
+
status: 'skip',
|
|
138
|
+
summary: `headless 无剪贴板;扩展目录路径:${path}`
|
|
139
|
+
};
|
|
140
|
+
const cmd = (()=>{
|
|
141
|
+
if (plat === 'darwin') return {
|
|
142
|
+
bin: 'pbcopy',
|
|
143
|
+
args: []
|
|
144
|
+
};
|
|
145
|
+
if (plat === 'linux') return {
|
|
146
|
+
bin: 'xclip',
|
|
147
|
+
args: [
|
|
148
|
+
'-selection',
|
|
149
|
+
'clipboard'
|
|
150
|
+
]
|
|
151
|
+
};
|
|
152
|
+
return {
|
|
153
|
+
bin: 'clip',
|
|
154
|
+
args: []
|
|
155
|
+
};
|
|
156
|
+
})();
|
|
157
|
+
return new Promise((resolveStep)=>{
|
|
158
|
+
let child;
|
|
159
|
+
try {
|
|
160
|
+
child = sp(cmd.bin, cmd.args, {
|
|
161
|
+
stdio: [
|
|
162
|
+
'pipe',
|
|
163
|
+
'ignore',
|
|
164
|
+
'ignore'
|
|
165
|
+
]
|
|
166
|
+
});
|
|
167
|
+
} catch {
|
|
168
|
+
resolveStep({
|
|
169
|
+
step: 'clipboard-extension-path',
|
|
170
|
+
status: 'skip',
|
|
171
|
+
summary: `剪贴板工具不可用(${cmd.bin});扩展目录路径手动复制: ${path}`
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
let settled = false;
|
|
176
|
+
const settle = (s)=>{
|
|
177
|
+
if (settled) return;
|
|
178
|
+
settled = true;
|
|
179
|
+
resolveStep(s);
|
|
180
|
+
};
|
|
181
|
+
child.on('error', ()=>settle({
|
|
182
|
+
step: 'clipboard-extension-path',
|
|
183
|
+
status: 'skip',
|
|
184
|
+
summary: `剪贴板工具启动失败(${cmd.bin});扩展目录路径手动复制: ${path}`
|
|
185
|
+
}));
|
|
186
|
+
child.on('exit', (code)=>{
|
|
187
|
+
if (code === 0) settle({
|
|
188
|
+
step: 'clipboard-extension-path',
|
|
189
|
+
status: 'ok',
|
|
190
|
+
summary: `已复制扩展目录到剪贴板: ${path}`
|
|
191
|
+
});
|
|
192
|
+
else settle({
|
|
193
|
+
step: 'clipboard-extension-path',
|
|
194
|
+
status: 'fail',
|
|
195
|
+
summary: `剪贴板工具退出码 ${code}(${cmd.bin});扩展目录路径手动复制: ${path}`
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
try {
|
|
199
|
+
child.stdin?.write(path);
|
|
200
|
+
child.stdin?.end();
|
|
201
|
+
} catch {
|
|
202
|
+
settle({
|
|
203
|
+
step: 'clipboard-extension-path',
|
|
204
|
+
status: 'skip',
|
|
205
|
+
summary: `剪贴板工具 stdin 不可写(${cmd.bin});扩展目录路径手动复制: ${path}`
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function panelGuideTerminal(plat, extensionDir, watchMs) {
|
|
211
|
+
const lines = [
|
|
212
|
+
'┌─ GoTry 扩展安装 ─────────────────────────────────┐',
|
|
213
|
+
'│ 共 3 步,约 30 秒,装完零弹窗 │',
|
|
214
|
+
'│ │',
|
|
215
|
+
'│ ① 在 Chrome 右上角开启「开发者模式」 │',
|
|
216
|
+
`│ ② 点「加载已解压的扩展程序」选这个目录(已复制): │`,
|
|
217
|
+
`│ ${extensionDir}`,
|
|
218
|
+
'│ ③ 弹窗「添加扩展」点「添加」 │',
|
|
219
|
+
`│ │`,
|
|
220
|
+
`│ 后台探活中(最长 ${Math.round(watchMs / 1000)}s)… │`,
|
|
221
|
+
'└───────────────────────────────────────────────────┘'
|
|
222
|
+
];
|
|
223
|
+
if (plat === 'headless') {
|
|
224
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
225
|
+
return {
|
|
226
|
+
step: 'panel-guide',
|
|
227
|
+
status: 'skip',
|
|
228
|
+
summary: 'headless 终端面板已打印'
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
232
|
+
return {
|
|
233
|
+
step: 'panel-guide',
|
|
234
|
+
status: 'skip',
|
|
235
|
+
summary: '终端面板已打印(GUI 面板降级)'
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function panelGuideGui(sp, plat, extensionDir, dryRun) {
|
|
239
|
+
if (dryRun) return {
|
|
240
|
+
step: 'panel-guide',
|
|
241
|
+
status: 'skip',
|
|
242
|
+
summary: '[dry-run] skip GUI 面板'
|
|
243
|
+
};
|
|
244
|
+
if (plat === 'headless') return panelGuideTerminal(plat, extensionDir, 120_000);
|
|
245
|
+
const message = `在 Chrome 右上角开启「开发者模式」 → 点「加载已解压的扩展程序」 → 选择(已复制):${extensionDir} → 弹窗点「添加扩展」`;
|
|
246
|
+
try {
|
|
247
|
+
if (plat === 'darwin') {
|
|
248
|
+
const script = `display dialog "${message.replace(/"/g, '\\"')}" with title "GoTry 扩展安装" buttons {"我装好了"} default button 1`;
|
|
249
|
+
sp('osascript', [
|
|
250
|
+
'-e',
|
|
251
|
+
script
|
|
252
|
+
], {
|
|
253
|
+
stdio: 'ignore',
|
|
254
|
+
detached: true
|
|
255
|
+
}).unref?.();
|
|
256
|
+
return {
|
|
257
|
+
step: 'panel-guide',
|
|
258
|
+
status: 'ok',
|
|
259
|
+
summary: '已弹 macOS Cocoa 引导面板'
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (plat === 'linux') {
|
|
263
|
+
const child = sp('zenity', [
|
|
264
|
+
'--info',
|
|
265
|
+
'--title=GoTry 扩展安装',
|
|
266
|
+
`--text=${message}`
|
|
267
|
+
], {
|
|
268
|
+
stdio: 'ignore',
|
|
269
|
+
detached: true
|
|
270
|
+
});
|
|
271
|
+
child.unref?.();
|
|
272
|
+
child.on('error', ()=>{
|
|
273
|
+
process.stdout.write(`(zenity 不可用,走终端面板)扩展目录: ${extensionDir}\n`);
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
step: 'panel-guide',
|
|
277
|
+
status: 'ok',
|
|
278
|
+
summary: '已弹 zenity 引导面板'
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
return panelGuideTerminal(plat, extensionDir, 120_000);
|
|
282
|
+
} catch (e) {
|
|
283
|
+
return panelGuideTerminal(plat, extensionDir, 120_000);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async function watchExtensionReady(dryRun, sourceDir, extensionDir) {
|
|
287
|
+
if (dryRun) return {
|
|
288
|
+
step: 'watch-extension-ready',
|
|
289
|
+
status: 'skip',
|
|
290
|
+
summary: '[dry-run] skip health-watch'
|
|
291
|
+
};
|
|
292
|
+
if (!existsSync(extensionDir) || !existsSync(join(extensionDir, 'manifest.json'))) {
|
|
293
|
+
return {
|
|
294
|
+
step: 'watch-extension-ready',
|
|
295
|
+
status: 'fail',
|
|
296
|
+
summary: `落位缺失 manifest.json:${extensionDir}`
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const srcStat = statSync(sourceDir);
|
|
300
|
+
const dstStat = statSync(extensionDir);
|
|
301
|
+
if (srcStat.mtimeMs > dstStat.mtimeMs) {
|
|
302
|
+
return {
|
|
303
|
+
step: 'watch-extension-ready',
|
|
304
|
+
status: 'fail',
|
|
305
|
+
summary: `扩展文件可能过期(源比落位新);重跑 npx gotry setup`
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
const manifest = JSON.parse(await (await import('node:fs/promises')).readFile(join(extensionDir, 'manifest.json'), 'utf8'));
|
|
309
|
+
if (typeof manifest.key !== 'string' || manifest.key.length < 100) {
|
|
310
|
+
return {
|
|
311
|
+
step: 'watch-extension-ready',
|
|
312
|
+
status: 'fail',
|
|
313
|
+
summary: 'manifest.key 缺失或过短,扩展 ID 不能稳定'
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
step: 'watch-extension-ready',
|
|
318
|
+
status: 'ok',
|
|
319
|
+
summary: '扩展文件就绪(manifest.key 校验通过,后台轮询就绪态由 health-watch 接管)'
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
export async function runOnboardingWizard(opts = {}) {
|
|
323
|
+
const sp = opts.spawn ?? spawn;
|
|
324
|
+
const dryRun = opts.dryRun ?? false;
|
|
325
|
+
const plat = detectPlatform(opts.forcePlatform);
|
|
326
|
+
const extensionDir = opts.extensionDir ?? defaultExtensionDir();
|
|
327
|
+
const sourceDir = opts.sourceDir ?? resolveExtensionSourceDir();
|
|
328
|
+
const steps = [];
|
|
329
|
+
steps.push(ensureExtensionFiles(sourceDir, extensionDir, dryRun));
|
|
330
|
+
steps.push(openChromeExtensions(sp, plat, dryRun));
|
|
331
|
+
if (plat !== 'headless') {
|
|
332
|
+
steps.push(await clipboardExtensionPath(sp, plat, extensionDir, dryRun));
|
|
333
|
+
steps.push(panelGuideGui(sp, plat, extensionDir, dryRun));
|
|
334
|
+
} else {
|
|
335
|
+
steps.push({
|
|
336
|
+
step: 'clipboard-extension-path',
|
|
337
|
+
status: 'skip',
|
|
338
|
+
summary: 'headless 跳过剪贴板'
|
|
339
|
+
});
|
|
340
|
+
steps.push(panelGuideTerminal(plat, extensionDir, 120_000));
|
|
341
|
+
}
|
|
342
|
+
steps.push(await watchExtensionReady(dryRun, sourceDir, extensionDir));
|
|
343
|
+
const ok = steps.every((s)=>s.status !== 'fail');
|
|
344
|
+
return {
|
|
345
|
+
ok,
|
|
346
|
+
steps,
|
|
347
|
+
platform: plat,
|
|
348
|
+
extensionDir
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
export function printWizardResult(result, destination = process.stdout) {
|
|
352
|
+
const tag = (s)=>{
|
|
353
|
+
if (s.status === 'ok') return '✅';
|
|
354
|
+
if (s.status === 'skip') return '⏭ ';
|
|
355
|
+
return '❌';
|
|
356
|
+
};
|
|
357
|
+
for (const s of result.steps){
|
|
358
|
+
destination.write(`${tag(s)} ${s.step}: ${s.summary}\n`);
|
|
359
|
+
}
|
|
360
|
+
destination.write(`platform=${result.platform}; extensionDir=${result.extensionDir}; overall=${result.ok ? 'ok' : 'has-failures'}\n`);
|
|
361
|
+
}
|
|
362
|
+
export function describeExtensionDir(extensionDir) {
|
|
363
|
+
if (!existsSync(extensionDir)) return {
|
|
364
|
+
exists: false,
|
|
365
|
+
fileCount: 0,
|
|
366
|
+
hasManifest: false
|
|
367
|
+
};
|
|
368
|
+
const files = readdirSync(extensionDir);
|
|
369
|
+
return {
|
|
370
|
+
exists: true,
|
|
371
|
+
fileCount: files.length,
|
|
372
|
+
hasManifest: files.includes('manifest.json')
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
//# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session/wizard.ts
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { openSession } from './session/transport.js';
|
|
2
|
+
import { extensionCookieNames, extensionOpenLogin, classifyBridgeFailure, NEEDS_EXTENSION_HINT } from './session/extension-channel.js';
|
|
3
|
+
import { resolveTransportMode } from './session-search.js';
|
|
2
4
|
export const LOGIN_TARGETS = {
|
|
3
5
|
'ctrip-flight': {
|
|
4
6
|
domain: 'ctrip.com',
|
|
@@ -47,6 +49,9 @@ export async function sessionLogin(q = {}) {
|
|
|
47
49
|
if (!target) {
|
|
48
50
|
return err(site, 'error', `未知站点 ${site}(可选 ${Object.keys(LOGIN_TARGETS).join('/')})`, started, ts);
|
|
49
51
|
}
|
|
52
|
+
if (resolveTransportMode() === 'extension') {
|
|
53
|
+
return sessionLoginViaExtension(site, target, q, started, ts);
|
|
54
|
+
}
|
|
50
55
|
const t = await openSession({
|
|
51
56
|
mode: 'cdp',
|
|
52
57
|
guard: false
|
|
@@ -114,6 +119,77 @@ export async function sessionLogin(q = {}) {
|
|
|
114
119
|
return err(site, 'error', e instanceof Error ? e.message : String(e), started, ts);
|
|
115
120
|
}
|
|
116
121
|
}
|
|
122
|
+
async function sessionLoginViaExtension(site, target, q, started, ts) {
|
|
123
|
+
const evidenceTag = `[会话:${site}-login@${ts}]`;
|
|
124
|
+
const quick = (verdict, error)=>({
|
|
125
|
+
ok: false,
|
|
126
|
+
via: 'session-login-error',
|
|
127
|
+
evidence: `[会话:login-error@${ts}] ${error.slice(0, 200)}`,
|
|
128
|
+
latencyMs: Date.now() - started,
|
|
129
|
+
verdict,
|
|
130
|
+
site,
|
|
131
|
+
error: verdict === 'needs-extension' ? `${error};${NEEDS_EXTENSION_HINT}`.slice(0, 400) : error.slice(0, 400)
|
|
132
|
+
});
|
|
133
|
+
const pre = await extensionCookieNames({
|
|
134
|
+
site,
|
|
135
|
+
domain: target.domain,
|
|
136
|
+
ticketNames: target.names,
|
|
137
|
+
timeoutMs: 8_000
|
|
138
|
+
});
|
|
139
|
+
if (!pre.ok) return quick(classifyBridgeFailure(pre.kind) === 'needs-extension' ? 'needs-extension' : 'error', pre.summary);
|
|
140
|
+
if (pre.tickets.length > 0) {
|
|
141
|
+
return {
|
|
142
|
+
ok: true,
|
|
143
|
+
via: 'session-login',
|
|
144
|
+
latencyMs: Date.now() - started,
|
|
145
|
+
verdict: 'logged-in',
|
|
146
|
+
site,
|
|
147
|
+
tickets: pre.tickets,
|
|
148
|
+
evidence: `${evidenceTag} 自动检测:票据 cookie 已在(先前登录已生效)——[${pre.tickets.join(', ')}](只读名字,0 网页交互,零弹窗)`
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const opened = await extensionOpenLogin({
|
|
152
|
+
site,
|
|
153
|
+
url: target.entryUrl,
|
|
154
|
+
timeoutMs: 15_000
|
|
155
|
+
});
|
|
156
|
+
if (!opened.ok) return quick(classifyBridgeFailure(opened.kind) === 'needs-extension' ? 'needs-extension' : 'error', opened.summary);
|
|
157
|
+
const waitMs = Math.min(Math.max(q.waitMs ?? 90_000, 0), 300_000);
|
|
158
|
+
const pollMs = Math.min(Math.max(q.pollMs ?? 3_000, 500), 10_000);
|
|
159
|
+
const deadline = Date.now() + waitMs;
|
|
160
|
+
let tickets = [];
|
|
161
|
+
while(Date.now() < deadline){
|
|
162
|
+
await new Promise((resolve)=>setTimeout(resolve, pollMs));
|
|
163
|
+
const poll = await extensionCookieNames({
|
|
164
|
+
site,
|
|
165
|
+
domain: target.domain,
|
|
166
|
+
ticketNames: target.names,
|
|
167
|
+
timeoutMs: 8_000
|
|
168
|
+
});
|
|
169
|
+
if (poll.ok) tickets = poll.tickets;
|
|
170
|
+
if (tickets.length > 0) break;
|
|
171
|
+
}
|
|
172
|
+
if (tickets.length > 0) {
|
|
173
|
+
return {
|
|
174
|
+
ok: true,
|
|
175
|
+
via: 'session-login',
|
|
176
|
+
latencyMs: Date.now() - started,
|
|
177
|
+
verdict: 'logged-in',
|
|
178
|
+
site,
|
|
179
|
+
tickets,
|
|
180
|
+
evidence: `${evidenceTag} 票据 cookie 已检出 [${tickets.join(', ')}](只读名字;登录在你自己的浏览器里完成,gotry 全程未接触任何密码/验证码/cookie 值)`
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
ok: true,
|
|
185
|
+
via: 'session-login',
|
|
186
|
+
latencyMs: Date.now() - started,
|
|
187
|
+
verdict: 'pending',
|
|
188
|
+
site,
|
|
189
|
+
tickets: [],
|
|
190
|
+
evidence: `${evidenceTag} 登录入口已在你的 Chrome 置前打开(${target.label});在标签页里正常登录完成后再说一声「继续查」即可——gotry 只检查"是否已登录",永不收集你的账号信息`
|
|
191
|
+
};
|
|
192
|
+
}
|
|
117
193
|
|
|
118
194
|
|
|
119
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/session-login.ts
|
|
195
|
+
//# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session-login.ts
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { openSession } from './session/transport.js';
|
|
2
|
+
import { extensionCookieNames, extensionSearchJob, classifyBridgeFailure, NEEDS_EXTENSION_HINT } from './session/extension-channel.js';
|
|
3
|
+
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
2
5
|
import { buildEntryUrl, NETWORK_HINTS, parseBatchSearch, LOGIN_COOKIE_NAMES, SITE_DOMAIN } from './session/adapters/ctrip-flight.js';
|
|
3
6
|
const MIN_INTERVAL_MS = 30_000;
|
|
4
7
|
const lastCallAt = new Map();
|
|
@@ -9,6 +12,24 @@ const CHALLENGE_RE = /验证|滑块|captcha|verify/i;
|
|
|
9
12
|
export function classifyTransportFailure(summary, cdpMode) {
|
|
10
13
|
return cdpMode && /日常 Chrome 未开调试端口|cdp attach 失败/.test(summary) ? 'needs-attach' : 'error';
|
|
11
14
|
}
|
|
15
|
+
export function resolveTransportMode(profileDir) {
|
|
16
|
+
if (profileDir !== undefined) return 'persistent';
|
|
17
|
+
return (process.env.GOTRY_SESSION_TRANSPORT ?? '').trim().toLowerCase() === 'cdp' ? 'cdp' : 'extension';
|
|
18
|
+
}
|
|
19
|
+
export function appendExtensionAudit(auditPath, entry) {
|
|
20
|
+
if (!auditPath) return;
|
|
21
|
+
try {
|
|
22
|
+
const record = {
|
|
23
|
+
ts: new Date().toISOString(),
|
|
24
|
+
...entry,
|
|
25
|
+
url: entry.url.slice(0, 400)
|
|
26
|
+
};
|
|
27
|
+
mkdirSync(dirname(auditPath), {
|
|
28
|
+
recursive: true
|
|
29
|
+
});
|
|
30
|
+
appendFileSync(auditPath, JSON.stringify(record) + '\n');
|
|
31
|
+
} catch {}
|
|
32
|
+
}
|
|
12
33
|
export async function sessionFlightSearch(q) {
|
|
13
34
|
const started = Date.now();
|
|
14
35
|
const ts = new Date().toISOString();
|
|
@@ -30,11 +51,57 @@ export async function sessionFlightSearch(q) {
|
|
|
30
51
|
if (!entry.ok || !entry.url) {
|
|
31
52
|
return err('error', `unresolved entry: ${(entry.unresolved ?? []).join('/')} 不在城市码表`);
|
|
32
53
|
}
|
|
54
|
+
const mode = resolveTransportMode(q.profileDir);
|
|
55
|
+
if (mode === 'extension') {
|
|
56
|
+
const login = await extensionCookieNames({
|
|
57
|
+
site,
|
|
58
|
+
domain: SITE_DOMAIN.replace(/^\./, ''),
|
|
59
|
+
ticketNames: LOGIN_COOKIE_NAMES
|
|
60
|
+
});
|
|
61
|
+
if (!login.ok) {
|
|
62
|
+
const verdict = classifyBridgeFailure(login.kind);
|
|
63
|
+
return err(verdict, verdict === 'needs-extension' ? `${login.summary};${NEEDS_EXTENSION_HINT}` : login.summary);
|
|
64
|
+
}
|
|
65
|
+
if (login.tickets.length === 0 && !q.allowAnonymous) {
|
|
66
|
+
return err('needs-login', '未检出你本人登录态——调用 gotry_session_login 为用户打开携程登录入口(登录在携程官网完成;gotry 永不经手密码/验证码/cookie 值)');
|
|
67
|
+
}
|
|
68
|
+
const r = await extensionSearchJob({
|
|
69
|
+
site,
|
|
70
|
+
url: entry.url,
|
|
71
|
+
timeoutMs: q.timeoutMs
|
|
72
|
+
});
|
|
73
|
+
appendExtensionAudit(q.auditPath, {
|
|
74
|
+
kind: 'extension-session-job',
|
|
75
|
+
site,
|
|
76
|
+
url: entry.url,
|
|
77
|
+
jobId: 'search',
|
|
78
|
+
result: r.ok ? r.timedOut ? 'timeout' : `body ${r.body.length}B title="${r.title.slice(0, 60)}"` : `${r.kind}:${r.summary.slice(0, 120)}`
|
|
79
|
+
});
|
|
80
|
+
if (!r.ok) {
|
|
81
|
+
const verdict = classifyBridgeFailure(r.kind);
|
|
82
|
+
return err(verdict, verdict === 'needs-extension' ? `${r.summary};${NEEDS_EXTENSION_HINT}` : r.summary);
|
|
83
|
+
}
|
|
84
|
+
const title = r.title;
|
|
85
|
+
const head = r.body.slice(0, 5000);
|
|
86
|
+
if (CHALLENGE_RE.test(title + head)) {
|
|
87
|
+
return err('challenged', `风控/验证码命中(title=${title.slice(0, 60)});按红线不重试不绕过,交还用户`);
|
|
88
|
+
}
|
|
89
|
+
const options = parseBatchSearch(r.body);
|
|
90
|
+
const verdict = options.length > 0 ? 'hit' : 'miss';
|
|
91
|
+
return {
|
|
92
|
+
ok: true,
|
|
93
|
+
via: 'session-ctrip-flight',
|
|
94
|
+
evidence: `[会话:${site}@${ts}] ${options.length} options;transport=extension(被动嗅探,零系统弹窗;扩展零写行为=物理只读)${q.allowAnonymous ? ';anonymous=自检态' : ''}`,
|
|
95
|
+
latencyMs: Date.now() - started,
|
|
96
|
+
verdict,
|
|
97
|
+
options
|
|
98
|
+
};
|
|
99
|
+
}
|
|
33
100
|
const t = await openSession({
|
|
34
101
|
profileDir: q.profileDir,
|
|
35
102
|
headless: q.headless,
|
|
36
103
|
auditPath: q.auditPath,
|
|
37
|
-
mode:
|
|
104
|
+
mode: mode === 'persistent' ? 'persistent' : 'cdp',
|
|
38
105
|
newPage: true
|
|
39
106
|
});
|
|
40
107
|
if (!t.ok) {
|
|
@@ -93,4 +160,4 @@ export async function sessionFlightSearch(q) {
|
|
|
93
160
|
}
|
|
94
161
|
|
|
95
162
|
|
|
96
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/session-search.ts
|
|
163
|
+
//# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session-search.ts
|
|
@@ -104,4 +104,4 @@ console.log(`\nACTION-CACHE: ${pass} pass, ${fail} fail`);
|
|
|
104
104
|
process.exit(fail > 0 ? 1 : 0);
|
|
105
105
|
|
|
106
106
|
|
|
107
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/scripts/action-cache-tests.ts
|
|
107
|
+
//# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/action-cache-tests.ts
|
|
@@ -51,4 +51,4 @@ import { videoSubtitle, githubSearch } from '../capabilities/agent-reach-deep.js
|
|
|
51
51
|
}console.log('\nAGENT-REACH DEEP TESTS: 4/4 OK(yt-dlp/gh 可选工具,不装则降级)');
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/scripts/agent-reach-deep-tests.ts
|
|
54
|
+
//# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/agent-reach-deep-tests.ts
|
|
@@ -35,4 +35,4 @@ import { readUrl } from '../capabilities/agent-reach.js';
|
|
|
35
35
|
}console.log('\nAGENT-REACH TESTS: 3/3 OK(readUrl 薄壳;上游真读见 wrapper 套)');
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/scripts/agent-reach-tests.ts
|
|
38
|
+
//# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/agent-reach-tests.ts
|
|
@@ -80,4 +80,4 @@ import { reach, reachStatus } from '../capabilities/agent-reach.js';
|
|
|
80
80
|
}console.log('\nAGENT-REACH WRAPPER TESTS: 7/7 OK(反射桥 + doctor 透传 + 自描述错误)');
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/scripts/agent-reach-wrapper-tests.ts
|
|
83
|
+
//# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/agent-reach-wrapper-tests.ts
|
|
@@ -184,4 +184,4 @@ console.log(`\nBOOKING SAGA TESTS: ${pass} ok, ${fail} fail`);
|
|
|
184
184
|
if (fail > 0) process.exit(1);
|
|
185
185
|
|
|
186
186
|
|
|
187
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/scripts/booking-saga-tests.ts
|
|
187
|
+
//# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/booking-saga-tests.ts
|