@danceiny/gotry 0.0.1-rc.14 → 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.
Files changed (142) hide show
  1. package/README.md +26 -20
  2. package/README.zh-CN.md +27 -21
  3. package/bin/gotry-bootstrap.js +331 -3
  4. package/cordis.gotry-patch.yml +10 -0
  5. package/data/airline-airports.json +39 -0
  6. package/dist/capabilities/agent-reach-deep.js +1 -1
  7. package/dist/capabilities/agent-reach.js +1 -1
  8. package/dist/capabilities/anything.js +1 -1
  9. package/dist/capabilities/artifacts.js +217 -0
  10. package/dist/capabilities/effect.js +293 -0
  11. package/dist/capabilities/fact-log.js +41 -0
  12. package/dist/capabilities/flyai.js +1 -1
  13. package/dist/capabilities/hbcli.js +43 -1
  14. package/dist/capabilities/incident-log.js +1 -1
  15. package/dist/capabilities/opensky.js +1 -1
  16. package/dist/capabilities/resilience.js +90 -0
  17. package/dist/capabilities/session/action-cache.js +1 -1
  18. package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
  19. package/dist/capabilities/session/adapters/meituan-local.js +1 -1
  20. package/dist/capabilities/session/benchmark.js +2 -1
  21. package/dist/capabilities/session/extension-bridge.js +345 -0
  22. package/dist/capabilities/session/extension-channel.js +93 -0
  23. package/dist/capabilities/session/extract.js +1 -1
  24. package/dist/capabilities/session/health-watch.js +216 -0
  25. package/dist/capabilities/session/read-guard.js +1 -1
  26. package/dist/capabilities/session/transport.js +1 -1
  27. package/dist/capabilities/session/wizard.js +377 -0
  28. package/dist/capabilities/session-consent.js +1 -1
  29. package/dist/capabilities/session-login.js +77 -1
  30. package/dist/capabilities/session-search.js +69 -2
  31. package/dist/capabilities/weather.js +1 -1
  32. package/dist/scripts/action-cache-tests.js +1 -1
  33. package/dist/scripts/agent-reach-deep-tests.js +1 -1
  34. package/dist/scripts/agent-reach-tests.js +1 -1
  35. package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
  36. package/dist/scripts/anything-tests.js +1 -1
  37. package/dist/scripts/async-collect.js +1 -1
  38. package/dist/scripts/booking-saga-tests.js +187 -0
  39. package/dist/scripts/bootstrap-tests.js +49 -3
  40. package/dist/scripts/build-changelog.js +279 -0
  41. package/dist/scripts/changelog-tests.js +165 -0
  42. package/dist/scripts/companion-tests.js +1 -1
  43. package/dist/scripts/diff-test.js +1 -1
  44. package/dist/scripts/effect-tests.js +474 -0
  45. package/dist/scripts/engine-run.js +1 -1
  46. package/dist/scripts/engine-tests.js +1 -1
  47. package/dist/scripts/extension-tests.js +468 -0
  48. package/dist/scripts/fact-gate-tests.js +318 -0
  49. package/dist/scripts/flyai-tests.js +1 -1
  50. package/dist/scripts/hbcli-e2e-tests.js +180 -0
  51. package/dist/scripts/hbcli-tests.js +1 -1
  52. package/dist/scripts/health-watch-cli.js +51 -0
  53. package/dist/scripts/i18n-tests.js +1 -1
  54. package/dist/scripts/incident-tests.js +1 -1
  55. package/dist/scripts/journey-tests.js +1 -1
  56. package/dist/scripts/ledger-tests.js +1 -1
  57. package/dist/scripts/ledger-workflow-crash.js +1 -1
  58. package/dist/scripts/memory-capture-tests.js +1 -1
  59. package/dist/scripts/memory-decay-tests.js +1 -1
  60. package/dist/scripts/memory-metrics.js +1 -1
  61. package/dist/scripts/memory-value-report.js +1 -1
  62. package/dist/scripts/nightly-evidence-tests.js +186 -0
  63. package/dist/scripts/nightly-evidence.js +289 -0
  64. package/dist/scripts/nudge-digest.js +1 -1
  65. package/dist/scripts/onboarding-tests.js +218 -0
  66. package/dist/scripts/opensky-check.js +1 -1
  67. package/dist/scripts/opensky-tests.js +1 -1
  68. package/dist/scripts/price-drift-tests.js +441 -0
  69. package/dist/scripts/price-drift-watch.js +491 -0
  70. package/dist/scripts/probe-poi-tests.js +1 -1
  71. package/dist/scripts/product-metrics.js +1 -1
  72. package/dist/scripts/publish-preverify.js +1 -1
  73. package/dist/scripts/realtime-pricing-tests.js +1 -1
  74. package/dist/scripts/replay-async.js +1 -1
  75. package/dist/scripts/replay-real.js +1 -1
  76. package/dist/scripts/replay.js +1 -1
  77. package/dist/scripts/session-attach-diagnose.js +1 -1
  78. package/dist/scripts/session-attach-poc.js +1 -1
  79. package/dist/scripts/session-benchmark.js +1 -1
  80. package/dist/scripts/session-extract-tests.js +1 -1
  81. package/dist/scripts/session-login.js +1 -1
  82. package/dist/scripts/session-tests.js +1 -1
  83. package/dist/scripts/skeleton-check.js +1 -1
  84. package/dist/scripts/skeleton-integration-test.js +1 -1
  85. package/dist/scripts/skills-contract-tests.js +1 -1
  86. package/dist/scripts/smoke.js +159 -1
  87. package/dist/scripts/state-cli-tests.js +1 -1
  88. package/dist/scripts/state-cli.js +1 -1
  89. package/dist/scripts/time-eval-tests.js +1 -1
  90. package/dist/scripts/travel-timeline-tests.js +1 -1
  91. package/dist/scripts/unified-tests.js +1 -1
  92. package/dist/scripts/weather-tests.js +1 -1
  93. package/dist/scripts/wizard-bootstrap.js +32 -0
  94. package/dist/scripts/z3-race-tests.js +1 -1
  95. package/dist/src/artifact-gate.js +310 -0
  96. package/dist/src/bookable-facts.js +349 -0
  97. package/dist/src/booking-saga.js +153 -0
  98. package/dist/src/bridge.js +1 -1
  99. package/dist/src/companions.js +1 -1
  100. package/dist/src/contracts.js +1 -1
  101. package/dist/src/dsh-llm.js +31 -7
  102. package/dist/src/engine.js +1 -1
  103. package/dist/src/i18n.js +1 -1
  104. package/dist/src/index.js +322 -46
  105. package/dist/src/journey.js +1 -1
  106. package/dist/src/loop.js +1 -1
  107. package/dist/src/memory-capture.js +1 -1
  108. package/dist/src/memory-decay.js +1 -1
  109. package/dist/src/memory-utility.js +1 -1
  110. package/dist/src/mock-llm.js +1 -1
  111. package/dist/src/model.js +1 -1
  112. package/dist/src/realtime-pricing.js +14 -7
  113. package/dist/src/slot-spec.js +1 -1
  114. package/dist/src/state-ledger.js +1 -1
  115. package/dist/src/time-anchor.js +1 -1
  116. package/dist/src/tool-packet.js +1 -1
  117. package/dist/src/travel-slots.js +1 -1
  118. package/dist/src/travel-timeline.js +1 -1
  119. package/dist/src/unified.js +1 -1
  120. package/dist/src/wish-pool.js +1 -1
  121. package/dist/src/z3-shared.js +1 -1
  122. package/extension/README.md +26 -0
  123. package/extension/background.js +160 -0
  124. package/extension/content-bridge.js +41 -0
  125. package/extension/content-main.js +62 -0
  126. package/extension/manifest.json +32 -0
  127. package/package.json +8 -2
  128. package/ts/capabilities/artifacts.ts +235 -0
  129. package/ts/capabilities/fact-log.ts +54 -0
  130. package/ts/capabilities/hbcli.ts +46 -0
  131. package/ts/capabilities/session/benchmark.ts +4 -1
  132. package/ts/capabilities/session/extension-bridge.ts +354 -0
  133. package/ts/capabilities/session/extension-channel.ts +92 -0
  134. package/ts/capabilities/session/health-watch.ts +257 -0
  135. package/ts/capabilities/session/wizard.ts +282 -0
  136. package/ts/capabilities/session-login.ts +53 -2
  137. package/ts/capabilities/session-search.ts +68 -3
  138. package/ts/src/artifact-gate.ts +365 -0
  139. package/ts/src/bookable-facts.ts +557 -0
  140. package/ts/src/booking-saga.ts +133 -0
  141. package/ts/src/dsh-llm.ts +40 -3
  142. package/ts/src/index.ts +230 -28
