@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
|
@@ -1,93 +1,163 @@
|
|
|
1
|
-
|
|
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
|
+
import { hbcliBinCandidates } from './hbcli.js';
|
|
37
|
+
import { spawnBounded } from './spawn-bounded.js';
|
|
38
|
+
function i18nText(v) {
|
|
39
|
+
if (!v)
|
|
40
|
+
return '';
|
|
41
|
+
if (typeof v === 'string')
|
|
42
|
+
return v;
|
|
43
|
+
return v.zh || v.en || v.ar || '';
|
|
44
|
+
}
|
|
45
|
+
/** hotel-be Latlng:{lat,lng};google/gaode 双源取先非零者 */
|
|
46
|
+
function latlngOf(coord) {
|
|
47
|
+
for (const src of [coord?.google, coord?.gaode]) {
|
|
48
|
+
const lat = typeof src?.lat === 'number' ? src.lat : undefined;
|
|
49
|
+
const lng = typeof src?.lng === 'number' ? src.lng : undefined;
|
|
50
|
+
if (lat !== undefined && lng !== undefined && (lat !== 0 || lng !== 0))
|
|
51
|
+
return { latitude: lat, longitude: lng };
|
|
52
|
+
}
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
2
55
|
function sh(cmd, args, opts) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
child.stdout?.on('data', (d)=>{
|
|
20
|
-
stdout += d.toString();
|
|
21
|
-
});
|
|
22
|
-
child.stderr?.on('data', (d)=>{
|
|
23
|
-
stderr += d.toString();
|
|
24
|
-
});
|
|
25
|
-
child.on('error', (e)=>{
|
|
26
|
-
error = e.message.slice(0, 200);
|
|
27
|
-
});
|
|
28
|
-
child.on('close', (code)=>{
|
|
29
|
-
clearTimeout(timer);
|
|
30
|
-
if (killTimer) clearTimeout(killTimer);
|
|
31
|
-
resolve({
|
|
32
|
-
code: killed ? -2 : code ?? -1,
|
|
33
|
-
stdout,
|
|
34
|
-
stderr,
|
|
35
|
-
error
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
ctrl.signal.addEventListener('abort', ()=>{
|
|
39
|
-
if (!child) return;
|
|
40
|
-
try {
|
|
41
|
-
child.kill('SIGTERM');
|
|
42
|
-
} catch {}
|
|
43
|
-
killTimer = setTimeout(()=>{
|
|
44
|
-
try {
|
|
45
|
-
child?.kill('SIGKILL');
|
|
46
|
-
} catch {}
|
|
47
|
-
}, 500);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
56
|
+
return spawnBounded(cmd, args, { env: opts.env, cwd: process.cwd(), timeoutMs: opts.timeoutMs, signal: opts.signal });
|
|
57
|
+
}
|
|
58
|
+
/** --json 必须在子命令前(cli.ts 全局旗标预扫描只认子命令前位置;JSON 错误也走结构化 stderr) */
|
|
59
|
+
function buildArgs(keyword, q) {
|
|
60
|
+
const args = ['--json', 'search', 'anything', keyword];
|
|
61
|
+
if (q.contentType)
|
|
62
|
+
args.push('--content-type', q.contentType);
|
|
63
|
+
if (q.parentDestinationId !== undefined)
|
|
64
|
+
args.push('--destination-id', String(q.parentDestinationId));
|
|
65
|
+
return args;
|
|
66
|
+
}
|
|
67
|
+
/** 旧版 CLI 无 search anything 子命令时的可行动升级指引(issue #195:裸 unknown command 读起来像工具坏了) */
|
|
68
|
+
function upgradeHint(stderr) {
|
|
69
|
+
return /unknown command/i.test(stderr)
|
|
70
|
+
? 'hbcli 版本过旧(无 search anything 子命令)——请升级:hbcli update,或重跑 npx @danceiny/gotry setup'
|
|
71
|
+
: null;
|
|
50
72
|
}
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
/**
|
|
74
|
+
* 失败人话化(对齐 hbcli.ts 的 issue #24 口径)。
|
|
75
|
+
*
|
|
76
|
+
* 这条消息会直接渲染进用户回复(loop.ts 的 D-7a PoI 探针)并进入模型面,所以
|
|
77
|
+
* `(exit null)`、`spawn hbcli ENOENT` 这类进程噪音不能出现——hbcli 未装配是
|
|
78
|
+
* 可选实时源的降级设计行为,不是工具坏了;无法归类时也要说清发生了什么。
|
|
79
|
+
*/
|
|
80
|
+
function failureReason(r, cleanupError) {
|
|
81
|
+
const suffix = cleanupError ? `;${cleanupError}` : '';
|
|
82
|
+
if (/ENOENT/i.test(r.error ?? ''))
|
|
83
|
+
return `未安装 hbcli(可选实时源;npx @danceiny/gotry setup 可按官方脚本安装)${suffix}`;
|
|
84
|
+
if (r.timedOut)
|
|
85
|
+
return `hbcli 实时查询超时(本轮按降级处理)${suffix}`;
|
|
86
|
+
const detail = (r.stderr ?? '').trim().slice(0, 200);
|
|
87
|
+
if (detail)
|
|
88
|
+
return `hbcli 报错:${detail}${suffix}`;
|
|
89
|
+
return (r.code == null
|
|
90
|
+
? 'hbcli 未返回可判定结果(进程异常结束,本轮按降级处理)'
|
|
91
|
+
: `hbcli 退出码 ${r.code},未返回可判定结果(本轮按降级处理)`) + suffix;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 实时 hbcli 通道是否开启(issue #617)。只有显式写 0 / false / off(不分大小写,忽略首尾空白)
|
|
95
|
+
* 才关闭;未设或任何其他取值都视为开启——终端用户依赖的实时源缺省不丢。调用时读取,
|
|
96
|
+
* 测试与 harness 可在进程内切换。
|
|
97
|
+
*/
|
|
98
|
+
export function hbcliLiveEnabled(env = process.env) {
|
|
99
|
+
const v = (env.GOTRY_HBCLI_LIVE ?? '').trim().toLowerCase();
|
|
100
|
+
return v !== '0' && v !== 'false' && v !== 'off';
|
|
101
|
+
}
|
|
102
|
+
/** Anything 通用搜索 — 任何搜索失败走降级;不抛错 */
|
|
53
103
|
export async function anythingSearch(q) {
|
|
54
104
|
const started = Date.now();
|
|
55
105
|
const ts = new Date().toISOString();
|
|
56
106
|
const kw = (q.keyword ?? '').trim();
|
|
57
107
|
if (!kw) {
|
|
58
108
|
return {
|
|
59
|
-
ok: false,
|
|
60
|
-
via: 'hbcli-anything-error',
|
|
109
|
+
ok: false, via: 'hbcli-anything-error',
|
|
61
110
|
evidence: `[实时API:hbcli-anything@error@${ts}] keyword empty`,
|
|
62
|
-
latencyMs: Date.now() - started,
|
|
63
|
-
|
|
64
|
-
|
|
111
|
+
latencyMs: Date.now() - started, verdict: 'error',
|
|
112
|
+
error: 'keyword is required',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const args = buildArgs(kw, q);
|
|
116
|
+
// spawn 级失败(ENOENT)按已知安装位回退(hbcli.ts callHbcliJson 同款);abort/超时/退码无重试无候选
|
|
117
|
+
const candidates = hbcliBinCandidates(q.hbcliBin ?? 'hbcli');
|
|
118
|
+
if (q.signal?.aborted) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false, via: 'hbcli-anything-error',
|
|
121
|
+
evidence: `[实时API:hbcli-anything@abort@${ts}] pre-aborted, zero spawn`,
|
|
122
|
+
latencyMs: 0, verdict: 'error', error: 'aborted by host signal',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// 离线开关:位置在入参校验与 pre-abort 之后,既有入参的既有结果/原因串逐字节不变;
|
|
126
|
+
// 关闭时与「未装配 hbcli」同形降级(ok:false / hbcli-anything-error / verdict:error + 人话原因,
|
|
127
|
+
// 无进程噪音,也不含 timeout 字样——效应层只重试 timeout 类),且不 spawn、不重试。
|
|
128
|
+
if (!hbcliLiveEnabled()) {
|
|
129
|
+
return {
|
|
130
|
+
ok: false, via: 'hbcli-anything-error',
|
|
131
|
+
evidence: `[实时API:hbcli-anything@offline@${ts}] GOTRY_HBCLI_LIVE switched off; live channel closed, zero spawn`,
|
|
132
|
+
latencyMs: Date.now() - started, verdict: 'error',
|
|
133
|
+
error: 'hbcli 实时通道已关闭(GOTRY_HBCLI_LIVE 离线开关;本轮按降级处理,未发起查询)',
|
|
65
134
|
};
|
|
66
135
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
kw
|
|
73
|
-
];
|
|
74
|
-
if (q.contentType) args.push('--content-type', q.contentType);
|
|
75
|
-
if (q.parentDestinationId !== undefined) {
|
|
76
|
-
args.push('--parent-destination-id', String(q.parentDestinationId));
|
|
136
|
+
let r = { code: null, stdout: '', stderr: '', aborted: false, timedOut: false };
|
|
137
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
138
|
+
r = await sh(candidates[i], args, { timeoutMs: q.timeoutMs ?? 12_000, env: process.env, signal: q.signal });
|
|
139
|
+
if (!(r.error && /ENOENT/.test(r.error) && !r.aborted && !r.timedOut && i < candidates.length - 1))
|
|
140
|
+
break;
|
|
77
141
|
}
|
|
78
|
-
const r = await sh(bin, args.slice(1), {
|
|
79
|
-
timeoutMs: q.timeoutMs ?? 12_000,
|
|
80
|
-
env: process.env
|
|
81
|
-
});
|
|
82
142
|
const latencyMs = Date.now() - started;
|
|
83
|
-
|
|
143
|
+
const cleanupError = r.groupReaped === false ? 'process group cleanup incomplete' : undefined;
|
|
144
|
+
if (r.aborted) {
|
|
145
|
+
return {
|
|
146
|
+
ok: false, via: 'hbcli-anything-error',
|
|
147
|
+
evidence: `[实时API:hbcli-anything@abort@${ts}]`,
|
|
148
|
+
latencyMs, verdict: 'error', error: ['aborted by host signal', cleanupError].filter(Boolean).join('; '),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (r.error || r.groupReaped === false || r.code !== 0) {
|
|
152
|
+
const raw = [r.error, r.stderr.slice(0, 200), cleanupError].filter(Boolean).join('; ') || `(exit ${r.code})`;
|
|
84
153
|
return {
|
|
85
154
|
ok: false,
|
|
86
155
|
via: 'hbcli-anything-error',
|
|
87
|
-
evidence
|
|
156
|
+
// evidence 保留上游原话(溯源契约,anything-tests 锁);error 走人话面
|
|
157
|
+
evidence: `[实时API:hbcli-anything@${r.timedOut ? 'timeout' : 'error'}@${ts}] ${raw}`,
|
|
88
158
|
latencyMs,
|
|
89
159
|
verdict: 'error',
|
|
90
|
-
error: r.
|
|
160
|
+
error: upgradeHint(r.stderr) ?? failureReason(r, cleanupError),
|
|
91
161
|
};
|
|
92
162
|
}
|
|
93
163
|
let data;
|
|
@@ -95,45 +165,42 @@ export async function anythingSearch(q) {
|
|
|
95
165
|
const jStart = r.stdout.indexOf('{') >= 0 ? r.stdout.indexOf('{') : r.stdout.indexOf('[');
|
|
96
166
|
const json = jStart >= 0 ? r.stdout.slice(jStart) : r.stdout;
|
|
97
167
|
data = JSON.parse(json);
|
|
98
|
-
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
99
170
|
return {
|
|
100
|
-
ok: false,
|
|
101
|
-
via: 'hbcli-anything-error',
|
|
171
|
+
ok: false, via: 'hbcli-anything-error',
|
|
102
172
|
evidence: `[实时API:hbcli-anything@error@${ts}] parse failed`,
|
|
103
173
|
latencyMs,
|
|
104
174
|
verdict: 'error',
|
|
105
|
-
error: 'failed to parse hbcli output as JSON'
|
|
175
|
+
error: 'failed to parse hbcli output as JSON',
|
|
106
176
|
};
|
|
107
177
|
}
|
|
108
178
|
const rawItems = data.candidates ?? [];
|
|
109
|
-
const hits = rawItems.map((it)=>{
|
|
179
|
+
const hits = rawItems.map((it) => {
|
|
110
180
|
const region = it.region ?? {};
|
|
111
181
|
const hotel = it.hotel ?? {};
|
|
112
|
-
const isHotel = it.type === 'hotel' ||
|
|
182
|
+
const isHotel = it.type === 'hotel' || Boolean(hotel.id);
|
|
183
|
+
const hotelCoords = latlngOf(hotel.latlngCoordinator);
|
|
184
|
+
const placeCoords = latlngOf(it.place?.latlngCoordinator);
|
|
185
|
+
const center = region.coordinates ?? {};
|
|
113
186
|
return {
|
|
114
|
-
type: isHotel ? 'hotel' : it.type === 'place'
|
|
115
|
-
name:
|
|
116
|
-
score: it.
|
|
117
|
-
latitude:
|
|
118
|
-
longitude:
|
|
119
|
-
destinationId: region.id,
|
|
120
|
-
hotelId: hotel.id
|
|
187
|
+
type: (isHotel ? 'hotel' : (it.type === 'place' ? 'place' : 'city')),
|
|
188
|
+
name: i18nText(hotel.name) || i18nText(region.name) || '?',
|
|
189
|
+
score: it.matchScore,
|
|
190
|
+
latitude: hotelCoords.latitude ?? placeCoords.latitude ?? (typeof center.centerLat === 'number' ? center.centerLat : undefined),
|
|
191
|
+
longitude: hotelCoords.longitude ?? placeCoords.longitude ?? (typeof center.centerLng === 'number' ? center.centerLng : undefined),
|
|
192
|
+
destinationId: hotel.destinationId !== undefined ? String(hotel.destinationId) : (region.id !== undefined ? String(region.id) : undefined),
|
|
193
|
+
hotelId: hotel.id !== undefined ? String(hotel.id) : undefined,
|
|
194
|
+
star: typeof hotel.star === 'number' && hotel.star > 0 ? hotel.star : undefined,
|
|
121
195
|
};
|
|
122
196
|
});
|
|
123
197
|
const verdict = hits.length > 0 ? 'hit' : 'miss';
|
|
124
198
|
return {
|
|
125
|
-
ok: true,
|
|
126
|
-
via: 'hbcli-anything',
|
|
199
|
+
ok: true, via: 'hbcli-anything',
|
|
127
200
|
evidence: `[实时API:hbcli-anything@${ts}] ${hits.length}/${rawItems.length} candidates`,
|
|
128
201
|
latencyMs,
|
|
129
202
|
verdict,
|
|
130
203
|
hits,
|
|
131
|
-
totalCandidates: rawItems.length
|
|
204
|
+
totalCandidates: rawItems.length,
|
|
132
205
|
};
|
|
133
206
|
}
|
|
134
|
-
export const _ = {
|
|
135
|
-
upperFirst
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/anything.ts
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 产物面(issue #25):agent 生成的文件不再只是「落在本地文件系统里的一个文件名」。
|
|
3
|
+
*
|
|
4
|
+
* 只读能力层,两个纯函数入口(dsh 工具 gotry_artifacts_list / gotry_artifacts_read 的实现):
|
|
5
|
+
* - listArtifacts: 产物发现。权威源 = 账本 workflow_runs(ADR-15;无账本的旧 root 回退
|
|
6
|
+
* 扫描 gotry-state/async/*.deliverable.md 文件视图),外加 dsh 工作目录顶层 *.md
|
|
7
|
+
* (agent 写出的行程/规划文件正落在这里——issue 截图里的 trip-2027-*.md 即此类)
|
|
8
|
+
* 与顶层 *.html/*.htm(issue #441:行程 HTML 产物需先能被发现)。
|
|
9
|
+
* 分页/搜索语义(issue #458):offset(零-based 非负整数)与 search(字面 case-insensitive
|
|
10
|
+
* 子串,匹配 id/title/filename,trim 后空串=不过滤)是先收集全部合格元数据 → 路径 dedupe →
|
|
11
|
+
* 搜索过滤 → updated DESC + (source,id,canonical path) 词典序 tie-break → 按
|
|
12
|
+
* pageSize 切片的全局模型;pageSize 默认 20、上限 50;nextOffset 仅当
|
|
13
|
+
* offset + returned < total;offset 越界返回空数组 + 准确 total + truncated:false +
|
|
14
|
+
* 无 nextOffset;total 反映已过滤集合的真实长度,不假装文件系统快照稳定。
|
|
15
|
+
* - readArtifact: 产物阅读。行窗口(offset/limit)+ 原始行号,输出 dsh read 卡所需的
|
|
16
|
+
* 全部字段({number,text}[] / totalLines / lang),UI 侧渲染为行号文件视图。
|
|
17
|
+
*
|
|
18
|
+
* 纪律:本层只读(不写任何文件;WriteGate 红线不涉及);读取范围白名单 =
|
|
19
|
+
* stateRoot 根 + dsh 工作目录(排除 node_modules/.git),扩展名白名单 =
|
|
20
|
+
* 文本类(md/txt/json/jsonl/csv/log/yaml/yml/html/htm)——本工具是「产物查看」,
|
|
21
|
+
* 不是通用文件浏览器。HTML 在本层只作源码文本读取:不解析标记、不运行脚本/内联事件、
|
|
22
|
+
* 不发起抓取;列表项的主动打开走宿主原生 HTML 预览(客户端以 Open HTML preview 标注
|
|
23
|
+
* 并提示页面脚本可能运行,属宿主 renderer 行为),交互式 Lavish 本地编辑反馈归 #438/#443。
|
|
24
|
+
*/
|
|
25
|
+
import { createHash } from 'node:crypto';
|
|
26
|
+
import { readdir, readFile, realpath, stat } from 'node:fs/promises';
|
|
27
|
+
import { existsSync } from 'node:fs';
|
|
28
|
+
import { isAbsolute, join, resolve, sep } from 'node:path';
|
|
29
|
+
import { openLedgerIfExists } from '../src/state-ledger.js';
|
|
30
|
+
const MAX_LIST = 50;
|
|
31
|
+
const DEFAULT_LIST = 20;
|
|
32
|
+
const MAX_WINDOW = 400;
|
|
33
|
+
const MAX_BYTES = 2 * 1024 * 1024;
|
|
34
|
+
const TEXT_EXT_LANG = {
|
|
35
|
+
md: 'markdown', txt: 'text', json: 'json', jsonl: 'json', csv: 'csv', log: 'text', yaml: 'yaml', yml: 'yaml',
|
|
36
|
+
html: 'html', htm: 'html',
|
|
37
|
+
};
|
|
38
|
+
/** 工作目录顶层可发现的产物扩展名(大小写不敏感);html/htm 只作源码预览。 */
|
|
39
|
+
const CWD_DISCOVER_EXT = /\.(md|html|htm)$/i;
|
|
40
|
+
const DIR_DENY = ['node_modules', '.git'];
|
|
41
|
+
/**
|
|
42
|
+
* Validate a nonnegative-integer pagination knob (`offset`). undefined defaults
|
|
43
|
+
* to 0; safe, integer, ≥0 numbers pass through; null / strings / nonfinite /
|
|
44
|
+
* noninteger / unsafe-integer (e.g. `Number.MAX_SAFE_INTEGER + 1`) values fail
|
|
45
|
+
* closed rather than being silently coerced. Safe-integer is the boundary that
|
|
46
|
+
* matters here: `Number.isInteger` would accept unsafe values whose arithmetic
|
|
47
|
+
* is no longer exact, and slicing into a sorted array with one is a real bug.
|
|
48
|
+
*/
|
|
49
|
+
function normalizeOffset(value, label) {
|
|
50
|
+
if (value === undefined)
|
|
51
|
+
return 0;
|
|
52
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
|
|
53
|
+
throw new Error(`${label} must be a nonnegative integer`);
|
|
54
|
+
}
|
|
55
|
+
if (!Number.isSafeInteger(value)) {
|
|
56
|
+
throw new Error(`${label} must be a safe integer (≤ Number.MAX_SAFE_INTEGER)`);
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Validate and clamp a page-size knob (`limit`). Integer-shaped numbers
|
|
62
|
+
* (including zero / negative, to keep the pre-#458 clamp-on-1 contract) clamp
|
|
63
|
+
* to [1, MAX_LIST]; strings, nonfinite, and noninteger values fail closed
|
|
64
|
+
* without silent coercion. undefined defaults to DEFAULT_LIST.
|
|
65
|
+
*/
|
|
66
|
+
function clampLimit(value) {
|
|
67
|
+
if (value === undefined)
|
|
68
|
+
return DEFAULT_LIST;
|
|
69
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value)) {
|
|
70
|
+
throw new Error(`limit must be a positive integer`);
|
|
71
|
+
}
|
|
72
|
+
return Math.max(1, Math.min(value, MAX_LIST));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validate the optional `search` filter. Strings trim; empty / whitespace-only
|
|
76
|
+
* means no filter (already documented). Anything other than a string fails
|
|
77
|
+
* closed rather than passing through as a silent "no filter".
|
|
78
|
+
*/
|
|
79
|
+
function normalizeSearch(value) {
|
|
80
|
+
if (value === undefined)
|
|
81
|
+
return undefined;
|
|
82
|
+
if (typeof value !== 'string')
|
|
83
|
+
throw new Error(`search must be a string`);
|
|
84
|
+
const trimmed = value.trim();
|
|
85
|
+
return trimmed === '' ? undefined : trimmed;
|
|
86
|
+
}
|
|
87
|
+
function rootOf(stateRoot) {
|
|
88
|
+
return stateRoot === '.' ? process.cwd() : resolve(stateRoot);
|
|
89
|
+
}
|
|
90
|
+
function underRoot(p, root) {
|
|
91
|
+
const r = resolve(root);
|
|
92
|
+
return p === r || p.startsWith(r + sep);
|
|
93
|
+
}
|
|
94
|
+
function hasDeniedSegment(p) {
|
|
95
|
+
return p.split(sep).some(seg => DIR_DENY.includes(seg));
|
|
96
|
+
}
|
|
97
|
+
function asyncDeliverablePath(root, id) {
|
|
98
|
+
return join(root, 'gotry-state', 'async', `${id}.deliverable.md`);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Stable sort key for a list row. Order: `updated` DESC (ISO strings sort
|
|
102
|
+
* lexicographically), then `source` ASC, then `id` ASC, then canonical `path`
|
|
103
|
+
* ASC. The deterministic tie-break matters when many entries share an
|
|
104
|
+
* `updated` value (e.g. equal mtime): pagination would otherwise emit the
|
|
105
|
+
* same id on two adjacent pages. All comparisons are pure codepoint order
|
|
106
|
+
* (no `localeCompare`) so the order is stable across hosts/locales.
|
|
107
|
+
*/
|
|
108
|
+
function compareArtifacts(a, b) {
|
|
109
|
+
const updA = String(a.updated ?? '');
|
|
110
|
+
const updB = String(b.updated ?? '');
|
|
111
|
+
if (updA !== updB)
|
|
112
|
+
return updA < updB ? 1 : -1;
|
|
113
|
+
if (a.source !== b.source)
|
|
114
|
+
return a.source < b.source ? -1 : 1;
|
|
115
|
+
if (a.id !== b.id)
|
|
116
|
+
return a.id < b.id ? -1 : 1;
|
|
117
|
+
return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
|
|
118
|
+
}
|
|
119
|
+
function filenameOf(entry) {
|
|
120
|
+
const lastSep = entry.path.lastIndexOf(sep);
|
|
121
|
+
return lastSep >= 0 ? entry.path.slice(lastSep + 1) : entry.path;
|
|
122
|
+
}
|
|
123
|
+
/** Literal substring predicate (case-insensitive) over id/title/filename. */
|
|
124
|
+
function matchesSearch(entry, needle) {
|
|
125
|
+
const lower = needle.toLowerCase();
|
|
126
|
+
return entry.id.toLowerCase().includes(lower)
|
|
127
|
+
|| entry.title.toLowerCase().includes(lower)
|
|
128
|
+
|| filenameOf(entry).toLowerCase().includes(lower);
|
|
129
|
+
}
|
|
130
|
+
/** 账本 workflow_runs 是权威(listWorkflowRuns 只读 SELECT 直查,不为一个视图改 ledger 类)。 */
|
|
131
|
+
function listRunsFromLedger(root, tenant) {
|
|
132
|
+
const ledger = openLedgerIfExists(root, tenant);
|
|
133
|
+
if (!ledger)
|
|
134
|
+
return [];
|
|
135
|
+
const rows = ledger.db
|
|
136
|
+
.prepare('SELECT id, goal, status, deliverable, updated FROM workflow_runs WHERE tenant_id = ? ORDER BY updated DESC')
|
|
137
|
+
.all(ledger.tenant);
|
|
138
|
+
return rows.map(r => {
|
|
139
|
+
const file = asyncDeliverablePath(root, r.id);
|
|
140
|
+
return {
|
|
141
|
+
source: 'async-run',
|
|
142
|
+
id: r.id,
|
|
143
|
+
title: r.goal,
|
|
144
|
+
path: file,
|
|
145
|
+
status: r.status,
|
|
146
|
+
updated: r.updated,
|
|
147
|
+
bytes: r.deliverable?.length,
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/** 无账本旧 root 的兼容视图:直接扫 async 目录的 deliverable 文件(只读,与清扫合同同一目录)。 */
|
|
152
|
+
async function listDeliverableFiles(root) {
|
|
153
|
+
const dir = join(root, 'gotry-state', 'async');
|
|
154
|
+
let names = [];
|
|
155
|
+
try {
|
|
156
|
+
names = (await readdir(dir)).filter(n => n.endsWith('.deliverable.md'));
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
const entries = [];
|
|
162
|
+
for (const n of names) {
|
|
163
|
+
const p = join(dir, n);
|
|
164
|
+
const st = await stat(p).catch(() => null);
|
|
165
|
+
if (!st?.isFile())
|
|
166
|
+
continue;
|
|
167
|
+
const canonical = await realpath(p).catch(() => null);
|
|
168
|
+
if (!canonical || !underRoot(canonical, root) || hasDeniedSegment(canonical))
|
|
169
|
+
continue;
|
|
170
|
+
entries.push({
|
|
171
|
+
source: 'async-run',
|
|
172
|
+
id: n.replace(/\.deliverable\.md$/, ''),
|
|
173
|
+
title: n.replace(/\.deliverable\.md$/, ''),
|
|
174
|
+
path: p,
|
|
175
|
+
status: existsSync(p.replace(/\.deliverable\.md$/, '.json')) ? 'pending-view' : 'legacy',
|
|
176
|
+
updated: new Date(st.mtimeMs).toISOString(),
|
|
177
|
+
bytes: st.size,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return entries;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* dsh 工作目录顶层可发现产物:md(agent 写出的行程规划等)+ html/htm 行程产物,
|
|
184
|
+
* 扩展名大小写不敏感;非递归,排除 dotfiles。每源在 dedupe/全局排序之前不限
|
|
185
|
+
* 数量——pageSize 由 listArtifacts 在排序后切片,避免 cwd 顶层 mtime 较老的
|
|
186
|
+
* 产物被提前丢失。
|
|
187
|
+
*/
|
|
188
|
+
async function listCwdArtifacts(cwd) {
|
|
189
|
+
let dirents;
|
|
190
|
+
try {
|
|
191
|
+
dirents = await readdir(cwd, { withFileTypes: true });
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
const entries = [];
|
|
197
|
+
for (const d of dirents) {
|
|
198
|
+
if (!d.isFile() || !CWD_DISCOVER_EXT.test(d.name) || d.name.startsWith('.'))
|
|
199
|
+
continue;
|
|
200
|
+
const p = join(cwd, d.name);
|
|
201
|
+
const st = await stat(p).catch(() => null);
|
|
202
|
+
if (!st)
|
|
203
|
+
continue;
|
|
204
|
+
const canonical = await realpath(p).catch(() => null);
|
|
205
|
+
if (!canonical || !underRoot(canonical, cwd) || hasDeniedSegment(canonical))
|
|
206
|
+
continue;
|
|
207
|
+
entries.push({
|
|
208
|
+
source: 'cwd-file',
|
|
209
|
+
id: d.name,
|
|
210
|
+
title: d.name.replace(CWD_DISCOVER_EXT, ''),
|
|
211
|
+
path: p,
|
|
212
|
+
updated: new Date(st.mtimeMs).toISOString(),
|
|
213
|
+
bytes: st.size,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return entries;
|
|
217
|
+
}
|
|
218
|
+
export async function listArtifacts(opts) {
|
|
219
|
+
const limit = clampLimit(opts.limit);
|
|
220
|
+
const offset = normalizeOffset(opts.offset, 'offset');
|
|
221
|
+
const needle = normalizeSearch(opts.search) ?? '';
|
|
222
|
+
const root = rootOf(opts.stateRoot);
|
|
223
|
+
const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();
|
|
224
|
+
const canonicalRoot = await realpath(root).catch(() => root);
|
|
225
|
+
const canonicalCwd = await realpath(cwd).catch(() => cwd);
|
|
226
|
+
// 1) collect all eligible entries per source, without per-source limits —
|
|
227
|
+
// pageSize is applied to the merged/deduped/sorted set so older cwd files
|
|
228
|
+
// stay reachable across pages.
|
|
229
|
+
const collected = [
|
|
230
|
+
...listRunsFromLedger(canonicalRoot, 'local'),
|
|
231
|
+
...(await listDeliverableFiles(canonicalRoot)),
|
|
232
|
+
...(await listCwdArtifacts(canonicalCwd)),
|
|
233
|
+
];
|
|
234
|
+
// 2) canonical-path dedupe (keep ledger authority when both sources point
|
|
235
|
+
// at the same file).
|
|
236
|
+
const seenPath = new Set();
|
|
237
|
+
const deduped = [];
|
|
238
|
+
for (const e of collected) {
|
|
239
|
+
if (seenPath.has(e.path))
|
|
240
|
+
continue;
|
|
241
|
+
seenPath.add(e.path);
|
|
242
|
+
deduped.push(e);
|
|
243
|
+
}
|
|
244
|
+
// 3) literal, case-insensitive substring filter over id/title/filename.
|
|
245
|
+
const filtered = needle === '' ? deduped : deduped.filter(e => matchesSearch(e, needle));
|
|
246
|
+
// 4) deterministic global sort (updated DESC + lexical tie-break) so equal
|
|
247
|
+
// mtimes have a stable page boundary.
|
|
248
|
+
filtered.sort(compareArtifacts);
|
|
249
|
+
// 5) page slice. offset may exceed total — return an empty page with the
|
|
250
|
+
// known total and `truncated:false`.
|
|
251
|
+
const total = filtered.length;
|
|
252
|
+
const start = Math.min(offset, total);
|
|
253
|
+
const end = Math.min(start + limit, total);
|
|
254
|
+
const page = filtered.slice(start, end);
|
|
255
|
+
const hasMore = start + page.length < total;
|
|
256
|
+
const result = {
|
|
257
|
+
artifacts: page,
|
|
258
|
+
total,
|
|
259
|
+
truncated: hasMore,
|
|
260
|
+
limit,
|
|
261
|
+
/** Echo the requested offset (post-validation) — not the clamped page start — so the caller can detect a beyond-end request. */
|
|
262
|
+
offset,
|
|
263
|
+
roots: [canonicalRoot, canonicalCwd],
|
|
264
|
+
};
|
|
265
|
+
if (hasMore)
|
|
266
|
+
result.nextOffset = start + page.length;
|
|
267
|
+
if (needle !== '')
|
|
268
|
+
result.search = needle;
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* 读一个产物。path 三形态:
|
|
273
|
+
* 1. 裸工单 id(无 / 无 .)→ 账本 workflow_runs.deliverable(权威),文件视图缺失也能读;
|
|
274
|
+
* 2. list 返回的绝对/相对路径 → 限定在 stateRoot 根或 dsh 工作目录内(排除 node_modules/.git);
|
|
275
|
+
* 3. 相对文件名 → 先按 dsh 工作目录顶层,再按 gotry-state/async/ 找。
|
|
276
|
+
* 窗口:offset(1 起)/limit(≤400 行);超窗返回 windowed:true,UI 用 read 卡渲染行号视图。
|
|
277
|
+
*/
|
|
278
|
+
export async function readArtifact(opts) {
|
|
279
|
+
const root = rootOf(opts.stateRoot);
|
|
280
|
+
const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();
|
|
281
|
+
const canonicalRoot = await realpath(root).catch(() => root);
|
|
282
|
+
const canonicalCwd = await realpath(cwd).catch(() => cwd);
|
|
283
|
+
const raw = String(opts.path ?? '').trim();
|
|
284
|
+
if (!raw)
|
|
285
|
+
return { ok: false, error: 'path 必填(来自 gotry_artifacts_list 的 path,或异步工单 id)' };
|
|
286
|
+
let text = null;
|
|
287
|
+
let filePath = '';
|
|
288
|
+
// 1) 裸工单 id:账本权威读(文件视图缺失不挡阅读)
|
|
289
|
+
if (!raw.includes('/') && !raw.includes('\\') && !raw.includes('.')) {
|
|
290
|
+
const ledger = openLedgerIfExists(root, 'local');
|
|
291
|
+
const run = ledger?.db
|
|
292
|
+
.prepare('SELECT id, goal, status, deliverable FROM workflow_runs WHERE id = ? AND tenant_id = ?')
|
|
293
|
+
.get(raw, ledger.tenant);
|
|
294
|
+
if (run?.deliverable) {
|
|
295
|
+
text = run.deliverable;
|
|
296
|
+
filePath = asyncDeliverablePath(root, raw);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
const p = asyncDeliverablePath(root, raw);
|
|
300
|
+
const canonical = await realpath(p).catch(() => null);
|
|
301
|
+
if (canonical) {
|
|
302
|
+
if (!underRoot(canonical, canonicalRoot) || hasDeniedSegment(canonical)) {
|
|
303
|
+
return { ok: false, error: `路径越界:${raw}`, hint: `只读 ${root} 与 dsh 工作目录内的文本产物` };
|
|
304
|
+
}
|
|
305
|
+
const ext = canonical.slice(canonical.lastIndexOf('.') + 1).toLowerCase();
|
|
306
|
+
if (!TEXT_EXT_LANG[ext]) {
|
|
307
|
+
return { ok: false, error: `不支持的文件类型 .${ext}`, hint: `白名单:${Object.keys(TEXT_EXT_LANG).join('/')}` };
|
|
308
|
+
}
|
|
309
|
+
const st = await stat(canonical).catch(() => null);
|
|
310
|
+
if (!st?.isFile())
|
|
311
|
+
return { ok: false, error: `文件不存在:${raw}`, hint: '先 gotry_artifacts_list 看在册产物' };
|
|
312
|
+
if (st.size > MAX_BYTES)
|
|
313
|
+
return { ok: false, error: `文件过大(${st.size} bytes > ${MAX_BYTES})` };
|
|
314
|
+
filePath = canonical;
|
|
315
|
+
text = await readFile(canonical, 'utf-8');
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (text === null)
|
|
319
|
+
return { ok: false, error: `工单 ${raw} 无 deliverable(未交付或不存在)`, hint: '先 gotry_artifacts_list 看在册产物' };
|
|
320
|
+
}
|
|
321
|
+
// 2) 路径形态:目录白名单 + 扩展名白名单
|
|
322
|
+
if (text === null) {
|
|
323
|
+
const candidates = isAbsolute(raw) ? [resolve(raw)] : [resolve(cwd, raw), resolve(root, raw), resolve(root, 'gotry-state', 'async', raw)];
|
|
324
|
+
// 预检按 cwd/root 的非 canonical 与 canonical 两种形态放行:list 返回的是 canonical
|
|
325
|
+
// 路径(realpath 后的 cwd),而 macOS 上 /var 与 /private/var 这类符号链接会让
|
|
326
|
+
// canonical 路径不匹配字面 cwd,导致 list→read 串联断裂。边界权威仍是下方
|
|
327
|
+
// realpath 后的 canonical 复检,这里放宽不改变可读集合。
|
|
328
|
+
const inScope = (p) => (underRoot(p, cwd) || underRoot(p, root) || underRoot(p, canonicalCwd) || underRoot(p, canonicalRoot)) && !hasDeniedSegment(p);
|
|
329
|
+
// Prefer an existing candidate so a cwd miss does not mask a valid state-root file.
|
|
330
|
+
const allowed = candidates.find(p => inScope(p) && existsSync(p)) ?? candidates.find(inScope);
|
|
331
|
+
if (!allowed) {
|
|
332
|
+
return { ok: false, error: `路径越界:${raw}`, hint: `只读 ${root} 与 dsh 工作目录内的文本产物` };
|
|
333
|
+
}
|
|
334
|
+
const canonical = await realpath(allowed).catch(() => null);
|
|
335
|
+
if (!canonical) {
|
|
336
|
+
return { ok: false, error: `文件不存在:${raw}`, hint: '先 gotry_artifacts_list 看在册产物' };
|
|
337
|
+
}
|
|
338
|
+
if (!(underRoot(canonical, canonicalCwd) || underRoot(canonical, canonicalRoot)) || hasDeniedSegment(canonical)) {
|
|
339
|
+
return { ok: false, error: `路径越界:${raw}`, hint: `只读 ${root} 与 dsh 工作目录内的文本产物` };
|
|
340
|
+
}
|
|
341
|
+
const ext = canonical.slice(canonical.lastIndexOf('.') + 1).toLowerCase();
|
|
342
|
+
if (!TEXT_EXT_LANG[ext]) {
|
|
343
|
+
return { ok: false, error: `不支持的文件类型 .${ext}`, hint: `白名单:${Object.keys(TEXT_EXT_LANG).join('/')}` };
|
|
344
|
+
}
|
|
345
|
+
const st = await stat(canonical).catch(() => null);
|
|
346
|
+
if (!st?.isFile())
|
|
347
|
+
return { ok: false, error: `文件不存在:${raw}`, hint: '先 gotry_artifacts_list 看在册产物' };
|
|
348
|
+
if (st.size > MAX_BYTES)
|
|
349
|
+
return { ok: false, error: `文件过大(${st.size} bytes > ${MAX_BYTES})` };
|
|
350
|
+
filePath = canonical;
|
|
351
|
+
text = await readFile(canonical, 'utf-8');
|
|
352
|
+
}
|
|
353
|
+
const allLines = text.split('\n');
|
|
354
|
+
const offset = Math.max(1, Math.min(opts.offset ?? 1, allLines.length));
|
|
355
|
+
const limit = Math.max(1, Math.min(opts.limit ?? MAX_WINDOW, MAX_WINDOW));
|
|
356
|
+
const slice = allLines.slice(offset - 1, offset - 1 + limit);
|
|
357
|
+
const ext = filePath.slice(filePath.lastIndexOf('.') + 1).toLowerCase();
|
|
358
|
+
return {
|
|
359
|
+
ok: true,
|
|
360
|
+
source: underRoot(filePath, canonicalRoot) ? 'state-root' : 'cwd',
|
|
361
|
+
path: filePath,
|
|
362
|
+
offset,
|
|
363
|
+
lines: slice.map((t, i) => ({ number: offset + i, text: t })),
|
|
364
|
+
totalLines: allLines.length,
|
|
365
|
+
lang: TEXT_EXT_LANG[ext] ?? 'text',
|
|
366
|
+
version: createHash('sha256').update(text).digest('hex').slice(0, 12),
|
|
367
|
+
content: slice.join('\n'),
|
|
368
|
+
windowed: allLines.length > offset - 1 + slice.length,
|
|
369
|
+
};
|
|
370
|
+
}
|