@danceiny/gotry 0.0.1-rc.9 → 0.2.0-rc.28
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 +172 -219
- package/README.zh-CN.md +222 -0
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-booking-copilot.js +53 -0
- package/bin/gotry-bootstrap.js +1788 -0
- package/bin/gotry-inner.js +733 -83
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +27 -0
- package/bin/gotry-runtime-resolution.js +49 -0
- package/bin/gotry.js +1 -1
- package/client/client.js +203 -0
- package/cordis.gotry-patch.yml +80 -56
- package/data/airline-airports.json +39 -0
- package/data/stations-12306-verify.json +524 -0
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +101 -146
- package/dist/capabilities/anything.js +161 -94
- package/dist/capabilities/artifacts.js +370 -0
- package/dist/capabilities/channel-health.js +131 -0
- package/dist/capabilities/channel-registry.js +256 -0
- package/dist/capabilities/doctor.js +553 -0
- package/dist/capabilities/effect.js +394 -0
- package/dist/capabilities/external-event.js +149 -0
- package/dist/capabilities/fact-log.js +55 -0
- package/dist/capabilities/flyai-config.js +337 -0
- package/dist/capabilities/flyai.js +947 -110
- package/dist/capabilities/fx-contract.js +293 -0
- package/dist/capabilities/geo-atlas.js +246 -0
- package/dist/capabilities/ground-transfer.js +676 -0
- package/dist/capabilities/hbcli.js +260 -123
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/itinerary-artifact.js +190 -0
- package/dist/capabilities/itinerary-deck-artifact.js +194 -0
- package/dist/capabilities/itinerary-deck-export.js +339 -0
- package/dist/capabilities/lavish-local.js +924 -0
- package/dist/capabilities/model-override.js +38 -0
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/provider-detail-sanitize.js +64 -0
- package/dist/capabilities/resilience.js +191 -0
- package/dist/capabilities/route-provider-conformance.js +341 -0
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
- package/dist/capabilities/session/adapters/dida-portal.js +274 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +342 -0
- package/dist/capabilities/session/benchmark.js +189 -0
- package/dist/capabilities/session/bridge-ledger.js +224 -0
- package/dist/capabilities/session/extension-bridge.js +636 -0
- package/dist/capabilities/session/extension-channel.js +109 -0
- package/dist/capabilities/session/extension-distribution.js +230 -0
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +114 -0
- package/dist/capabilities/session/health-watch.js +215 -0
- package/dist/capabilities/session/read-guard.js +75 -24
- package/dist/capabilities/session/static-flight-golden.js +150 -0
- package/dist/capabilities/session/transport.js +97 -83
- package/dist/capabilities/session/wizard.js +117 -0
- package/dist/capabilities/session-consent.js +178 -0
- package/dist/capabilities/session-login.js +170 -0
- package/dist/capabilities/session-search.js +635 -44
- package/dist/capabilities/spawn-bounded.js +203 -0
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +140 -0
- package/dist/capabilities/weather.js +243 -138
- package/dist/data/session-golden-20.json +25 -0
- package/dist/data/sf-golden-manifest.json +103 -0
- package/dist/data/sf-static-routes.json +91 -0
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
- package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-error-tests.js +354 -0
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
- package/dist/scripts/anything-path-measure-tests.js +133 -0
- package/dist/scripts/anything-path-measure.js +150 -0
- package/dist/scripts/anything-tests.js +250 -60
- package/dist/scripts/artifact-client-contract-tests.js +220 -0
- package/dist/scripts/artifacts-capability-tests.js +727 -0
- package/dist/scripts/async-collect.js +50 -11
- package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
- package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
- package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
- package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
- package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
- package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
- package/dist/scripts/booking-copilot-dsh-warmer-proof.js +126 -0
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
- package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
- package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
- package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
- package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
- package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +125 -0
- package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
- package/dist/scripts/bootstrap-tests.js +1549 -0
- package/dist/scripts/bridge-events-tests.js +239 -0
- package/dist/scripts/bridge-ledger-tests.js +428 -0
- package/dist/scripts/build-changelog.js +238 -0
- package/dist/scripts/build-metrics-report.js +331 -0
- package/dist/scripts/changelog-tests.js +118 -0
- package/dist/scripts/channel-probe-tests.js +103 -0
- package/dist/scripts/channel-probe.js +169 -0
- package/dist/scripts/channel-registry-tests.js +208 -0
- package/dist/scripts/city-scenario-tier-tests.js +234 -0
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-runner-stop-tests.js +311 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +600 -0
- package/dist/scripts/drill-multiuser-ledger-tests.js +1076 -0
- package/dist/scripts/drill-sdk-descendant-cleanup-tests.js +606 -0
- package/dist/scripts/drill-w2a-sensor-tests.js +788 -0
- package/dist/scripts/dsh-artifact-e2e.js +279 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-llm-tests.js +437 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
- package/dist/scripts/dsh-runtime-closure.js +162 -0
- package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +539 -0
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +174 -0
- package/dist/scripts/evaluation-contract-tests.js +268 -0
- package/dist/scripts/extension-distribution-cli.js +39 -0
- package/dist/scripts/extension-distribution-tests.js +443 -0
- package/dist/scripts/extension-tests.js +988 -0
- package/dist/scripts/external-event-tests.js +350 -0
- package/dist/scripts/fact-gate-tests.js +869 -0
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-setup-tests.js +228 -0
- package/dist/scripts/flyai-setup-tool-tests.js +54 -0
- package/dist/scripts/flyai-tests.js +692 -0
- package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
- package/dist/scripts/fx-contract-tests.js +201 -0
- package/dist/scripts/g5-guard-tests.js +150 -0
- package/dist/scripts/g5-guard.js +193 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/geo-atlas-tests.js +283 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
- package/dist/scripts/gotry-try-demo-tests.js +79 -0
- package/dist/scripts/gotry-try-demo.js +49 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +159 -0
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +121 -69
- package/dist/scripts/health-watch-cli.js +73 -0
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +41 -0
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +569 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
- package/dist/scripts/itinerary-artifact-tests.js +368 -0
- package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
- package/dist/scripts/itinerary-deck-export-tests.js +552 -0
- package/dist/scripts/itinerary-deck-tests.js +435 -0
- package/dist/scripts/itinerary-html-tests.js +426 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/lavish-local-tests.js +1226 -0
- package/dist/scripts/lavish-product-tools-tests.js +537 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +382 -246
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/m3-cohort-tests.js +714 -0
- package/dist/scripts/m3-cohort.js +310 -0
- package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
- package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
- package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
- package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
- package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +631 -0
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +151 -0
- package/dist/scripts/model-override-e2e.js +137 -0
- package/dist/scripts/nightly-evidence-tests.js +203 -0
- package/dist/scripts/nightly-evidence.js +303 -0
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/observability-tests.js +381 -0
- package/dist/scripts/onboarding-tests.js +161 -0
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/outcome-projection-tests.js +427 -0
- package/dist/scripts/persona-sim-tests.js +603 -0
- package/dist/scripts/persona-sim.js +1081 -0
- package/dist/scripts/persona-surface-guard-tests.js +49 -0
- package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +340 -0
- package/dist/scripts/price-drift-watch.js +447 -0
- package/dist/scripts/probe-poi-tests.js +53 -45
- package/dist/scripts/product-metrics.js +483 -0
- package/dist/scripts/publish-preverify.js +110 -0
- package/dist/scripts/realtime-pricing-tests.js +76 -0
- package/dist/scripts/recall-tests.js +513 -0
- package/dist/scripts/replay-async.js +22 -18
- package/dist/scripts/replay-real.js +18 -18
- package/dist/scripts/replay.js +54 -82
- package/dist/scripts/route-provider-conformance-tests.js +391 -0
- package/dist/scripts/session-attach-diagnose.js +20 -20
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +180 -0
- package/dist/scripts/session-dida-live-e2e.js +199 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-link-tests.js +478 -0
- package/dist/scripts/session-login.js +27 -46
- package/dist/scripts/session-tests.js +796 -96
- package/dist/scripts/session-zone-crash.js +49 -0
- package/dist/scripts/session-zone-ledger-tests.js +432 -0
- package/dist/scripts/session-zone-observation-tests.js +492 -0
- package/dist/scripts/session-zone-observe.js +135 -0
- package/dist/scripts/session-zone-wiring-tests.js +566 -0
- package/dist/scripts/session-zones-tests.js +374 -0
- package/dist/scripts/sf-live-benchmark.js +367 -0
- package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
- package/dist/scripts/sf-live-cli-tests.js +92 -0
- package/dist/scripts/sf-live-options.js +46 -0
- package/dist/scripts/sf-manifest-tests.js +30 -0
- package/dist/scripts/sf-manifest.js +33 -0
- package/dist/scripts/sf-soft-score-tests.js +67 -0
- package/dist/scripts/sf-summary-tests.js +324 -0
- package/dist/scripts/sf-summary.js +525 -0
- package/dist/scripts/share-tests.js +432 -0
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +28 -11
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +21 -0
- package/dist/scripts/smoke.js +690 -246
- package/dist/scripts/spawn-bounded-abort-tests.js +244 -0
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +565 -198
- package/dist/scripts/state-repair-cli.js +66 -0
- package/dist/scripts/state-repair-tests.js +140 -0
- package/dist/scripts/static-golden-tests.js +165 -0
- package/dist/scripts/time-eval-tests.js +212 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +169 -0
- package/dist/scripts/turn-handoff-collect.js +161 -0
- package/dist/scripts/turn-policy-tests.js +55 -0
- package/dist/scripts/typed-contract-canary.js +176 -0
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +60 -20
- package/dist/scripts/visa-policy-tests.js +62 -0
- package/dist/scripts/weather-tests.js +276 -41
- package/dist/scripts/wish-channel-gate-tests.js +54 -0
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +46 -0
- package/dist/src/artifact-gate.js +829 -0
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +92 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +551 -0
- package/dist/src/benchmark-agent-conformance.js +578 -0
- package/dist/src/benchmark-environment-bridge.js +515 -0
- package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
- package/dist/src/benchmark-tool-isolation.js +151 -0
- package/dist/src/bookable-facts.js +533 -0
- package/dist/src/booking-saga.js +97 -0
- package/dist/src/booking-surface/availability-policy.js +697 -0
- package/dist/src/booking-surface/booking-intent.js +43 -0
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/canonical-schema.js +116 -0
- package/dist/src/booking-surface/contracts.js +75 -0
- package/dist/src/booking-surface/dsh-planner.js +1854 -0
- package/dist/src/booking-surface/dsh-plugin.js +378 -0
- package/dist/src/booking-surface/dsh-worker.js +98 -0
- package/dist/src/booking-surface/error-codes.js +231 -0
- package/dist/src/booking-surface/index.js +9 -0
- package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
- package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
- package/dist/src/booking-surface/profile.js +24 -0
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +1968 -0
- package/dist/src/booking-surface/server.js +674 -0
- package/dist/src/booking-surface/startup.js +168 -0
- package/dist/src/booking-surface/validation.js +261 -0
- package/dist/src/bridge.js +20 -13
- package/dist/src/city-scenario-tier.js +378 -0
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +171 -115
- package/dist/src/engine.js +276 -193
- package/dist/src/evaluation-cadence.js +97 -0
- package/dist/src/evaluation-contracts.js +283 -0
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/flyai-setup-tool.js +70 -0
- package/dist/src/gotry-backend.js +125 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +88 -0
- package/dist/src/index.js +2385 -881
- package/dist/src/itinerary-deck.js +139 -0
- package/dist/src/itinerary-doc-shared.js +661 -0
- package/dist/src/itinerary-html.js +131 -0
- package/dist/src/journey.js +155 -108
- package/dist/src/lavish-tools.js +400 -0
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +331 -161
- package/dist/src/m3-cohort.js +1577 -0
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/outcome-projection.js +746 -0
- package/dist/src/realtime-pricing.js +103 -0
- package/dist/src/recall/card.js +74 -0
- package/dist/src/recall/evaluator.js +140 -0
- package/dist/src/recall/integration.js +17 -0
- package/dist/src/recall/tick.js +166 -0
- package/dist/src/session-link/plan-it-action.js +48 -0
- package/dist/src/session-link/session-link.js +248 -0
- package/dist/src/session-zone-ledger.js +312 -0
- package/dist/src/session-zone-observation.js +354 -0
- package/dist/src/session-zone-wiring.js +658 -0
- package/dist/src/session-zones.js +510 -0
- package/dist/src/share/adapters.js +51 -0
- package/dist/src/share/share-consent.js +76 -0
- package/dist/src/share/share-deck.js +58 -0
- package/dist/src/share/share-token.js +146 -0
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +696 -524
- package/dist/src/state-repair.js +299 -0
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +287 -0
- package/dist/src/turn-policy.js +118 -0
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +600 -360
- package/dist/src/wish-pool.js +42 -12
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +284 -0
- package/extension/README.md +21 -0
- package/extension/README.zh-CN.md +21 -0
- package/extension/background.js +463 -0
- package/extension/content-bridge.js +270 -0
- package/extension/content-main.js +111 -0
- package/extension/manifest.json +60 -0
- package/package.json +407 -14
- package/schemas/booking.intent.schema.json +193 -0
- package/schemas/booking.surface.schema.json +2622 -0
- package/ts/capabilities/agent-reach-deep.ts +114 -0
- package/ts/capabilities/agent-reach.ts +198 -0
- package/ts/capabilities/anything.ts +282 -0
- package/ts/capabilities/artifacts.ts +428 -0
- package/ts/capabilities/fact-log.ts +54 -0
- package/ts/capabilities/flyai-config.ts +388 -0
- package/ts/capabilities/flyai.ts +1274 -0
- package/ts/capabilities/hbcli.ts +253 -71
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/itinerary-artifact.ts +214 -0
- package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
- package/ts/capabilities/itinerary-deck-export.ts +378 -0
- package/ts/capabilities/opensky.ts +121 -0
- package/ts/capabilities/session/action-cache.ts +148 -0
- package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
- package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
- package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
- package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
- package/ts/capabilities/session/benchmark.ts +276 -0
- package/ts/capabilities/session/bridge-ledger.ts +333 -0
- package/ts/capabilities/session/extension-bridge.ts +794 -0
- package/ts/capabilities/session/extension-channel.ts +144 -0
- package/ts/capabilities/session/extension-distribution.ts +266 -0
- package/ts/capabilities/session/extract.ts +54 -0
- package/ts/capabilities/session/golden-score.ts +139 -0
- package/ts/capabilities/session/health-watch.ts +264 -0
- package/ts/capabilities/session/read-guard.ts +141 -0
- package/ts/capabilities/session/static-flight-golden.ts +209 -0
- package/ts/capabilities/session/transport.ts +146 -0
- package/ts/capabilities/session/wizard.ts +140 -0
- package/ts/capabilities/session-consent.ts +211 -0
- package/ts/capabilities/session-login.ts +208 -0
- package/ts/capabilities/session-search.ts +893 -0
- package/ts/capabilities/weather.ts +330 -0
- package/ts/dsh-runtime/vendor/README.md +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -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 +58 -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 +131 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -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 +267 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -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 +23 -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/geo.d.ts +61 -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/staticmap-route.d.ts +31 -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 +75 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
- package/ts/package.json +293 -3
- package/ts/scripts/async-collect.ts +91 -0
- package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
- package/ts/scripts/skeleton-check.ts +1 -1
- package/ts/scripts/skeleton-integration-test.ts +23 -1
- package/ts/scripts/state-cli.ts +586 -0
- package/ts/scripts/turn-handoff-collect.ts +178 -0
- package/ts/src/artifact-gate.ts +991 -0
- package/ts/src/benchmark-agent-conformance.ts +619 -0
- package/ts/src/benchmark-environment-bridge.ts +538 -0
- package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
- package/ts/src/benchmark-tool-isolation.ts +166 -0
- package/ts/src/bookable-facts.ts +837 -0
- package/ts/src/booking-saga.ts +133 -0
- package/ts/src/booking-surface/availability-policy.ts +522 -0
- package/ts/src/booking-surface/booking-intent.ts +86 -0
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/canonical-schema.js +116 -0
- package/ts/src/booking-surface/contracts.ts +475 -0
- package/ts/src/booking-surface/dsh-planner.ts +1949 -0
- package/ts/src/booking-surface/dsh-plugin.js +378 -0
- package/ts/src/booking-surface/dsh-worker.ts +110 -0
- package/ts/src/booking-surface/error-codes.ts +241 -0
- package/ts/src/booking-surface/index.ts +9 -0
- package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
- package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
- package/ts/src/booking-surface/profile.ts +42 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/runtime.ts +1793 -0
- package/ts/src/booking-surface/server.ts +733 -0
- package/ts/src/booking-surface/startup.ts +201 -0
- package/ts/src/booking-surface/validation.ts +217 -0
- package/ts/src/companions.ts +113 -0
- package/ts/src/contracts.ts +17 -3
- package/ts/src/dsh-llm.ts +152 -17
- package/ts/src/flyai-setup-tool.ts +77 -0
- package/ts/src/index.ts +2123 -200
- package/ts/src/itinerary-deck.ts +189 -0
- package/ts/src/itinerary-doc-shared.ts +741 -0
- package/ts/src/itinerary-html.ts +171 -0
- package/ts/src/loop.ts +234 -26
- package/ts/src/memory-capture.ts +283 -0
- package/ts/src/memory-decay.ts +55 -0
- package/ts/src/memory-utility.ts +70 -0
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/recall/card.ts +106 -0
- package/ts/src/recall/evaluator.ts +170 -0
- package/ts/src/recall/integration.ts +35 -0
- package/ts/src/recall/tick.ts +213 -0
- package/ts/src/session-link/plan-it-action.ts +78 -0
- package/ts/src/session-link/session-link.ts +275 -0
- package/ts/src/share/adapters.ts +93 -0
- package/ts/src/share/share-consent.ts +105 -0
- package/ts/src/share/share-deck.ts +85 -0
- package/ts/src/share/share-token.ts +166 -0
- package/ts/src/slot-spec.ts +172 -0
- package/ts/src/state-ledger.ts +1166 -0
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +338 -0
- package/ts/src/tool-packet.ts +32 -0
- package/ts/src/travel-slots.ts +219 -0
- package/ts/src/travel-timeline.ts +83 -0
- package/ts/src/turn-deadline.ts +366 -0
- package/ts/src/turn-policy.ts +154 -0
- package/ts/src/unified.ts +444 -105
- package/ts/src/wish-pool.ts +79 -0
- package/dist/scripts/session-attach-wait.js +0 -41
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-reach 深度能力层:yt-dlp 字幕提取 + gh 仓库搜索(可选工具,系统有就调,没有降级)。
|
|
3
|
+
*
|
|
4
|
+
* 与 agent-reach.ts(r.jina.ai 读网页)互补——dsh LLM 的 3 个 agent-reach 工具:
|
|
5
|
+
* - gotry_web_search → r.jina.ai(已落,零配置)
|
|
6
|
+
* - gotry_video_subtitle → yt-dlp(本文件;系统装了就调,没装降级提示安装方式)
|
|
7
|
+
* - gotry_github_search → gh(本文件;系统装了就调,没装降级提示安装方式)
|
|
8
|
+
*
|
|
9
|
+
* 契约(与 anything/weather/opensky/agent-reach 同构,L4 不变量):
|
|
10
|
+
* - 永不抛错:spawn 失败/超时/工具未装一律降级返回;
|
|
11
|
+
* - 证据链标注:[agent-reach:yt-dlp@ts] / [agent-reach:gh@ts] / [@error@ts] / [@not-installed@ts];
|
|
12
|
+
* - 零强制依赖:不 npm install 任何东西,用系统 PATH 里的二进制。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { spawn } from 'node:child_process'
|
|
16
|
+
|
|
17
|
+
export interface SpawnQuery {
|
|
18
|
+
/** 完整命令参数(不含 bin) */
|
|
19
|
+
args: string[]
|
|
20
|
+
/** 二进制名;默认按能力层定 */
|
|
21
|
+
bin?: string
|
|
22
|
+
/** 超时(ms);默认 30_000 */
|
|
23
|
+
timeoutMs?: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SpawnResult {
|
|
27
|
+
ok: boolean
|
|
28
|
+
/** found / not-installed / error / timeout */
|
|
29
|
+
verdict: 'found' | 'not-installed' | 'error' | 'timeout'
|
|
30
|
+
evidence: string
|
|
31
|
+
latencyMs: number
|
|
32
|
+
stdout?: string
|
|
33
|
+
stderr?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function run(bin: string, args: string[], timeoutMs: number): Promise<{ code: number | null; stdout: string; stderr: string; err?: string; timedOut: boolean }> {
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
const child = spawn(bin, args, { env: process.env })
|
|
39
|
+
let stdout = ''
|
|
40
|
+
let stderr = ''
|
|
41
|
+
let err: string | undefined
|
|
42
|
+
let timedOut = false
|
|
43
|
+
const timer = setTimeout(() => {
|
|
44
|
+
timedOut = true
|
|
45
|
+
try { child.kill('SIGKILL') } catch { /* ignore */ }
|
|
46
|
+
}, timeoutMs)
|
|
47
|
+
child.stdout.on('data', (d: Buffer) => { stdout += d.toString() })
|
|
48
|
+
child.stderr.on('data', (d: Buffer) => { stderr += d.toString() })
|
|
49
|
+
child.on('error', (e) => { err = (e as Error).message })
|
|
50
|
+
child.on('close', (code) => {
|
|
51
|
+
clearTimeout(timer)
|
|
52
|
+
resolve({ code, stdout, stderr, err, timedOut })
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const ts = () => new Date().toISOString()
|
|
58
|
+
|
|
59
|
+
/** yt-dlp 提取视频字幕(YouTube/B站;系统需已装 yt-dlp) */
|
|
60
|
+
export async function videoSubtitle(q: { url: string; lang?: string; timeoutMs?: number }): Promise<SpawnResult & { subtitles?: string }> {
|
|
61
|
+
const started = Date.now()
|
|
62
|
+
const lang = q.lang ?? 'zh-Hans,zh,en'
|
|
63
|
+
const args = ['--write-sub', '--write-auto-sub', '--sub-langs', lang, '--skip-download', '--sub-format', 'vtt', '-o', '/tmp/gotry-sub-%(id)s', q.url]
|
|
64
|
+
const r = await run('yt-dlp', args, q.timeoutMs ?? 60_000)
|
|
65
|
+
const latencyMs = Date.now() - started
|
|
66
|
+
// not-installed: ENOENT
|
|
67
|
+
if (r.err?.includes('ENOENT')) {
|
|
68
|
+
return { ok: false, verdict: 'not-installed', evidence: `[agent-reach:yt-dlp@not-installed@${ts()}]`, latencyMs, stderr: 'yt-dlp 未安装;装法: brew install yt-dlp 或 pip install yt-dlp' }
|
|
69
|
+
}
|
|
70
|
+
if (r.timedOut) {
|
|
71
|
+
return { ok: false, verdict: 'timeout', evidence: `[agent-reach:yt-dlp@error@${ts()}] timeout`, latencyMs }
|
|
72
|
+
}
|
|
73
|
+
if (r.code !== 0) {
|
|
74
|
+
return { ok: false, verdict: 'error', evidence: `[agent-reach:yt-dlp@error@${ts()}] exit ${r.code}`, latencyMs, stderr: r.stderr.slice(0, 400) }
|
|
75
|
+
}
|
|
76
|
+
// 从 stdout 抓写入的字幕文件名
|
|
77
|
+
const m = r.stdout.match(/Writing subtitles to:\s*(\S+)/)
|
|
78
|
+
if (!m) {
|
|
79
|
+
return { ok: false, verdict: 'error', evidence: `[agent-reach:yt-dlp@error@${ts()}] no subtitle file in output`, latencyMs, stderr: r.stdout.slice(0, 300) }
|
|
80
|
+
}
|
|
81
|
+
const { readFile } = await import('node:fs/promises')
|
|
82
|
+
let subtitles: string | undefined
|
|
83
|
+
try {
|
|
84
|
+
subtitles = (await readFile(m[1]!, 'utf-8')).slice(0, 8000)
|
|
85
|
+
} catch { /* 字幕文件读失败保留 undefined */ }
|
|
86
|
+
return { ok: true, verdict: 'found', evidence: `[agent-reach:yt-dlp@${ts()}]`, latencyMs, stdout: r.stdout.slice(0, 400), subtitles }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** gh 搜索 GitHub 仓库(系统需已装 gh 并登录) */
|
|
90
|
+
export async function githubSearch(q: { query: string; limit?: number; timeoutMs?: number }): Promise<SpawnResult & { repos?: Array<{ name: string; description?: string; stars?: number; url?: string }> }> {
|
|
91
|
+
const started = Date.now()
|
|
92
|
+
const limit = q.limit ?? 5
|
|
93
|
+
const args = ['search', 'repos', q.query, '--json', 'fullName,description,stargazersCount,url', '--limit', String(limit)]
|
|
94
|
+
const r = await run('gh', args, q.timeoutMs ?? 30_000)
|
|
95
|
+
const latencyMs = Date.now() - started
|
|
96
|
+
if (r.err?.includes('ENOENT')) {
|
|
97
|
+
return { ok: false, verdict: 'not-installed', evidence: `[agent-reach:gh@not-installed@${ts()}]`, latencyMs, stderr: 'gh 未安装;装法: brew install gh && gh auth login' }
|
|
98
|
+
}
|
|
99
|
+
if (r.timedOut) {
|
|
100
|
+
return { ok: false, verdict: 'timeout', evidence: `[agent-reach:gh@error@${ts()}] timeout`, latencyMs }
|
|
101
|
+
}
|
|
102
|
+
if (r.code !== 0) {
|
|
103
|
+
return { ok: false, verdict: 'error', evidence: `[agent-reach:gh@error@${ts()}] exit ${r.code}`, latencyMs, stderr: r.stderr.slice(0, 300) }
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const repos = JSON.parse(r.stdout) as Array<{ fullName: string; description?: string; stargazersCount?: number; url?: string }>
|
|
107
|
+
return {
|
|
108
|
+
ok: true, verdict: 'found', evidence: `[agent-reach:gh@${ts()}]`, latencyMs,
|
|
109
|
+
repos: repos.map(x => ({ name: x.fullName, description: x.description, stars: x.stargazersCount, url: x.url })),
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
return { ok: false, verdict: 'error', evidence: `[agent-reach:gh@error@${ts()}] parse fail`, latencyMs, stderr: r.stdout.slice(0, 300) }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-reach wrapper —— 上游 Panniantong/Agent-Reach 的薄壳(零渠道知识)。
|
|
3
|
+
*
|
|
4
|
+
* 分工(2026-08-22 founder 纠偏「wrapper 不是 router」):
|
|
5
|
+
* - 知识(渠道清单/方法签名/setup 指引)→ 上游:agent_reach.channels 注册表 + Channel.check() + guides/
|
|
6
|
+
* - 决策(该调哪个渠道哪个方法) → dsh 里的 LLM(靠本 wrapper 的自描述错误拿清单)
|
|
7
|
+
* - 管道(spawn/超时/永不抛错/证据链) → 本文件 + agent-reach-bridge.py,上游加渠道零改动
|
|
8
|
+
*
|
|
9
|
+
* 上游设计意图(Channel 基类原话):"After installation, agents call upstream tools directly."
|
|
10
|
+
* - python-API 渠道(web.read / v2ex.get_hot_topics / xueqiu.get_stock_quote / youtube.transcribe ...)
|
|
11
|
+
* 走本文件 reach() 的反射桥
|
|
12
|
+
* - 外部 CLI 工具渠道(github→gh / youtube 字幕→yt-dlp 等)由专用 dsh 工具(gotry_github_search /
|
|
13
|
+
* gotry_video_subtitle,见 agent-reach-deep.ts)充当执行面;上游无 python API 的不再自行拼命令
|
|
14
|
+
*
|
|
15
|
+
* 契约(L4 不变量,与 anything/weather/opensky 同构):
|
|
16
|
+
* - 永不抛错;verdict: found / not-installed / needs-setup / error
|
|
17
|
+
* - needs-setup 的 setup 文案 = 上游 check() 原话透传,不转述
|
|
18
|
+
* - 证据链 [agent-reach:<channel>.<method>@ts]
|
|
19
|
+
* - 失败人话化(#559):进程级失败与桥结构化错误均走分类文案,不漏 traceback、stderr、凭证样原话;setup(上游 check() 原话)与 inventory(上游注册表)透传契约不变
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { spawn } from 'node:child_process'
|
|
23
|
+
import { existsSync } from 'node:fs'
|
|
24
|
+
import { resolve } from 'node:path'
|
|
25
|
+
|
|
26
|
+
export interface ReachResult {
|
|
27
|
+
channel: string
|
|
28
|
+
method: string
|
|
29
|
+
ok: boolean
|
|
30
|
+
verdict: 'found' | 'not-installed' | 'needs-setup' | 'error'
|
|
31
|
+
evidence: string
|
|
32
|
+
latencyMs: number
|
|
33
|
+
data?: unknown
|
|
34
|
+
/** 上游清单(未知渠道→渠道表;未知方法→方法签名表),LLM 据此自纠 */
|
|
35
|
+
inventory?: Record<string, unknown>
|
|
36
|
+
/** 上游 check() 原话(仅 needs-setup 时) */
|
|
37
|
+
setup?: string
|
|
38
|
+
error?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ts = () => new Date().toISOString()
|
|
42
|
+
const repoRoot = () => resolve(import.meta.dirname, '..', '..')
|
|
43
|
+
const venvPython = () => resolve(repoRoot(), '.venv/bin/python')
|
|
44
|
+
const bridgeScript = () => resolve(import.meta.dirname, 'agent-reach-bridge.py')
|
|
45
|
+
|
|
46
|
+
function run(bin: string, args: string[], timeoutMs: number): Promise<{ code: number | null; stdout: string; stderr: string; err?: string; timedOut: boolean }> {
|
|
47
|
+
return new Promise((resolveRun) => {
|
|
48
|
+
const child = spawn(bin, args, { env: process.env })
|
|
49
|
+
let stdout = ''
|
|
50
|
+
let stderr = ''
|
|
51
|
+
let err: string | undefined
|
|
52
|
+
let timedOut = false
|
|
53
|
+
const timer = setTimeout(() => {
|
|
54
|
+
timedOut = true
|
|
55
|
+
try { child.kill('SIGKILL') } catch { /* ignore */ }
|
|
56
|
+
}, timeoutMs)
|
|
57
|
+
child.stdout.on('data', (d: Buffer) => { stdout += d.toString() })
|
|
58
|
+
child.stderr.on('data', (d: Buffer) => { stderr += d.toString() })
|
|
59
|
+
child.on('error', (e) => { err = (e as Error).message })
|
|
60
|
+
child.on('close', (code) => { clearTimeout(timer); resolveRun({ code, stdout, stderr, err, timedOut }) })
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface BridgeOut {
|
|
65
|
+
ok?: boolean
|
|
66
|
+
data?: unknown
|
|
67
|
+
error?: string
|
|
68
|
+
check?: { status?: string; message?: string }
|
|
69
|
+
channels?: Record<string, string>
|
|
70
|
+
methods?: Record<string, string>
|
|
71
|
+
channel?: Record<string, unknown>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 桥进程级失败人话化(对齐 anything.ts failureReason 口径;issue #559 第五缺口)。
|
|
76
|
+
*
|
|
77
|
+
* 这条消息会直接渲染进用户回复(gotry_agent_reach 的 error 分支、gotry_web_search 的
|
|
78
|
+
* unavailable 括号),所以 Python traceback、上游凭证片段这类桥进程 stderr 原文不能出现——
|
|
79
|
+
* 桥进程失败是可选实时源的降级设计行为,不是工具坏了;无法归类时也要说清发生了什么。
|
|
80
|
+
*/
|
|
81
|
+
function bridgeFailureReason(r: { code: number | null; stdout: string; timedOut: boolean }, timeoutMs: number): string {
|
|
82
|
+
if (r.timedOut) return `agent-reach 反射桥超时(>${timeoutMs}ms,本轮按降级处理;可调大 timeoutMs 或稍后再试)`
|
|
83
|
+
if (!r.stdout.trim()) {
|
|
84
|
+
const why = r.code == null ? '进程异常结束' : `退出码 ${r.code}`
|
|
85
|
+
return `agent-reach 反射桥未返回可判定结果(${why},本轮按降级处理);整体装配状态可用 gotry_doctor 查看`
|
|
86
|
+
}
|
|
87
|
+
return `agent-reach 反射桥输出无法解析(退出码 ${r.code ?? '未知'},本轮按降级处理);整体装配状态可用 gotry_doctor 查看`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** 通用入口:反射调上游任意渠道方法;永不抛错 */
|
|
91
|
+
export async function reach(q: { channel: string; method: string; args?: string[] | string; timeoutMs?: number; /** 测试注入:替换 .venv/bin/python(确定性失败面;与 anything.ts hbcliBin 同构) */ pythonBin?: string }): Promise<ReachResult> {
|
|
92
|
+
const started = Date.now()
|
|
93
|
+
const evidence = (state: string) => `[agent-reach:${q.channel}.${q.method}@${state === 'found' ? ts() : `${state}@${ts()}`}]`
|
|
94
|
+
const base = { channel: q.channel, method: q.method }
|
|
95
|
+
|
|
96
|
+
if (!q.channel || !q.method) {
|
|
97
|
+
return { ...base, ok: false, verdict: 'error', evidence: evidence('error'), latencyMs: 0, error: 'channel 与 method 必填(渠道/方法清单:先随便调一次,inventory 会带回上游清单)' }
|
|
98
|
+
}
|
|
99
|
+
const py = q.pythonBin ?? venvPython()
|
|
100
|
+
if (!existsSync(py)) {
|
|
101
|
+
return { ...base, ok: false, verdict: 'not-installed', evidence: evidence('not-installed'), latencyMs: 0, setup: 'gotry .venv 缺 python——可选依赖未装配。补装:终端跑 npx @danceiny/gotry doctor --fix,或让用户看体检报告 npx @danceiny/gotry doctor' }
|
|
102
|
+
}
|
|
103
|
+
const args = Array.isArray(q.args) ? q.args : (q.args ? q.args.split(/\s+/).filter(Boolean) : [])
|
|
104
|
+
const timeoutMs = q.timeoutMs ?? 30_000
|
|
105
|
+
const r = await run(py, [bridgeScript(), q.channel, q.method, ...args], timeoutMs)
|
|
106
|
+
const latencyMs = Date.now() - started
|
|
107
|
+
|
|
108
|
+
let parsedRaw: unknown
|
|
109
|
+
try {
|
|
110
|
+
parsedRaw = JSON.parse((r.stdout || '').trim().split('\n').filter(Boolean).pop() ?? '{}')
|
|
111
|
+
} catch { /* 走下面的兜底 */ }
|
|
112
|
+
// 形状闸:JSON 合法但不是对象(null/数组/标量)同不可解析处理,永不抛错(#559)
|
|
113
|
+
const parsed: BridgeOut = typeof parsedRaw === 'object' && parsedRaw !== null && !Array.isArray(parsedRaw) ? parsedRaw as BridgeOut : {}
|
|
114
|
+
// 桥结构化 error 是任意上游异常原话(可含凭证 URL/traceback),只在判定分支用,不透传用户面(#559)
|
|
115
|
+
const bridgeError = typeof parsed.error === 'string' && parsed.error.length > 0 ? parsed.error : undefined
|
|
116
|
+
|
|
117
|
+
if (r.err?.includes('ENOENT')) {
|
|
118
|
+
return { ...base, ok: false, verdict: 'not-installed', evidence: evidence('not-installed'), latencyMs, setup: '.venv/bin/python 不可执行——补装:终端跑 npx @danceiny/gotry doctor --fix' }
|
|
119
|
+
}
|
|
120
|
+
if (parsed.ok === true) {
|
|
121
|
+
return { ...base, ok: true, verdict: 'found', evidence: evidence('found'), latencyMs, data: parsed.data }
|
|
122
|
+
}
|
|
123
|
+
// 上游 agent_reach 包未装(bridge 自己的 exit 3 输出)
|
|
124
|
+
if (bridgeError?.includes('agent_reach 未安装')) {
|
|
125
|
+
return { ...base, ok: false, verdict: 'not-installed', evidence: evidence('not-installed'), latencyMs, setup: '.venv 缺 agent-reach 包——补装:终端跑 npx @danceiny/gotry doctor --fix' }
|
|
126
|
+
}
|
|
127
|
+
// 未知渠道/方法:自描述,附上游清单(不拦 LLM,让它看清单自纠);error 用自身查询值给安全
|
|
128
|
+
// 描述——桥报错文案是任意上游文本不透传,清单本体不受影响(#559)
|
|
129
|
+
if (r.code === 2) {
|
|
130
|
+
return {
|
|
131
|
+
...base, ok: false, verdict: 'error', evidence: evidence('error'), latencyMs,
|
|
132
|
+
error: `${q.channel}.${q.method} 不在上游 agent-reach 渠道/方法清单内——按返回 inventory 清单改正渠道名/方法名后重调`,
|
|
133
|
+
inventory: parsed.channels ? { channels: parsed.channels } : { channel: parsed.channel, methods: parsed.methods },
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// 调用抛错:若上游 check() 给出 warn/off,把上游原话作为 setup 透传;error 走分类安全文案
|
|
137
|
+
const checkStatus = parsed.check?.status
|
|
138
|
+
if (checkStatus === 'warn' || checkStatus === 'off') {
|
|
139
|
+
return {
|
|
140
|
+
...base, ok: false, verdict: 'needs-setup', evidence: evidence('needs-setup'), latencyMs,
|
|
141
|
+
error: `${q.channel}.${q.method} 调用失败:上游体检结论为 ${checkStatus}(渠道未就绪),本轮按降级处理;按 setup 指引配置后重试`,
|
|
142
|
+
setup: typeof parsed.check?.message === 'string' ? parsed.check.message : undefined,
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// 桥报 ok:false(不论 error 字段可读与否)都按渠道调用失败分类;桥结构化 error 原话与
|
|
146
|
+
// 进程级失败(stdout 空/半截 JSON/形状不对/超时)都走人话——raw 原话可能带 traceback/
|
|
147
|
+
// 凭证片段,不进用户面;透传契约只剩 setup 与 inventory(#559)
|
|
148
|
+
return {
|
|
149
|
+
...base, ok: false, verdict: 'error', evidence: evidence('error'), latencyMs,
|
|
150
|
+
error: parsed.ok === false
|
|
151
|
+
? `${q.channel}.${q.method} 调用失败:桥报告渠道调用出错但未附可读体检指引,本轮按降级处理;可调同渠道 check() 体检或用 gotry_doctor 查装配状态`
|
|
152
|
+
: bridgeFailureReason(r, timeoutMs),
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** 读网页:委托上游 WebChannel.read(Jina Reader 后端);供 gotry web 读取工具使用 */
|
|
157
|
+
export async function readUrl(q: { url: string; timeoutMs?: number; /** 测试注入:替换 .venv/bin/python(与 reach.pythonBin 同构) */ pythonBin?: string }): Promise<{ ok: boolean; via: string; title?: string; content?: string; evidence: string; latencyMs: number; error?: string }> {
|
|
158
|
+
if (!/^https?:\/\//.test(q.url.trim())) {
|
|
159
|
+
return { ok: false, via: 'r.jina.ai-error', evidence: `[agent-reach:web.read@error@${ts()}] 非法 URL(需 http(s)://)`, latencyMs: 0, error: 'url 必须以 http(s):// 开头' }
|
|
160
|
+
}
|
|
161
|
+
const r = await reach({ channel: 'web', method: 'read', args: [q.url.trim()], timeoutMs: q.timeoutMs, pythonBin: q.pythonBin })
|
|
162
|
+
const content = typeof r.data === 'string' ? r.data.slice(0, 8000) : undefined
|
|
163
|
+
// 上游 Jina Reader 输出首行 "Title: ...",提取为标题(展示层逻辑,非渠道知识)
|
|
164
|
+
const title = content?.match(/^Title: (.+)$/m)?.[1]
|
|
165
|
+
return {
|
|
166
|
+
ok: r.ok, via: r.ok ? 'r.jina.ai' : 'r.jina.ai-error',
|
|
167
|
+
title, content, evidence: r.evidence, latencyMs: r.latencyMs,
|
|
168
|
+
// not-installed/needs-setup 的补装/配置指引住在 setup,不穿过来 gotry_web_search 会渲染 unavailable (undefined)(#559)
|
|
169
|
+
error: r.setup ? [r.error, r.setup].filter(Boolean).join(';') : r.error,
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface ReachStatus {
|
|
174
|
+
ok: boolean
|
|
175
|
+
via: 'agent-reach-cli' | 'not-installed'
|
|
176
|
+
evidence: string
|
|
177
|
+
latencyMs: number
|
|
178
|
+
/** 上游 doctor 原始输出(渠道状态以上游为准,gotry 不自探测) */
|
|
179
|
+
output: string
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** 渠道体检:spawn 真 agent-reach doctor,输出原样透传 */
|
|
183
|
+
export async function reachStatus(timeoutMs = 90_000): Promise<ReachStatus> {
|
|
184
|
+
const started = Date.now()
|
|
185
|
+
const bin = resolve(repoRoot(), '.venv/bin/agent-reach')
|
|
186
|
+
if (!existsSync(bin)) {
|
|
187
|
+
return { ok: false, via: 'not-installed', evidence: `[agent-reach:doctor@not-installed@${ts()}]`, latencyMs: 0, output: 'Agent Reach 未装配(.venv 缺失)——可选依赖,补装:终端跑 npx @danceiny/gotry doctor --fix;或让助手调 gotry_doctor 看整体体检报告' }
|
|
188
|
+
}
|
|
189
|
+
const r = await run(bin, ['doctor'], timeoutMs)
|
|
190
|
+
const output = (r.stdout || r.stderr).trim()
|
|
191
|
+
return {
|
|
192
|
+
ok: !r.err?.includes('ENOENT') && r.code === 0,
|
|
193
|
+
via: 'agent-reach-cli',
|
|
194
|
+
evidence: `[agent-reach:doctor@${ts()}] via .venv/bin/agent-reach`,
|
|
195
|
+
latencyMs: Date.now() - started,
|
|
196
|
+
output: output.slice(0, 4000),
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anything 通用搜索能力层:hbcli → hotel-be Anything 函数。
|
|
3
|
+
*
|
|
4
|
+
* 链路(同构 hbcli.ts):
|
|
5
|
+
* gotry capabilities/anything.ts
|
|
6
|
+
* → spawn hbcli --json search anything <kw> [opts]
|
|
7
|
+
* → hotel-be /api/search/anything(httpdispatch 自动注册;httpdispatch 路由面)
|
|
8
|
+
* → search/service/geography.go func Anything (mixed 城市+酒店搜索)
|
|
9
|
+
*
|
|
10
|
+
* wire 契约(2026-09-07 对齐上游源码;SearchItem = FuzzySearchItem + hotel):
|
|
11
|
+
* req { keyword, contentType?, context.destinationId?, minHotelCount? }
|
|
12
|
+
* resp { candidates: [{ type: 'city'|'place'|'hotel',
|
|
13
|
+
* region?: { id, name:{en,zh,ar}, coordinates:{centerLat,centerLng}, countryCode, … },
|
|
14
|
+
* place?: { latlngCoordinator },
|
|
15
|
+
* matchScore?, hotel?: { id, name:{en,zh,ar}, star, latlngCoordinator:{google|gaode:{lat,lng} } } }] }
|
|
16
|
+
*
|
|
17
|
+
* 三值语义:
|
|
18
|
+
* - hit: 命中候选(混合 city/hotel,maybe-coords)
|
|
19
|
+
* - miss: hbcli exit 0,但 candidates 为空(查无结果)
|
|
20
|
+
* - err: hbcli exit ≠ 0 或超时(降级,降级产物 标 [实时API:hbcli-anything@error@ts])
|
|
21
|
+
*
|
|
22
|
+
* 离线开关(issue #617;与会话通道 GOTRY_SESSION_LIVE 同形,方向相反以保持产品默认不变):
|
|
23
|
+
* GOTRY_HBCLI_LIVE=0|false|off(不分大小写)→ 零 spawn、不碰凭据与后端,走同一条降级面
|
|
24
|
+
* (verdict=error,证据标 [实时API:hbcli-anything@offline@ts]);未设或任何其他取值 = 与此前
|
|
25
|
+
* 完全一致(hbcli 可用即 live)。判定见 hbcliLiveEnabled;mock/dry-run harness(nightly-evidence --dry-run、
|
|
26
|
+
* persona-sim、replay*)缺省把它置 0,operator 显式设置的值不覆盖。
|
|
27
|
+
*
|
|
28
|
+
* 契约(与 hbcli/weather/opensky 同构,L4 不变量):
|
|
29
|
+
* - 永不抛错:网络/超时/解析失败一律降级返回;
|
|
30
|
+
* - 证据链标注:成功 [实时API:hbcli-anything@ts];失败 [实时API:hbcli-anything@error@ts];
|
|
31
|
+
* - 永不阻塞调用方:周边有 12s 默认超时,LLM 不等。
|
|
32
|
+
*
|
|
33
|
+
* 第 7 个能力层(与 hbcli.ts / weather.ts / opensky.ts 平级),data-sources.md §4
|
|
34
|
+
* Google Place 链路的「酒店-be 中间层」入口。
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { hbcliBinCandidates } from './hbcli.ts'
|
|
38
|
+
import { spawnBounded } from './spawn-bounded.ts'
|
|
39
|
+
|
|
40
|
+
export interface AnythingQuery {
|
|
41
|
+
/** 多词以空格 join,与 hbcli argument-parser 一致;前后 trim。空则报错(unless contentType 强限定) */
|
|
42
|
+
keyword: string
|
|
43
|
+
/** 'city' | 'hotel' | undefined(混合) */
|
|
44
|
+
contentType?: 'city' | 'hotel'
|
|
45
|
+
/** 限定子区域 ID(上游 SearchReq.context.destinationId) */
|
|
46
|
+
parentDestinationId?: string | number
|
|
47
|
+
/** 默认 12_000 ms */
|
|
48
|
+
timeoutMs?: number
|
|
49
|
+
/** 显式 hbcli 路径(默认 'hbcli',从 $PATH 找;默认名自动回退 ~/.local/bin 等已知安装位) */
|
|
50
|
+
hbcliBin?: string
|
|
51
|
+
/** 宿主取消信号(exec.signal) */
|
|
52
|
+
signal?: AbortSignal
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface AnythingHit {
|
|
56
|
+
type: 'city' | 'hotel' | 'place'
|
|
57
|
+
/** i18n name 解析(zh 优先,产品中文优先;回退 en/ar) */
|
|
58
|
+
name: string
|
|
59
|
+
/** matchScore(文本相关度,越高越匹配) */
|
|
60
|
+
score?: number
|
|
61
|
+
/** lat/lng(region.center 或 hotel/place latlngCoordinator,若给出) */
|
|
62
|
+
latitude?: number
|
|
63
|
+
longitude?: number
|
|
64
|
+
/** city id(region.id;酒店候选为所属 destinationId) */
|
|
65
|
+
destinationId?: string
|
|
66
|
+
/** hotel id(若有) */
|
|
67
|
+
hotelId?: string
|
|
68
|
+
/** 星级(酒店候选,若返回) */
|
|
69
|
+
star?: number
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface AnythingResult {
|
|
73
|
+
ok: boolean
|
|
74
|
+
via: 'hbcli-anything' | 'hbcli-anything-error'
|
|
75
|
+
evidence: string
|
|
76
|
+
latencyMs: number
|
|
77
|
+
/** 三值 */
|
|
78
|
+
verdict: 'hit' | 'miss' | 'error'
|
|
79
|
+
hits?: AnythingHit[]
|
|
80
|
+
totalCandidates?: number
|
|
81
|
+
error?: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** 上游 i18n.I18N {en,zh,ar};tolerant 读取(字段可能缺省,老字符串形态也兜住) */
|
|
85
|
+
type I18N = { en?: string; zh?: string; ar?: string } | string | undefined
|
|
86
|
+
|
|
87
|
+
function i18nText(v: I18N): string {
|
|
88
|
+
if (!v) return ''
|
|
89
|
+
if (typeof v === 'string') return v
|
|
90
|
+
return v.zh || v.en || v.ar || ''
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** hotel-be Latlng:{lat,lng};google/gaode 双源取先非零者 */
|
|
94
|
+
function latlngOf(coord: { google?: { lat?: number; lng?: number }; gaode?: { lat?: number; lng?: number } } | undefined): { latitude?: number; longitude?: number } {
|
|
95
|
+
for (const src of [coord?.google, coord?.gaode]) {
|
|
96
|
+
const lat = typeof src?.lat === 'number' ? src.lat : undefined
|
|
97
|
+
const lng = typeof src?.lng === 'number' ? src.lng : undefined
|
|
98
|
+
if (lat !== undefined && lng !== undefined && (lat !== 0 || lng !== 0)) return { latitude: lat, longitude: lng }
|
|
99
|
+
}
|
|
100
|
+
return {}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface RawSearchResp {
|
|
104
|
+
candidates?: RawSearchItem[]
|
|
105
|
+
}
|
|
106
|
+
/** hotel-be SearchItem wire 形状(FuzzySearchItem + hotel,2026-09-07 对齐源码) */
|
|
107
|
+
interface RawSearchItem {
|
|
108
|
+
type?: string
|
|
109
|
+
matchScore?: number
|
|
110
|
+
region?: {
|
|
111
|
+
id?: string | number
|
|
112
|
+
name?: I18N
|
|
113
|
+
countryCode?: string
|
|
114
|
+
coordinates?: { centerLat?: number; centerLng?: number }
|
|
115
|
+
}
|
|
116
|
+
place?: { latlngCoordinator?: { google?: { lat?: number; lng?: number }; gaode?: { lat?: number; lng?: number } } }
|
|
117
|
+
hotel?: {
|
|
118
|
+
id?: string | number
|
|
119
|
+
name?: I18N
|
|
120
|
+
star?: number
|
|
121
|
+
destinationId?: string | number
|
|
122
|
+
latlngCoordinator?: { google?: { lat?: number; lng?: number }; gaode?: { lat?: number; lng?: number } }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function sh(cmd: string, args: string[], opts: { timeoutMs: number; env: NodeJS.ProcessEnv; signal?: AbortSignal }) {
|
|
127
|
+
return spawnBounded(cmd, args, { env: opts.env, cwd: process.cwd(), timeoutMs: opts.timeoutMs, signal: opts.signal })
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** --json 必须在子命令前(cli.ts 全局旗标预扫描只认子命令前位置;JSON 错误也走结构化 stderr) */
|
|
131
|
+
function buildArgs(keyword: string, q: AnythingQuery): string[] {
|
|
132
|
+
const args = ['--json', 'search', 'anything', keyword]
|
|
133
|
+
if (q.contentType) args.push('--content-type', q.contentType)
|
|
134
|
+
if (q.parentDestinationId !== undefined) args.push('--destination-id', String(q.parentDestinationId))
|
|
135
|
+
return args
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** 旧版 CLI 无 search anything 子命令时的可行动升级指引(issue #195:裸 unknown command 读起来像工具坏了) */
|
|
139
|
+
function upgradeHint(stderr: string): string | null {
|
|
140
|
+
return /unknown command/i.test(stderr)
|
|
141
|
+
? 'hbcli 版本过旧(无 search anything 子命令)——请升级:hbcli update,或重跑 npx @danceiny/gotry setup'
|
|
142
|
+
: null
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 失败人话化(对齐 hbcli.ts 的 issue #24 口径)。
|
|
147
|
+
*
|
|
148
|
+
* 这条消息会直接渲染进用户回复(loop.ts 的 D-7a PoI 探针)并进入模型面,所以
|
|
149
|
+
* `(exit null)`、`spawn hbcli ENOENT` 这类进程噪音不能出现——hbcli 未装配是
|
|
150
|
+
* 可选实时源的降级设计行为,不是工具坏了;无法归类时也要说清发生了什么。
|
|
151
|
+
*/
|
|
152
|
+
function failureReason(
|
|
153
|
+
r: { error?: string; code?: number | null; stderr?: string; timedOut?: boolean },
|
|
154
|
+
cleanupError: string | undefined,
|
|
155
|
+
): string {
|
|
156
|
+
const suffix = cleanupError ? `;${cleanupError}` : ''
|
|
157
|
+
if (/ENOENT/i.test(r.error ?? '')) return `未安装 hbcli(可选实时源;npx @danceiny/gotry setup 可按官方脚本安装)${suffix}`
|
|
158
|
+
if (r.timedOut) return `hbcli 实时查询超时(本轮按降级处理)${suffix}`
|
|
159
|
+
const detail = (r.stderr ?? '').trim().slice(0, 200)
|
|
160
|
+
if (detail) return `hbcli 报错:${detail}${suffix}`
|
|
161
|
+
return (r.code == null
|
|
162
|
+
? 'hbcli 未返回可判定结果(进程异常结束,本轮按降级处理)'
|
|
163
|
+
: `hbcli 退出码 ${r.code},未返回可判定结果(本轮按降级处理)`) + suffix
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 实时 hbcli 通道是否开启(issue #617)。只有显式写 0 / false / off(不分大小写,忽略首尾空白)
|
|
168
|
+
* 才关闭;未设或任何其他取值都视为开启——终端用户依赖的实时源缺省不丢。调用时读取,
|
|
169
|
+
* 测试与 harness 可在进程内切换。
|
|
170
|
+
*/
|
|
171
|
+
export function hbcliLiveEnabled(env: Partial<Pick<NodeJS.ProcessEnv, 'GOTRY_HBCLI_LIVE'>> = process.env): boolean {
|
|
172
|
+
const v = (env.GOTRY_HBCLI_LIVE ?? '').trim().toLowerCase()
|
|
173
|
+
return v !== '0' && v !== 'false' && v !== 'off'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Anything 通用搜索 — 任何搜索失败走降级;不抛错 */
|
|
177
|
+
export async function anythingSearch(q: AnythingQuery): Promise<AnythingResult> {
|
|
178
|
+
const started = Date.now()
|
|
179
|
+
const ts = new Date().toISOString()
|
|
180
|
+
const kw = (q.keyword ?? '').trim()
|
|
181
|
+
if (!kw) {
|
|
182
|
+
return {
|
|
183
|
+
ok: false, via: 'hbcli-anything-error',
|
|
184
|
+
evidence: `[实时API:hbcli-anything@error@${ts}] keyword empty`,
|
|
185
|
+
latencyMs: Date.now() - started, verdict: 'error',
|
|
186
|
+
error: 'keyword is required',
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const args = buildArgs(kw, q)
|
|
190
|
+
// spawn 级失败(ENOENT)按已知安装位回退(hbcli.ts callHbcliJson 同款);abort/超时/退码无重试无候选
|
|
191
|
+
const candidates = hbcliBinCandidates(q.hbcliBin ?? 'hbcli')
|
|
192
|
+
if (q.signal?.aborted) {
|
|
193
|
+
return {
|
|
194
|
+
ok: false, via: 'hbcli-anything-error',
|
|
195
|
+
evidence: `[实时API:hbcli-anything@abort@${ts}] pre-aborted, zero spawn`,
|
|
196
|
+
latencyMs: 0, verdict: 'error', error: 'aborted by host signal',
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// 离线开关:位置在入参校验与 pre-abort 之后,既有入参的既有结果/原因串逐字节不变;
|
|
200
|
+
// 关闭时与「未装配 hbcli」同形降级(ok:false / hbcli-anything-error / verdict:error + 人话原因,
|
|
201
|
+
// 无进程噪音,也不含 timeout 字样——效应层只重试 timeout 类),且不 spawn、不重试。
|
|
202
|
+
if (!hbcliLiveEnabled()) {
|
|
203
|
+
return {
|
|
204
|
+
ok: false, via: 'hbcli-anything-error',
|
|
205
|
+
evidence: `[实时API:hbcli-anything@offline@${ts}] GOTRY_HBCLI_LIVE switched off; live channel closed, zero spawn`,
|
|
206
|
+
latencyMs: Date.now() - started, verdict: 'error',
|
|
207
|
+
error: 'hbcli 实时通道已关闭(GOTRY_HBCLI_LIVE 离线开关;本轮按降级处理,未发起查询)',
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
let r: Awaited<ReturnType<typeof spawnBounded>> = { code: null, stdout: '', stderr: '', aborted: false, timedOut: false }
|
|
211
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
212
|
+
r = await sh(candidates[i]!, args, { timeoutMs: q.timeoutMs ?? 12_000, env: process.env, signal: q.signal })
|
|
213
|
+
if (!(r.error && /ENOENT/.test(r.error) && !r.aborted && !r.timedOut && i < candidates.length - 1)) break
|
|
214
|
+
}
|
|
215
|
+
const latencyMs = Date.now() - started
|
|
216
|
+
const cleanupError = r.groupReaped === false ? 'process group cleanup incomplete' : undefined
|
|
217
|
+
|
|
218
|
+
if (r.aborted) {
|
|
219
|
+
return {
|
|
220
|
+
ok: false, via: 'hbcli-anything-error',
|
|
221
|
+
evidence: `[实时API:hbcli-anything@abort@${ts}]`,
|
|
222
|
+
latencyMs, verdict: 'error', error: ['aborted by host signal', cleanupError].filter(Boolean).join('; '),
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (r.error || r.groupReaped === false || r.code !== 0) {
|
|
226
|
+
const raw = [r.error, r.stderr.slice(0, 200), cleanupError].filter(Boolean).join('; ') || `(exit ${r.code})`
|
|
227
|
+
return {
|
|
228
|
+
ok: false,
|
|
229
|
+
via: 'hbcli-anything-error',
|
|
230
|
+
// evidence 保留上游原话(溯源契约,anything-tests 锁);error 走人话面
|
|
231
|
+
evidence: `[实时API:hbcli-anything@${r.timedOut ? 'timeout' : 'error'}@${ts}] ${raw}`,
|
|
232
|
+
latencyMs,
|
|
233
|
+
verdict: 'error',
|
|
234
|
+
error: upgradeHint(r.stderr) ?? failureReason(r, cleanupError),
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let data: RawSearchResp
|
|
239
|
+
try {
|
|
240
|
+
const jStart = r.stdout.indexOf('{') >= 0 ? r.stdout.indexOf('{') : r.stdout.indexOf('[')
|
|
241
|
+
const json = jStart >= 0 ? r.stdout.slice(jStart) : r.stdout
|
|
242
|
+
data = JSON.parse(json) as RawSearchResp
|
|
243
|
+
} catch {
|
|
244
|
+
return {
|
|
245
|
+
ok: false, via: 'hbcli-anything-error',
|
|
246
|
+
evidence: `[实时API:hbcli-anything@error@${ts}] parse failed`,
|
|
247
|
+
latencyMs,
|
|
248
|
+
verdict: 'error',
|
|
249
|
+
error: 'failed to parse hbcli output as JSON',
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const rawItems = data.candidates ?? []
|
|
254
|
+
const hits: AnythingHit[] = rawItems.map((it) => {
|
|
255
|
+
const region = it.region ?? {}
|
|
256
|
+
const hotel = it.hotel ?? {}
|
|
257
|
+
const isHotel = it.type === 'hotel' || Boolean(hotel.id)
|
|
258
|
+
const hotelCoords = latlngOf(hotel.latlngCoordinator)
|
|
259
|
+
const placeCoords = latlngOf(it.place?.latlngCoordinator)
|
|
260
|
+
const center = region.coordinates ?? {}
|
|
261
|
+
return {
|
|
262
|
+
type: (isHotel ? 'hotel' : (it.type === 'place' ? 'place' : 'city')) as AnythingHit['type'],
|
|
263
|
+
name: i18nText(hotel.name) || i18nText(region.name) || '?',
|
|
264
|
+
score: it.matchScore,
|
|
265
|
+
latitude: hotelCoords.latitude ?? placeCoords.latitude ?? (typeof center.centerLat === 'number' ? center.centerLat : undefined),
|
|
266
|
+
longitude: hotelCoords.longitude ?? placeCoords.longitude ?? (typeof center.centerLng === 'number' ? center.centerLng : undefined),
|
|
267
|
+
destinationId: hotel.destinationId !== undefined ? String(hotel.destinationId) : (region.id !== undefined ? String(region.id) : undefined),
|
|
268
|
+
hotelId: hotel.id !== undefined ? String(hotel.id) : undefined,
|
|
269
|
+
star: typeof hotel.star === 'number' && hotel.star > 0 ? hotel.star : undefined,
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
const verdict: AnythingResult['verdict'] = hits.length > 0 ? 'hit' : 'miss'
|
|
274
|
+
return {
|
|
275
|
+
ok: true, via: 'hbcli-anything',
|
|
276
|
+
evidence: `[实时API:hbcli-anything@${ts}] ${hits.length}/${rawItems.length} candidates`,
|
|
277
|
+
latencyMs,
|
|
278
|
+
verdict,
|
|
279
|
+
hits,
|
|
280
|
+
totalCandidates: rawItems.length,
|
|
281
|
+
}
|
|
282
|
+
}
|