@danceiny/gotry 0.0.1-rc.20 → 0.0.1-rc.21
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 +14 -3
- package/README.zh-CN.md +15 -4
- package/bin/gotry-bootstrap.js +53 -8
- package/cordis.gotry-patch.yml +12 -5
- package/dist/capabilities/doctor.js +88 -16
- package/dist/capabilities/session/health-watch.js +10 -3
- package/dist/scripts/benchmark-environment-bridge-e2e.js +53 -15
- package/dist/scripts/benchmark-environment-bridge-tests.js +450 -28
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +211 -2
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +132 -2
- package/dist/scripts/bootstrap-tests.js +2 -1
- package/dist/scripts/hbcli-e2e-tests.js +1 -1
- package/dist/scripts/health-watch-cli.js +9 -1
- package/dist/scripts/map-tools-vendor-package-proof.js +276 -0
- package/dist/scripts/persona-surface-guard-tests.js +8 -3
- package/dist/src/benchmark-agent-conformance.js +155 -5
- package/dist/src/benchmark-environment-bridge.js +53 -71
- package/dist/src/booking-surface/dsh-planner.js +24 -8
- package/extension/manifest.json +2 -2
- package/package.json +2 -1
- package/ts/capabilities/session/health-watch.ts +9 -2
- package/ts/package.json +15 -7
- package/ts/scripts/map-tools-vendor-package-proof.ts +268 -0
- package/ts/src/benchmark-agent-conformance.ts +135 -4
- package/ts/src/benchmark-environment-bridge.ts +25 -40
- package/ts/src/booking-surface/dsh-planner.ts +25 -13
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
2
3
|
import { DSH_EMBEDDED_BOOKING_TOOL_NAMES, buildDshPlannerEnvironment, createDshEmbeddedBookingPlanner } from '../src/booking-surface/dsh-planner.js';
|
|
3
4
|
const availability = {
|
|
4
5
|
initialized: true,
|
|
@@ -343,7 +344,8 @@ const sanitizedRefPort = {
|
|
|
343
344
|
action: {
|
|
344
345
|
...searchRun,
|
|
345
346
|
factRefs: [
|
|
346
|
-
'draft:destination=Dubai'
|
|
347
|
+
'draft:destination=Dubai',
|
|
348
|
+
'draft:destination.Dubai'
|
|
347
349
|
]
|
|
348
350
|
}
|
|
349
351
|
}
|
|
@@ -373,9 +375,216 @@ const sanitizedDecisions = await sanitizedRef.plannerFactory(task).next({
|
|
|
373
375
|
});
|
|
374
376
|
assert.equal(sanitizedDecisions[0]?.kind, 'operation', 'sanitizer maps off-charset characters deterministically');
|
|
375
377
|
assert.deepEqual(sanitizedDecisions[0].action?.factRefs, [
|
|
378
|
+
'modelref:43b07dd7fc80f4a9889b6c672c450a911086293cab2ef4058b95b53559a2d100',
|
|
376
379
|
'draft:destination.Dubai'
|
|
377
380
|
]);
|
|
378
381
|
await sanitizedRef.close();
|
|
382
|
+
const collisionRawRefs = [
|
|
383
|
+
'fact://a/b',
|
|
384
|
+
'fact:..a?b'
|
|
385
|
+
];
|
|
386
|
+
const collisionResults = [];
|
|
387
|
+
for (const [index, rawRef] of collisionRawRefs.entries()){
|
|
388
|
+
const collisionPlanner = await createDshEmbeddedBookingPlanner({
|
|
389
|
+
runPort: {
|
|
390
|
+
async run () {
|
|
391
|
+
return {
|
|
392
|
+
finalResponse: '',
|
|
393
|
+
events: [
|
|
394
|
+
{
|
|
395
|
+
type: 'tool/call',
|
|
396
|
+
data: {
|
|
397
|
+
name: 'booking_search_hotels',
|
|
398
|
+
arguments: JSON.stringify({
|
|
399
|
+
decision: {
|
|
400
|
+
kind: 'operation',
|
|
401
|
+
action: {
|
|
402
|
+
...searchRun,
|
|
403
|
+
actionId: `action-dsh-collision-${index}`,
|
|
404
|
+
factRefs: [
|
|
405
|
+
rawRef
|
|
406
|
+
]
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
})
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
]
|
|
413
|
+
};
|
|
414
|
+
},
|
|
415
|
+
async close () {}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
const decisions = await collisionPlanner.plannerFactory(task).next({
|
|
419
|
+
task,
|
|
420
|
+
turn: {
|
|
421
|
+
schemaVersion: 'booking.surface',
|
|
422
|
+
kind: 'user.turn',
|
|
423
|
+
taskId: task.taskId,
|
|
424
|
+
turnId: `dsh-collision-${index}`,
|
|
425
|
+
workspace,
|
|
426
|
+
request: {
|
|
427
|
+
text: 'Find hotels'
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
const ref = decisions[0].action?.factRefs?.[0];
|
|
432
|
+
assert.match(ref ?? '', /^[A-Za-z0-9][A-Za-z0-9:._-]*$/, 'unsafe refs map to the runtime-safe pattern');
|
|
433
|
+
collisionResults.push(ref ?? '');
|
|
434
|
+
await collisionPlanner.close();
|
|
435
|
+
}
|
|
436
|
+
assert.notEqual(collisionResults[0], collisionResults[1], 'distinct unsafe raw refs retain collision-resistant aliases');
|
|
437
|
+
assert.equal(collisionRawRefs[0].replace(/#/g, ':').replace(/[^A-Za-z0-9:._-]/g, '.'), collisionRawRefs[1].replace(/#/g, ':').replace(/[^A-Za-z0-9:._-]/g, '.'), 'the collision pair shares the legacy readable projection');
|
|
438
|
+
async function assertFactRefsRejected(factRefs, message) {
|
|
439
|
+
const planner = await createDshEmbeddedBookingPlanner({
|
|
440
|
+
runPort: {
|
|
441
|
+
async run () {
|
|
442
|
+
return {
|
|
443
|
+
finalResponse: '',
|
|
444
|
+
events: [
|
|
445
|
+
{
|
|
446
|
+
type: 'tool/call',
|
|
447
|
+
data: {
|
|
448
|
+
name: 'booking_search_hotels',
|
|
449
|
+
arguments: JSON.stringify({
|
|
450
|
+
decision: {
|
|
451
|
+
kind: 'operation',
|
|
452
|
+
action: {
|
|
453
|
+
...searchRun,
|
|
454
|
+
factRefs
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
})
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
]
|
|
461
|
+
};
|
|
462
|
+
},
|
|
463
|
+
async close () {}
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
await assert.rejects(planner.plannerFactory(task).next({
|
|
467
|
+
task,
|
|
468
|
+
turn: {
|
|
469
|
+
schemaVersion: 'booking.surface',
|
|
470
|
+
kind: 'user.turn',
|
|
471
|
+
taskId: task.taskId,
|
|
472
|
+
turnId: 'dsh-ref-rejection',
|
|
473
|
+
workspace,
|
|
474
|
+
request: {
|
|
475
|
+
text: 'Find hotels'
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}), /planner_invalid_action/, message);
|
|
479
|
+
await planner.close();
|
|
480
|
+
}
|
|
481
|
+
const firstAlias = 'modelref:'.concat(createHash('sha256').update(collisionRawRefs[0], 'utf8').digest('hex'));
|
|
482
|
+
await assertFactRefsRejected([
|
|
483
|
+
collisionRawRefs[0],
|
|
484
|
+
firstAlias
|
|
485
|
+
], 'unsafe raw ref cannot alias a same-action reserved modelref');
|
|
486
|
+
await assertFactRefsRejected([
|
|
487
|
+
collisionRawRefs[0],
|
|
488
|
+
collisionRawRefs[0]
|
|
489
|
+
], 'repair-time duplicate factRefs are rejected by canonical validation');
|
|
490
|
+
await assertFactRefsRejected([
|
|
491
|
+
'modelref:'.concat('a'.repeat(64))
|
|
492
|
+
], 'direct modelref namespace input is reserved and rejected');
|
|
493
|
+
const reservedRecovery = await createDshEmbeddedBookingPlanner({
|
|
494
|
+
runPort: {
|
|
495
|
+
async run () {
|
|
496
|
+
return {
|
|
497
|
+
finalResponse: JSON.stringify({
|
|
498
|
+
kind: 'operation',
|
|
499
|
+
action: {
|
|
500
|
+
...searchRun,
|
|
501
|
+
factRefs: [
|
|
502
|
+
'modelref:'.concat('b'.repeat(64))
|
|
503
|
+
]
|
|
504
|
+
}
|
|
505
|
+
}),
|
|
506
|
+
events: []
|
|
507
|
+
};
|
|
508
|
+
},
|
|
509
|
+
async close () {}
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
const reservedRecoveryDecision = await reservedRecovery.plannerFactory(task).next({
|
|
513
|
+
task,
|
|
514
|
+
turn: {
|
|
515
|
+
schemaVersion: 'booking.surface',
|
|
516
|
+
kind: 'user.turn',
|
|
517
|
+
taskId: task.taskId,
|
|
518
|
+
turnId: 'dsh-reserved-recovery',
|
|
519
|
+
workspace,
|
|
520
|
+
request: {
|
|
521
|
+
text: 'Find hotels'
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
assert.equal(reservedRecoveryDecision[0]?.kind, 'error', 'finalResponse recovery rejects direct reserved modelref aliases');
|
|
526
|
+
await reservedRecovery.close();
|
|
527
|
+
let stableRefCall = 0;
|
|
528
|
+
const stableRawRef = 'fact://same/raw';
|
|
529
|
+
const stablePlanner = await createDshEmbeddedBookingPlanner({
|
|
530
|
+
runPort: {
|
|
531
|
+
async run () {
|
|
532
|
+
stableRefCall += 1;
|
|
533
|
+
return {
|
|
534
|
+
finalResponse: '',
|
|
535
|
+
events: [
|
|
536
|
+
{
|
|
537
|
+
type: 'tool/call',
|
|
538
|
+
data: {
|
|
539
|
+
name: 'booking_search_hotels',
|
|
540
|
+
arguments: JSON.stringify({
|
|
541
|
+
decision: {
|
|
542
|
+
kind: 'operation',
|
|
543
|
+
action: {
|
|
544
|
+
...searchRun,
|
|
545
|
+
actionId: `action-dsh-stable-${stableRefCall}`,
|
|
546
|
+
factRefs: [
|
|
547
|
+
stableRawRef
|
|
548
|
+
]
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
})
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
]
|
|
555
|
+
};
|
|
556
|
+
},
|
|
557
|
+
async close () {}
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
const stableFirst = await stablePlanner.plannerFactory(task).next({
|
|
561
|
+
task,
|
|
562
|
+
turn: {
|
|
563
|
+
schemaVersion: 'booking.surface',
|
|
564
|
+
kind: 'user.turn',
|
|
565
|
+
taskId: task.taskId,
|
|
566
|
+
turnId: 'dsh-stable-1',
|
|
567
|
+
workspace,
|
|
568
|
+
request: {
|
|
569
|
+
text: 'Find hotels'
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
const stableSecond = await stablePlanner.plannerFactory(task).next({
|
|
574
|
+
task,
|
|
575
|
+
turn: {
|
|
576
|
+
schemaVersion: 'booking.surface',
|
|
577
|
+
kind: 'user.turn',
|
|
578
|
+
taskId: task.taskId,
|
|
579
|
+
turnId: 'dsh-stable-2',
|
|
580
|
+
workspace,
|
|
581
|
+
request: {
|
|
582
|
+
text: 'Find hotels again'
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
assert.equal(stableFirst[0].action?.factRefs?.[0], stableSecond[0].action?.factRefs?.[0], 'same raw ref is stable across actions and turns');
|
|
587
|
+
await stablePlanner.close();
|
|
379
588
|
const unauthorisedPort = {
|
|
380
589
|
async run () {
|
|
381
590
|
return {
|
|
@@ -458,7 +667,7 @@ const fragmentDecisions = await fragmentRef.plannerFactory(task).next({
|
|
|
458
667
|
});
|
|
459
668
|
assert.equal(fragmentDecisions[0]?.kind, 'operation', 'JSON-pointer fragment factRefs repair into the safe charset');
|
|
460
669
|
assert.deepEqual(fragmentDecisions[0].action?.factRefs, [
|
|
461
|
-
`
|
|
670
|
+
`modelref:c7819d3c0c375fe1bd389006338b99be890fa8e3ddd9390f243cba8b5c139d9a`
|
|
462
671
|
]);
|
|
463
672
|
await fragmentRef.close();
|
|
464
673
|
const truncatedPort = {
|
|
@@ -142,7 +142,137 @@ const action = (id, revision = 0, extra = {})=>({
|
|
|
142
142
|
input: {},
|
|
143
143
|
...extra
|
|
144
144
|
});
|
|
145
|
-
|
|
145
|
+
const repairedFactRef = `modelref:${'a'.repeat(64)}`;
|
|
146
|
+
{
|
|
147
|
+
const root = mkdtempSync(join(tmpdir(), 'gotry-booking-v2-factref-action-authority-'));
|
|
148
|
+
const ledger = ensureLedger(root);
|
|
149
|
+
const rt = new BookingCopilotTaskRuntime(ledger, {
|
|
150
|
+
contextRefFactory: ()=>'ctx-v2'
|
|
151
|
+
});
|
|
152
|
+
const limitedWorkspace = {
|
|
153
|
+
...workspace(0),
|
|
154
|
+
capabilities: {
|
|
155
|
+
surface: 'tenant',
|
|
156
|
+
allowedActions: [
|
|
157
|
+
'search.run'
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const t = rt.startTask({
|
|
162
|
+
...turn('task-factref-action-authority'),
|
|
163
|
+
workspace: limitedWorkspace
|
|
164
|
+
});
|
|
165
|
+
const before = ledger.countEvents();
|
|
166
|
+
assert.throws(()=>rt.issueOperation(t.taskId, {
|
|
167
|
+
...action('factref-forged-action', 0, {
|
|
168
|
+
factRefs: [
|
|
169
|
+
repairedFactRef,
|
|
170
|
+
'checkout.prepare'
|
|
171
|
+
]
|
|
172
|
+
}),
|
|
173
|
+
kind: 'checkout.prepare',
|
|
174
|
+
input: {
|
|
175
|
+
offerRef: 'offer-a',
|
|
176
|
+
offerVersionRef: 'offer-a:v1',
|
|
177
|
+
verifiedOfferRef: 'verified-offer-a'
|
|
178
|
+
}
|
|
179
|
+
}), /unsupported_action/, 'factRefs cannot add an action to the surface allowlist');
|
|
180
|
+
assert.equal(ledger.countEvents(), before, 'rejected factRef authority does not persist an operation');
|
|
181
|
+
ledger.close();
|
|
182
|
+
rmSync(root, {
|
|
183
|
+
recursive: true,
|
|
184
|
+
force: true
|
|
185
|
+
});
|
|
186
|
+
}{
|
|
187
|
+
const root = mkdtempSync(join(tmpdir(), 'gotry-booking-v2-factref-offer-authority-'));
|
|
188
|
+
const ledger = ensureLedger(root);
|
|
189
|
+
const rt = new BookingCopilotTaskRuntime(ledger, {
|
|
190
|
+
contextRefFactory: ()=>'ctx-v2'
|
|
191
|
+
});
|
|
192
|
+
const offerWorkspace = {
|
|
193
|
+
...workspace(0),
|
|
194
|
+
visibleHotels: [
|
|
195
|
+
{
|
|
196
|
+
hotelRef: 'hotel-a',
|
|
197
|
+
name: 'Hotel A',
|
|
198
|
+
factRefs: []
|
|
199
|
+
}
|
|
200
|
+
],
|
|
201
|
+
loadedOffers: [
|
|
202
|
+
loadedOffer('offer-a', 'hotel-a', 'offer-a:v1')
|
|
203
|
+
]
|
|
204
|
+
};
|
|
205
|
+
const t = rt.startTask({
|
|
206
|
+
...turn('task-factref-offer-authority'),
|
|
207
|
+
workspace: offerWorkspace
|
|
208
|
+
});
|
|
209
|
+
const before = ledger.countEvents();
|
|
210
|
+
assert.throws(()=>rt.issueOperation(t.taskId, {
|
|
211
|
+
...action('factref-forged-version', 0, {
|
|
212
|
+
factRefs: [
|
|
213
|
+
repairedFactRef,
|
|
214
|
+
'offer-a:v2'
|
|
215
|
+
]
|
|
216
|
+
}),
|
|
217
|
+
kind: 'offer.check',
|
|
218
|
+
input: {
|
|
219
|
+
offerRef: 'offer-a',
|
|
220
|
+
offerVersionRef: 'offer-a:v2'
|
|
221
|
+
}
|
|
222
|
+
}), /offer_version_not_loaded/, 'factRefs cannot promote an unloaded offer version');
|
|
223
|
+
assert.equal(ledger.countEvents(), before, 'rejected factRef version does not persist an operation');
|
|
224
|
+
ledger.close();
|
|
225
|
+
rmSync(root, {
|
|
226
|
+
recursive: true,
|
|
227
|
+
force: true
|
|
228
|
+
});
|
|
229
|
+
}{
|
|
230
|
+
const root = mkdtempSync(join(tmpdir(), 'gotry-booking-v2-factref-verified-authority-'));
|
|
231
|
+
const ledger = ensureLedger(root);
|
|
232
|
+
const rt = new BookingCopilotTaskRuntime(ledger, {
|
|
233
|
+
contextRefFactory: ()=>'ctx-v2',
|
|
234
|
+
now: ()=>'2026-09-01T10:00:00.000Z'
|
|
235
|
+
});
|
|
236
|
+
const offerWorkspace = {
|
|
237
|
+
...workspace(0),
|
|
238
|
+
visibleHotels: [
|
|
239
|
+
{
|
|
240
|
+
hotelRef: 'hotel-a',
|
|
241
|
+
name: 'Hotel A',
|
|
242
|
+
factRefs: []
|
|
243
|
+
}
|
|
244
|
+
],
|
|
245
|
+
loadedOffers: [
|
|
246
|
+
loadedOffer('offer-a', 'hotel-a', 'offer-a:v1')
|
|
247
|
+
],
|
|
248
|
+
selectedOfferRef: 'offer-a'
|
|
249
|
+
};
|
|
250
|
+
const t = rt.startTask({
|
|
251
|
+
...turn('task-factref-verified-authority'),
|
|
252
|
+
workspace: offerWorkspace
|
|
253
|
+
});
|
|
254
|
+
const before = ledger.countEvents();
|
|
255
|
+
assert.throws(()=>rt.issueOperation(t.taskId, {
|
|
256
|
+
...action('factref-forged-verified', 0, {
|
|
257
|
+
factRefs: [
|
|
258
|
+
repairedFactRef,
|
|
259
|
+
'verified-offer-a'
|
|
260
|
+
]
|
|
261
|
+
}),
|
|
262
|
+
kind: 'checkout.prepare',
|
|
263
|
+
input: {
|
|
264
|
+
offerRef: 'offer-a',
|
|
265
|
+
offerVersionRef: 'offer-a:v1',
|
|
266
|
+
verifiedOfferRef: 'verified-offer-a'
|
|
267
|
+
}
|
|
268
|
+
}), /offer_version_not_loaded/, 'factRefs cannot create verified-offer authority');
|
|
269
|
+
assert.equal(ledger.countEvents(), before, 'rejected factRef verification does not persist an operation');
|
|
270
|
+
ledger.close();
|
|
271
|
+
rmSync(root, {
|
|
272
|
+
recursive: true,
|
|
273
|
+
force: true
|
|
274
|
+
});
|
|
275
|
+
}let id = 0;
|
|
146
276
|
const runtime = new BookingCopilotTaskRuntime(ensureLedger(stateRoot), {
|
|
147
277
|
idFactory: (prefix)=>`${prefix}-${++id}`,
|
|
148
278
|
now: ()=>'2026-09-01T10:00:00.000Z'
|
|
@@ -3997,7 +4127,7 @@ rmSync(offerRoot, {
|
|
|
3997
4127
|
recursive: true,
|
|
3998
4128
|
force: true
|
|
3999
4129
|
});
|
|
4000
|
-
console.log('BOOKING COPILOT RUNTIME PROOF: task scope, receipt binding, approval authority/one-time, recovery, ingress/SSE session OK');
|
|
4130
|
+
console.log('BOOKING COPILOT RUNTIME PROOF: task scope, receipt binding, factRef non-authority, approval authority/one-time, recovery, ingress/SSE session OK');
|
|
4001
4131
|
|
|
4002
4132
|
|
|
4003
4133
|
//# sourceURL=ts/scripts/booking-copilot-runtime-proof-tests.ts
|
|
@@ -76,7 +76,8 @@ const c6 = runBootstrap([
|
|
|
76
76
|
], {
|
|
77
77
|
GOTRY_SETUP_EXTENSION: '0',
|
|
78
78
|
GOTRY_ONBOARDING_TIMEOUT_MS: '600',
|
|
79
|
-
GOTRY_ONBOARDING_INTERVAL_MS: '200'
|
|
79
|
+
GOTRY_ONBOARDING_INTERVAL_MS: '200',
|
|
80
|
+
GOTRY_ONBOARDING_BRIDGE_PORTS: '0'
|
|
80
81
|
});
|
|
81
82
|
assert.equal(c6.code, 1, `wizard(超时)应 exit 1,实际 ${c6.code}\n${c6.out}`);
|
|
82
83
|
assert.ok(c6.out.includes('gotry-wizard'), '应输出 [gotry-wizard] 标签');
|
|
@@ -10,7 +10,7 @@ const SANDBOX_APP_KEY = 'hotelbyte_api_demo';
|
|
|
10
10
|
const SANDBOX_APP_SECRET = 'hotelbyte_api_demo';
|
|
11
11
|
const GUIDANCE = [
|
|
12
12
|
'staicli(hbcli)账号配置指引:',
|
|
13
|
-
' 1. 安装 CLI(若缺):npx gotry setup(
|
|
13
|
+
' 1. 安装 CLI(若缺):npx gotry setup(npm 装 staicli@npmjs → PATH 的 hbcli)',
|
|
14
14
|
' 2. 快速试用(沙箱演示账号,来自 hotel-be 种子,user/domain/predefined_user_demo.go):',
|
|
15
15
|
' hbcli auth set-credentials --app-key hotelbyte_api_demo --app-secret hotelbyte_api_demo',
|
|
16
16
|
' 3. 正式接入:向 HotelByte 申请专属 appKey/appSecret(hbk_*/hbs_*),替换第 2 步凭证;',
|
|
@@ -15,6 +15,13 @@ function parseArgs(argv) {
|
|
|
15
15
|
}
|
|
16
16
|
if (Number.isNaN(args.timeoutMs) || args.timeoutMs < 0) args.timeoutMs = 120_000;
|
|
17
17
|
if (Number.isNaN(args.intervalMs) || args.intervalMs < 1_000) args.intervalMs = 5_000;
|
|
18
|
+
const rawPorts = process.env.GOTRY_ONBOARDING_BRIDGE_PORTS;
|
|
19
|
+
if (rawPorts) {
|
|
20
|
+
const parsed = rawPorts.split(',').map((part)=>Number.parseInt(part.trim(), 10));
|
|
21
|
+
if (parsed.every((port)=>Number.isInteger(port) && port >= 0 && port <= 65535)) {
|
|
22
|
+
args.ports = parsed;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
18
25
|
return args;
|
|
19
26
|
}
|
|
20
27
|
async function main() {
|
|
@@ -24,7 +31,8 @@ async function main() {
|
|
|
24
31
|
timeoutMs: args.timeoutMs,
|
|
25
32
|
intervalMs: args.intervalMs,
|
|
26
33
|
keepBridge: true,
|
|
27
|
-
businessProbe
|
|
34
|
+
businessProbe,
|
|
35
|
+
ports: args.ports
|
|
28
36
|
});
|
|
29
37
|
process.on('SIGINT', ()=>{
|
|
30
38
|
watch.cancel('SIGINT');
|