@@ -0,0 +1,90 @@
1
+ export function backoffDelayMs(policy, failedAttempt) {
2
+ const raw = policy.baseDelayMs * 2 ** (Math.max(1, failedAttempt) - 1);
3
+ return Math.min(raw, policy.maxDelayMs);
4
+ }
5
+ const defaultSleep = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
6
+ export async function withRetry(fn, policy, onBackoff) {
7
+ const sleep = policy.sleep ?? defaultSleep;
8
+ const maxAttempts = Math.max(1, policy.maxAttempts);
9
+ let attempts = 0;
10
+ let backoffMs = 0;
11
+ for(;;){
12
+ attempts += 1;
13
+ let result = null;
14
+ let error = null;
15
+ try {
16
+ result = await fn();
17
+ } catch (e) {
18
+ error = e;
19
+ }
20
+ const retryable = policy.isRetryable?.(result, error) ?? (error != null || result?.ok === false);
21
+ if (attempts < maxAttempts && retryable) {
22
+ const delay = backoffDelayMs(policy, attempts);
23
+ onBackoff?.(attempts, delay);
24
+ backoffMs += delay;
25
+ await sleep(delay);
26
+ continue;
27
+ }
28
+ return {
29
+ result,
30
+ error,
31
+ attempts,
32
+ backoffMs
33
+ };
34
+ }
35
+ }
36
+ export class CircuitBreaker {
37
+ o;
38
+ consecutiveFailures = 0;
39
+ openedAt = 0;
40
+ probeInFlight = false;
41
+ constructor(o){
42
+ this.o = o;
43
+ }
44
+ threshold() {
45
+ return Math.max(1, this.o.failureThreshold);
46
+ }
47
+ now() {
48
+ return (this.o.now ?? Date.now)();
49
+ }
50
+ state() {
51
+ if (this.openedAt > 0 && this.now() - this.openedAt >= this.o.openMs) return 'half-open';
52
+ if (this.openedAt > 0) return 'open';
53
+ return 'closed';
54
+ }
55
+ canAttempt() {
56
+ const s = this.state();
57
+ if (s === 'closed') return {
58
+ allowed: true,
59
+ state: s
60
+ };
61
+ if (s === 'open') return {
62
+ allowed: false,
63
+ state: s
64
+ };
65
+ if (this.probeInFlight) return {
66
+ allowed: false,
67
+ state: s
68
+ };
69
+ this.probeInFlight = true;
70
+ return {
71
+ allowed: true,
72
+ state: s
73
+ };
74
+ }
75
+ onSuccess() {
76
+ this.consecutiveFailures = 0;
77
+ this.openedAt = 0;
78
+ this.probeInFlight = false;
79
+ }
80
+ onFailure() {
81
+ this.probeInFlight = false;
82
+ this.consecutiveFailures += 1;
83
+ if (this.openedAt > 0 || this.consecutiveFailures >= this.threshold()) {
84
+ this.openedAt = this.now();
85
+ }
86
+ }
87
+ }
88
+
89
+
90
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/resilience.ts
@@ -117,4 +117,4 @@ export function actionCachePath(stateRoot) {
117
117
  }
118
118
 
119
119
 
120
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/session/action-cache.ts
120
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session/action-cache.ts
@@ -80,4 +80,4 @@ export function parseBatchSearch(body) {
80
80
  }
81
81
 
82
82
 
83
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/session/adapters/ctrip-flight.ts
83
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session/adapters/ctrip-flight.ts
@@ -70,4 +70,4 @@ export function extractListings(entries, opts = {}) {
70
70
  }
71
71
 
72
72
 
73
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/session/adapters/meituan-local.ts
73
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session/adapters/meituan-local.ts
@@ -228,6 +228,7 @@ export function evaluateDoubleSource(input) {
228
228
  }
229
229
  if (!session) return baseEvaluation('source_unavailable', 'no_spend_stop');
230
230
  if (session.verdict === 'needs-attach') return baseEvaluation('waiting_attach', 'no_spend_waiting_user');
231
+ if (session.verdict === 'needs-extension') return baseEvaluation('waiting_extension', 'no_spend_waiting_user');
231
232
  if (session.verdict === 'needs-login') return baseEvaluation('waiting_login', 'no_spend_waiting_user');
232
233
  if (!official || official.verdict !== 'hit' || session.verdict !== 'hit') {
233
234
  return baseEvaluation('source_unavailable', 'no_spend_stop');
@@ -249,4 +250,4 @@ export function evaluateDoubleSource(input) {
249
250
  }
250
251
 
251
252
 
252
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/session/benchmark.ts
253
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session/benchmark.ts
@@ -0,0 +1,345 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { createServer } from 'node:http';
3
+ export const BRIDGE_PORTS = [
4
+ 8791,
5
+ 8792,
6
+ 8793,
7
+ 8794,
8
+ 8795
9
+ ];
10
+ export const BRIDGE_PROTOCOL = 'session-bridge.v1';
11
+ export const EXTENSION_ID = 'olpgkofjhhiiiahdkkbcninhjmegghfe';
12
+ export const EXTENSION_ORIGIN = `chrome-extension://${EXTENSION_ID}`;
13
+ export const EXTENSION_CONNECTED_WINDOW_MS = 45_000;
14
+ export const JOBS_LONG_POLL_MS = 20_000;
15
+ export const DEFAULT_EXTENSION_WAIT_MS = 6_000;
16
+ export const MAX_RESULT_BODY_BYTES = 8 * 1024 * 1024;
17
+ function readBody(req, cap) {
18
+ return new Promise((resolve)=>{
19
+ const chunks = [];
20
+ let size = 0;
21
+ let done = false;
22
+ req.on('data', (c)=>{
23
+ if (done) return;
24
+ size += c.length;
25
+ if (size > cap) {
26
+ done = true;
27
+ req.removeAllListeners('data');
28
+ resolve({
29
+ err: `body 超 ${cap} 字节上限(防失控回包)`
30
+ });
31
+ return;
32
+ }
33
+ chunks.push(c);
34
+ });
35
+ req.on('end', ()=>{
36
+ if (!done) {
37
+ done = true;
38
+ resolve(Buffer.concat(chunks).toString('utf8'));
39
+ }
40
+ });
41
+ req.on('error', ()=>{
42
+ if (!done) {
43
+ done = true;
44
+ resolve({
45
+ err: 'body 读取失败'
46
+ });
47
+ }
48
+ });
49
+ });
50
+ }
51
+ export async function createSessionBridge(opts = {}) {
52
+ const ports = opts.ports ?? [
53
+ ...BRIDGE_PORTS
54
+ ];
55
+ const extensionOrigin = opts.extensionOrigin ?? EXTENSION_ORIGIN;
56
+ const now = opts.now ?? Date.now;
57
+ const queue = [];
58
+ const inFlight = new Map();
59
+ const parked = [];
60
+ let lastSeenAt = 0;
61
+ let closed = false;
62
+ let port = 0;
63
+ const extensionConnected = ()=>lastSeenAt > 0 && now() - lastSeenAt < EXTENSION_CONNECTED_WINDOW_MS;
64
+ function takeQueued() {
65
+ const next = queue.shift();
66
+ if (next) inFlight.set(next.job.jobId, next);
67
+ return next ?? null;
68
+ }
69
+ const server = createServer((req, res)=>{
70
+ if (closed) {
71
+ res.statusCode = 503;
72
+ res.end();
73
+ return;
74
+ }
75
+ const urlPath = (req.url ?? '').split('?')[0];
76
+ const origin = req.headers.origin;
77
+ const isPost = req.method === 'POST';
78
+ if (isPost && origin !== extensionOrigin) {
79
+ res.statusCode = 403;
80
+ res.setHeader('content-type', 'application/json');
81
+ res.end(JSON.stringify({
82
+ ok: false,
83
+ error: 'origin 不在桥白名单'
84
+ }));
85
+ return;
86
+ }
87
+ if (origin === extensionOrigin) {
88
+ lastSeenAt = now();
89
+ }
90
+ const finish = (code, body)=>{
91
+ res.statusCode = code;
92
+ res.setHeader('content-type', 'application/json');
93
+ res.end(JSON.stringify(body));
94
+ };
95
+ if (req.method === 'GET' && urlPath === '/health') {
96
+ finish(200, {
97
+ ok: true,
98
+ service: 'gotry-session-bridge',
99
+ protocol: BRIDGE_PROTOCOL
100
+ });
101
+ return;
102
+ }
103
+ if (req.method === 'GET' && urlPath === '/status') {
104
+ finish(200, {
105
+ ok: true,
106
+ port,
107
+ protocol: BRIDGE_PROTOCOL,
108
+ extensionConnected: extensionConnected(),
109
+ lastSeenMsAgo: lastSeenAt > 0 ? now() - lastSeenAt : null,
110
+ queued: queue.length,
111
+ inFlight: inFlight.size
112
+ });
113
+ return;
114
+ }
115
+ if (isPost && urlPath === '/jobs') {
116
+ const next = takeQueued();
117
+ if (next) {
118
+ finish(200, {
119
+ job: next.job
120
+ });
121
+ return;
122
+ }
123
+ const parkTimer = setTimeout(()=>{
124
+ const i = parked.findIndex((p)=>p.res === res);
125
+ if (i >= 0) parked.splice(i, 1);
126
+ finish(200, {
127
+ job: null
128
+ });
129
+ }, JOBS_LONG_POLL_MS);
130
+ parked.push({
131
+ res,
132
+ timer: parkTimer
133
+ });
134
+ res.on('close', ()=>{
135
+ const i = parked.findIndex((p)=>p.res === res);
136
+ if (i >= 0) parked.splice(i, 1);
137
+ clearTimeout(parkTimer);
138
+ });
139
+ return;
140
+ }
141
+ if (isPost && urlPath.startsWith('/results/')) {
142
+ const jobId = decodeURIComponent(urlPath.slice('/results/'.length));
143
+ void readBody(req, MAX_RESULT_BODY_BYTES).then((body)=>{
144
+ if (typeof body !== 'string') {
145
+ finish(400, {
146
+ ok: false,
147
+ error: body.err
148
+ });
149
+ return;
150
+ }
151
+ let parsed;
152
+ try {
153
+ parsed = JSON.parse(body);
154
+ } catch {
155
+ finish(400, {
156
+ ok: false,
157
+ error: '回包不是 JSON'
158
+ });
159
+ return;
160
+ }
161
+ finish(200, {
162
+ ok: true
163
+ });
164
+ resolveJob(jobId, parsed);
165
+ });
166
+ return;
167
+ }
168
+ finish(404, {
169
+ ok: false,
170
+ error: 'unknown endpoint'
171
+ });
172
+ });
173
+ function resolveJob(jobId, parsed) {
174
+ const inFlightHit = inFlight.get(jobId);
175
+ if (inFlightHit) {
176
+ inFlight.delete(jobId);
177
+ if (inFlightHit.timer) clearTimeout(inFlightHit.timer);
178
+ inFlightHit.resolve({
179
+ ok: true,
180
+ result: parsed
181
+ });
182
+ return;
183
+ }
184
+ const idx = queue.findIndex((q)=>q.job.jobId === jobId);
185
+ if (idx < 0) return;
186
+ const [entry] = queue.splice(idx, 1);
187
+ if (entry.timer) clearTimeout(entry.timer);
188
+ entry.resolve({
189
+ ok: true,
190
+ result: parsed
191
+ });
192
+ }
193
+ function dispatchToParked(entry) {
194
+ const p = parked.shift();
195
+ if (!p) return false;
196
+ clearTimeout(p.timer);
197
+ inFlight.set(entry.job.jobId, entry);
198
+ p.res.statusCode = 200;
199
+ p.res.setHeader('content-type', 'application/json');
200
+ p.res.end(JSON.stringify({
201
+ job: entry.job
202
+ }));
203
+ return true;
204
+ }
205
+ const bridge = {
206
+ port: 0,
207
+ extensionConnected,
208
+ submit (job, submitOpts = {}) {
209
+ const extensionWaitMs = submitOpts.extensionWaitMs ?? DEFAULT_EXTENSION_WAIT_MS;
210
+ const timeoutMs = submitOpts.timeoutMs ?? job.timeoutMs ?? 30_000;
211
+ const full = {
212
+ jobId: randomUUID(),
213
+ ...job
214
+ };
215
+ return new Promise((resolve)=>{
216
+ if (closed) {
217
+ resolve({
218
+ ok: false,
219
+ reason: 'bridge-unavailable',
220
+ summary: '扩展桥已关闭'
221
+ });
222
+ return;
223
+ }
224
+ const entry = {
225
+ job: full,
226
+ resolve: null,
227
+ timer: null
228
+ };
229
+ const settle = (outcome)=>{
230
+ if (entry.timer) clearTimeout(entry.timer);
231
+ const i = queue.findIndex((q)=>q.job.jobId === full.jobId);
232
+ if (i >= 0) queue.splice(i, 1);
233
+ inFlight.delete(full.jobId);
234
+ resolve(outcome);
235
+ };
236
+ entry.resolve = settle;
237
+ entry.timer = setTimeout(()=>{
238
+ settle({
239
+ ok: false,
240
+ reason: 'timeout',
241
+ summary: `扩展未在 ${timeoutMs}ms 内回包(标签页无嗅探命中或扩展已停用)`
242
+ });
243
+ }, timeoutMs);
244
+ if (!dispatchToParked(entry)) queue.push(entry);
245
+ let waited = 0;
246
+ const waitTimer = setInterval(()=>{
247
+ waited += 250;
248
+ if (bridge.extensionConnected()) {
249
+ clearInterval(waitTimer);
250
+ return;
251
+ }
252
+ if (waited >= extensionWaitMs) {
253
+ clearInterval(waitTimer);
254
+ settle({
255
+ ok: false,
256
+ reason: 'extension-not-connected',
257
+ summary: 'GoTry Session Bridge 扩展未连接(未安装或已停用)。一次性安装:npx gotry setup 查看指引 → chrome://extensions 开发者模式「加载已解压的扩展程序」指向 ~/.gotry/extension'
258
+ });
259
+ }
260
+ }, 250);
261
+ });
262
+ },
263
+ close: ()=>new Promise((resolve)=>{
264
+ closed = true;
265
+ const voided = {
266
+ ok: false,
267
+ reason: 'bridge-unavailable',
268
+ summary: '扩展桥关闭,任务作废(零花费)'
269
+ };
270
+ for (const q of queue.splice(0)){
271
+ if (q.timer) clearTimeout(q.timer);
272
+ q.resolve(voided);
273
+ }
274
+ for (const [, q] of inFlight){
275
+ if (q.timer) clearTimeout(q.timer);
276
+ q.resolve(voided);
277
+ }
278
+ inFlight.clear();
279
+ for (const p of parked.splice(0)){
280
+ clearTimeout(p.timer);
281
+ p.res.destroy();
282
+ }
283
+ server.close(()=>resolve());
284
+ server.closeAllConnections?.();
285
+ })
286
+ };
287
+ const listenAt = (p)=>new Promise((resolve, reject)=>{
288
+ const onError = (e)=>{
289
+ server.off('listening', onListening);
290
+ reject(e);
291
+ };
292
+ const onListening = ()=>{
293
+ server.off('error', onError);
294
+ resolve();
295
+ };
296
+ server.once('error', onError);
297
+ server.once('listening', onListening);
298
+ server.listen(p, '127.0.0.1');
299
+ });
300
+ for (const p of ports){
301
+ try {
302
+ await listenAt(p);
303
+ if (!opts.keepBridge) {
304
+ server.unref();
305
+ }
306
+ port = server.address().port;
307
+ bridge.port = port;
308
+ return {
309
+ ok: true,
310
+ bridge
311
+ };
312
+ } catch {}
313
+ }
314
+ return {
315
+ ok: false,
316
+ summary: `扩展桥端口池 ${ports.join('/')} 全部被占(多为并行 gotry 实例);不影响官方通道 gotry_flyai_search。关闭多余实例后重试。`
317
+ };
318
+ }
319
+ let singleton = null;
320
+ export async function getOrCreateSessionBridge(opts = {}) {
321
+ if (singleton) return singleton;
322
+ const created = await createSessionBridge(opts);
323
+ const result = created.ok ? {
324
+ ok: true,
325
+ bridge: created.bridge
326
+ } : {
327
+ ok: false,
328
+ summary: created.summary
329
+ };
330
+ singleton = result;
331
+ return result;
332
+ }
333
+ export async function __resetSessionBridgeForTest() {
334
+ if (singleton?.ok) await singleton.bridge.close().catch(()=>{});
335
+ singleton = null;
336
+ }
337
+ export function __setSessionBridgeForTest(bridge) {
338
+ singleton = bridge ? {
339
+ ok: true,
340
+ bridge
341
+ } : null;
342
+ }
343
+
344
+
345
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session/extension-bridge.ts
@@ -0,0 +1,93 @@
1
+ import { getOrCreateSessionBridge } from './extension-bridge.js';
2
+ export const NEEDS_EXTENSION_HINT = '需要一次性安装 GoTry Session Bridge 浏览器扩展(约 30 秒,装完零弹窗):跑 npx gotry setup 拿指引,' + '或在 chrome://extensions 开发者模式「加载已解压的扩展程序」指向 ~/.gotry/extension';
3
+ export function classifyBridgeFailure(kind) {
4
+ return kind === 'extension-not-connected' ? 'needs-extension' : 'error';
5
+ }
6
+ function bridgeReady(b) {
7
+ return b.ok === true;
8
+ }
9
+ export async function extensionCookieNames(q) {
10
+ const bridge = await getOrCreateSessionBridge();
11
+ if (!bridgeReady(bridge)) return {
12
+ ok: false,
13
+ kind: 'bridge-unavailable',
14
+ summary: bridge.summary
15
+ };
16
+ const outcome = await bridge.bridge.submit({
17
+ kind: 'cookie-names',
18
+ site: q.site,
19
+ timeoutMs: q.timeoutMs ?? 8_000
20
+ }, {
21
+ timeoutMs: q.timeoutMs ?? 8_000
22
+ });
23
+ if (!outcome.ok) return {
24
+ ok: false,
25
+ kind: outcome.reason,
26
+ summary: outcome.summary
27
+ };
28
+ const names = Array.isArray(outcome.result.names) ? outcome.result.names.filter((n)=>typeof n === 'string') : [];
29
+ return {
30
+ ok: true,
31
+ tickets: names
32
+ };
33
+ }
34
+ export async function extensionOpenLogin(q) {
35
+ const bridge = await getOrCreateSessionBridge();
36
+ if (!bridgeReady(bridge)) return {
37
+ ok: false,
38
+ kind: 'bridge-unavailable',
39
+ summary: bridge.summary
40
+ };
41
+ const outcome = await bridge.bridge.submit({
42
+ kind: 'open-login',
43
+ site: q.site,
44
+ url: q.url
45
+ }, {
46
+ timeoutMs: q.timeoutMs ?? 15_000
47
+ });
48
+ if (!outcome.ok) return {
49
+ ok: false,
50
+ kind: outcome.reason,
51
+ summary: outcome.summary
52
+ };
53
+ if (!outcome.result.opened) return {
54
+ ok: false,
55
+ kind: 'job-error',
56
+ summary: outcome.result.error ?? '登录入口打开失败'
57
+ };
58
+ return {
59
+ ok: true,
60
+ opened: true
61
+ };
62
+ }
63
+ export async function extensionSearchJob(q) {
64
+ const bridge = await getOrCreateSessionBridge();
65
+ if (!bridgeReady(bridge)) return {
66
+ ok: false,
67
+ kind: 'bridge-unavailable',
68
+ summary: bridge.summary
69
+ };
70
+ const timeoutMs = q.timeoutMs ?? 30_000;
71
+ const outcome = await bridge.bridge.submit({
72
+ kind: 'search',
73
+ site: q.site,
74
+ url: q.url,
75
+ timeoutMs
76
+ }, {
77
+ timeoutMs: timeoutMs + 10_000
78
+ });
79
+ if (!outcome.ok) return {
80
+ ok: false,
81
+ kind: outcome.reason,
82
+ summary: outcome.summary
83
+ };
84
+ return {
85
+ ok: true,
86
+ body: typeof outcome.result.body === 'string' ? outcome.result.body : '',
87
+ title: typeof outcome.result.title === 'string' ? outcome.result.title : '',
88
+ timedOut: outcome.result.timeout === true
89
+ };
90
+ }
91
+
92
+
93
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session/extension-channel.ts
@@ -37,4 +37,4 @@ export function nameAffinity(a, b) {
37
37
  }
38
38
 
39
39
 
40
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/session/extract.ts
40
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/session/extract.ts