@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24
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 +102 -194
- package/README.zh-CN.md +115 -210
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-bootstrap.js +466 -35
- package/bin/gotry-inner.js +255 -36
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +2 -2
- package/client/client.js +174 -0
- package/cordis.gotry-patch.yml +70 -93
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +63 -148
- package/dist/capabilities/anything.js +89 -75
- package/dist/capabilities/artifacts.js +123 -104
- package/dist/capabilities/channel-health.js +65 -45
- package/dist/capabilities/channel-registry.js +108 -87
- package/dist/capabilities/doctor.js +357 -235
- package/dist/capabilities/effect.js +223 -309
- package/dist/capabilities/fact-log.js +28 -14
- package/dist/capabilities/flyai.js +245 -156
- package/dist/capabilities/ground-transfer.js +509 -0
- package/dist/capabilities/hbcli.js +144 -148
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/model-override.js +30 -10
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/resilience.js +50 -32
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
- package/dist/capabilities/session/adapters/dida-portal.js +206 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +169 -112
- package/dist/capabilities/session/benchmark.js +85 -149
- package/dist/capabilities/session/extension-bridge.js +320 -259
- package/dist/capabilities/session/extension-channel.js +57 -76
- package/dist/capabilities/session/extension-distribution.js +115 -119
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +55 -33
- package/dist/capabilities/session/health-watch.js +146 -154
- package/dist/capabilities/session/read-guard.js +55 -47
- package/dist/capabilities/session/static-flight-golden.js +49 -36
- package/dist/capabilities/session/transport.js +54 -61
- package/dist/capabilities/session/wizard.js +63 -93
- package/dist/capabilities/session-consent.js +143 -47
- package/dist/capabilities/session-login.js +95 -126
- package/dist/capabilities/session-search.js +391 -253
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +87 -73
- package/dist/capabilities/weather.js +163 -235
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
- package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
- package/dist/scripts/anything-tests.js +58 -62
- package/dist/scripts/artifact-client-contract-tests.js +69 -0
- package/dist/scripts/artifacts-capability-tests.js +235 -0
- package/dist/scripts/async-collect.js +26 -15
- package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
- package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
- package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
- package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
- package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
- package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
- 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 +61 -123
- package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
- package/dist/scripts/bootstrap-tests.js +1383 -128
- package/dist/scripts/build-changelog.js +89 -130
- package/dist/scripts/build-metrics-report.js +135 -143
- package/dist/scripts/changelog-tests.js +35 -82
- package/dist/scripts/channel-probe-tests.js +44 -116
- package/dist/scripts/channel-probe.js +84 -167
- package/dist/scripts/channel-registry-tests.js +97 -185
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +428 -120
- package/dist/scripts/dsh-artifact-e2e.js +150 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
- package/dist/scripts/dsh-runtime-closure.js +75 -63
- package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +165 -552
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +69 -242
- package/dist/scripts/evaluation-contract-tests.js +193 -499
- package/dist/scripts/extension-distribution-cli.js +19 -15
- package/dist/scripts/extension-distribution-tests.js +98 -211
- package/dist/scripts/extension-tests.js +366 -389
- package/dist/scripts/fact-gate-tests.js +725 -263
- 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-tests.js +325 -68
- package/dist/scripts/g5-guard-tests.js +131 -0
- package/dist/scripts/g5-guard.js +189 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +71 -90
- 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 +41 -115
- package/dist/scripts/health-watch-cli.js +42 -28
- 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 +16 -9
- 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 +547 -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/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/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +325 -340
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
- 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 +474 -222
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +62 -199
- package/dist/scripts/model-override-e2e.js +66 -105
- package/dist/scripts/nightly-evidence-tests.js +64 -89
- package/dist/scripts/nightly-evidence.js +108 -111
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/onboarding-tests.js +63 -88
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/persona-surface-guard-tests.js +37 -12
- package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +126 -227
- package/dist/scripts/price-drift-watch.js +209 -253
- package/dist/scripts/probe-poi-tests.js +52 -45
- package/dist/scripts/product-metrics.js +187 -273
- package/dist/scripts/publish-preverify.js +47 -41
- package/dist/scripts/realtime-pricing-tests.js +37 -76
- package/dist/scripts/replay-async.js +20 -18
- package/dist/scripts/replay-real.js +16 -18
- package/dist/scripts/replay.js +52 -82
- package/dist/scripts/session-attach-diagnose.js +16 -12
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +66 -147
- package/dist/scripts/session-dida-live-e2e.js +151 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-login.js +14 -13
- package/dist/scripts/session-tests.js +545 -583
- package/dist/scripts/sf-live-benchmark.js +92 -110
- package/dist/scripts/sf-live-cli-tests.js +2 -9
- package/dist/scripts/sf-soft-score-tests.js +19 -60
- package/dist/scripts/sf-summary-tests.js +149 -0
- package/dist/scripts/sf-summary.js +466 -84
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +11 -10
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +12 -20
- package/dist/scripts/smoke.js +541 -422
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +549 -212
- package/dist/scripts/static-golden-tests.js +46 -180
- package/dist/scripts/time-eval-tests.js +210 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +46 -93
- package/dist/scripts/turn-handoff-collect.js +71 -99
- package/dist/scripts/turn-policy-tests.js +28 -29
- package/dist/scripts/typed-contract-canary.js +77 -162
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +23 -19
- package/dist/scripts/visa-policy-tests.js +31 -44
- package/dist/scripts/weather-tests.js +121 -543
- package/dist/scripts/wish-channel-gate-tests.js +29 -86
- 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 +18 -12
- package/dist/src/artifact-gate.js +652 -221
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +55 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +194 -0
- package/dist/src/benchmark-agent-conformance.js +296 -262
- package/dist/src/benchmark-environment-bridge.js +298 -413
- package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
- package/dist/src/benchmark-tool-isolation.js +94 -67
- package/dist/src/bookable-facts.js +299 -174
- package/dist/src/booking-saga.js +64 -120
- package/dist/src/booking-surface/availability-policy.js +443 -576
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/contracts.js +24 -80
- package/dist/src/booking-surface/dsh-planner.js +510 -281
- package/dist/src/booking-surface/dsh-plugin.js +17 -1
- package/dist/src/booking-surface/error-codes.js +82 -10
- package/dist/src/booking-surface/index.js +0 -3
- package/dist/src/booking-surface/profile.js +10 -54
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +765 -891
- package/dist/src/booking-surface/server.js +200 -251
- package/dist/src/booking-surface/startup.js +58 -54
- package/dist/src/booking-surface/validation.js +150 -218
- package/dist/src/bridge.js +20 -13
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +82 -120
- package/dist/src/engine.js +253 -239
- package/dist/src/evaluation-cadence.js +72 -209
- package/dist/src/evaluation-contracts.js +220 -843
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/gotry-backend.js +70 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +18 -7
- package/dist/src/index.js +1425 -1629
- package/dist/src/journey.js +132 -81
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +257 -166
- 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/realtime-pricing.js +63 -72
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +310 -471
- 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 +121 -127
- package/dist/src/turn-policy.js +61 -89
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +504 -381
- package/dist/src/wish-pool.js +39 -14
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +273 -7
- package/extension/README.md +4 -3
- package/extension/background.js +125 -14
- package/extension/content-bridge.js +23 -1
- package/extension/content-main.js +15 -4
- package/extension/manifest.json +16 -8
- package/package.json +251 -220
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +1 -1
- package/ts/capabilities/artifacts.ts +46 -9
- package/ts/capabilities/flyai.ts +156 -37
- package/ts/capabilities/hbcli.ts +1 -1
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
- package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
- package/ts/capabilities/session/extension-bridge.ts +226 -101
- package/ts/capabilities/session/extension-channel.ts +22 -9
- package/ts/capabilities/session/wizard.ts +2 -2
- package/ts/capabilities/session-consent.ts +98 -14
- package/ts/capabilities/session-login.ts +1 -0
- package/ts/capabilities/session-search.ts +315 -31
- package/ts/dsh-runtime/vendor/README.md +8 -4
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
- package/ts/package.json +236 -16
- package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
- package/ts/scripts/state-cli.ts +418 -51
- package/ts/src/artifact-gate.ts +561 -15
- package/ts/src/benchmark-environment-bridge.ts +37 -20
- package/ts/src/bookable-facts.ts +183 -7
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/dsh-planner.ts +240 -32
- package/ts/src/booking-surface/dsh-plugin.js +17 -1
- package/ts/src/booking-surface/error-codes.ts +75 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/server.ts +84 -51
- package/ts/src/booking-surface/validation.ts +2 -0
- package/ts/src/contracts.ts +12 -2
- package/ts/src/dsh-llm.ts +4 -8
- package/ts/src/index.ts +609 -90
- package/ts/src/loop.ts +116 -7
- package/ts/src/memory-capture.ts +227 -3
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/state-ledger.ts +47 -29
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +217 -13
- package/ts/src/unified.ts +316 -54
|
@@ -1,100 +1,170 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 进程级事故日志(D-NEW):同步、durable 的 JSONL 诊断记录。
|
|
3
|
+
*
|
|
4
|
+
* `uncaughtExceptionMonitor` 只观察 fatal 事故并留下证据;宿主仍决定
|
|
5
|
+
* 是否退出。GoTry 不吞掉 fatal 事件、不调用 process.exit、也不隐式重启。
|
|
6
|
+
* 工具 execute 的业务失败仍由 guardToolExecute 转成结构化失败返回。
|
|
7
|
+
*
|
|
8
|
+
* §11 状态面同步:事故日志是 gotry-state/incidents.jsonl,
|
|
9
|
+
* 用户可见、可清理。
|
|
10
|
+
*/
|
|
11
|
+
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, writeFileSync } from 'node:fs';
|
|
2
12
|
import { dirname, isAbsolute, join } from 'node:path';
|
|
3
|
-
import {
|
|
13
|
+
import { pathToFileURL } from 'node:url';
|
|
14
|
+
/** 解析 gotry-state 绝对路径(stateRoot 可相对也可绝对) */
|
|
4
15
|
export function resolveIncidentsPath(stateRoot, filename = 'incidents.jsonl') {
|
|
5
|
-
const root = isAbsolute(stateRoot)
|
|
16
|
+
const root = isAbsolute(stateRoot)
|
|
17
|
+
? stateRoot
|
|
18
|
+
: join(process.cwd(), stateRoot);
|
|
6
19
|
return join(root, 'gotry-state', filename);
|
|
7
20
|
}
|
|
8
21
|
function ensureDir(path) {
|
|
9
22
|
const dir = dirname(path);
|
|
10
|
-
if (!existsSync(dir))
|
|
11
|
-
recursive: true
|
|
12
|
-
});
|
|
23
|
+
if (!existsSync(dir))
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
13
25
|
}
|
|
14
26
|
let installed = false;
|
|
15
27
|
let logPath = null;
|
|
28
|
+
let activeMonitor = null;
|
|
29
|
+
function safeErrorMessage(value) {
|
|
30
|
+
try {
|
|
31
|
+
if (value instanceof Error)
|
|
32
|
+
return typeof value.message === 'string' ? value.message : '[error-without-message]';
|
|
33
|
+
if (typeof value === 'string')
|
|
34
|
+
return value;
|
|
35
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint' || typeof value === 'symbol')
|
|
36
|
+
return String(value);
|
|
37
|
+
const json = JSON.stringify(value);
|
|
38
|
+
return typeof json === 'string' ? json : '[unprintable-error]';
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return '[unprintable-error]';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function safeErrorStack(value) {
|
|
45
|
+
try {
|
|
46
|
+
const stack = value?.stack;
|
|
47
|
+
return typeof stack === 'string' ? stack : '';
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 写入一条事故并 fsync。整个 append 使用同一个 fd,并保证所有失败路径
|
|
55
|
+
* 都尝试 close;返回 true 只表示写入、fsync、close 全部成功。
|
|
56
|
+
*/
|
|
16
57
|
export function recordIncident(inc, stateRoot) {
|
|
17
|
-
const target = stateRoot ? resolveIncidentsPath(stateRoot) : logPath ?? resolveIncidentsPath('.');
|
|
58
|
+
const target = stateRoot ? resolveIncidentsPath(stateRoot) : (logPath ?? resolveIncidentsPath('.'));
|
|
59
|
+
let fd;
|
|
60
|
+
let closeAttempted = false;
|
|
18
61
|
try {
|
|
19
62
|
ensureDir(target);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
63
|
+
fd = openSync(target, 'a');
|
|
64
|
+
writeFileSync(fd, JSON.stringify(inc) + '\n', { encoding: 'utf8' });
|
|
65
|
+
fsyncSync(fd);
|
|
66
|
+
closeAttempted = true;
|
|
67
|
+
closeSync(fd);
|
|
68
|
+
fd = undefined;
|
|
26
69
|
return true;
|
|
27
|
-
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// 事故处理中的诊断失败绝不能再次抛出。
|
|
28
73
|
return false;
|
|
29
74
|
}
|
|
75
|
+
finally {
|
|
76
|
+
if (fd !== undefined && !closeAttempted) {
|
|
77
|
+
closeAttempted = true;
|
|
78
|
+
try {
|
|
79
|
+
closeSync(fd);
|
|
80
|
+
}
|
|
81
|
+
catch { /* best effort close on a failed write */ }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
30
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* 安装进程级事故观察器。只挂 monitor,不改变 Node/dsh 的 fatal 行为。
|
|
87
|
+
* 进程整个生命周期仅生效一次;disposer 供宿主卸载和测试使用。
|
|
88
|
+
*/
|
|
31
89
|
export function installProcessGuards(stateRoot, labels) {
|
|
32
|
-
if (installed)
|
|
90
|
+
if (installed)
|
|
91
|
+
return () => { };
|
|
33
92
|
installed = true;
|
|
34
93
|
logPath = resolveIncidentsPath(stateRoot);
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
kind
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
94
|
+
const monitor = (err, origin) => {
|
|
95
|
+
try {
|
|
96
|
+
const kind = origin === 'unhandledRejection' ? 'unhandledRejection' : 'uncaughtException';
|
|
97
|
+
const source = kind === 'unhandledRejection'
|
|
98
|
+
? (labels?.unhandledRejection ?? 'promise')
|
|
99
|
+
: (labels?.uncaughtException ?? origin);
|
|
100
|
+
recordIncident({
|
|
101
|
+
ts: new Date().toISOString(),
|
|
102
|
+
kind,
|
|
103
|
+
message: safeErrorMessage(err).slice(0, 2000),
|
|
104
|
+
stack: safeErrorStack(err).slice(0, 4000),
|
|
105
|
+
source,
|
|
106
|
+
}, stateRoot);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Formatting must never replace the original fatal event. Keep a final
|
|
110
|
+
// plain fallback attempt for diagnostics, while the host retains control.
|
|
111
|
+
try {
|
|
112
|
+
recordIncident({
|
|
113
|
+
ts: new Date().toISOString(),
|
|
114
|
+
kind: origin === 'unhandledRejection' ? 'unhandledRejection' : 'uncaughtException',
|
|
115
|
+
message: '[incident-observer-fallback]',
|
|
116
|
+
source: 'incident-observer',
|
|
117
|
+
}, stateRoot);
|
|
118
|
+
}
|
|
119
|
+
catch { /* observer is strictly best effort */ }
|
|
120
|
+
}
|
|
54
121
|
};
|
|
55
|
-
process.on('
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
122
|
+
process.on('uncaughtExceptionMonitor', monitor);
|
|
123
|
+
activeMonitor = monitor;
|
|
124
|
+
let active = true;
|
|
125
|
+
return () => {
|
|
126
|
+
if (!active)
|
|
127
|
+
return;
|
|
128
|
+
active = false;
|
|
129
|
+
if (activeMonitor !== monitor)
|
|
130
|
+
return;
|
|
131
|
+
process.off('uncaughtExceptionMonitor', monitor);
|
|
60
132
|
installed = false;
|
|
133
|
+
logPath = null;
|
|
134
|
+
activeMonitor = null;
|
|
61
135
|
};
|
|
62
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* 工具执行面异常隔离(D-NEW gotry 侧收尾):dsh 的一个工具 execute 抛错/拒绝
|
|
139
|
+
* 会沿 cordis 传到主循环,拖垮整个会话。包装后:降级为结构化错误返回给 LLM、
|
|
140
|
+
* 事故落盘,永不向上抛。落盘失败也不抛(双保险,仍返回结构化错误)。
|
|
141
|
+
*/
|
|
63
142
|
export function guardToolExecute(name, stateRoot, execute) {
|
|
64
|
-
return async (args, exec)=>{
|
|
143
|
+
return async (args, exec) => {
|
|
65
144
|
try {
|
|
66
145
|
return await execute(args, exec);
|
|
67
|
-
}
|
|
68
|
-
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
const message = safeErrorMessage(e);
|
|
69
149
|
recordIncident({
|
|
70
150
|
ts: new Date().toISOString(),
|
|
71
151
|
kind: 'tool_execute_error',
|
|
72
|
-
message: `${name}: ${
|
|
73
|
-
stack: (
|
|
74
|
-
source: name
|
|
152
|
+
message: `${name}: ${message}`.slice(0, 2000),
|
|
153
|
+
stack: safeErrorStack(e).slice(0, 4000),
|
|
154
|
+
source: name,
|
|
75
155
|
}, stateRoot);
|
|
76
156
|
const failure = {
|
|
77
157
|
ok: false,
|
|
78
|
-
summary: `gotry_${name} 内部错误(已隔离,会话继续): ${
|
|
79
|
-
evidence: `[incident:tool_execute_error@${new Date().toISOString()}]
|
|
158
|
+
summary: `gotry_${name} 内部错误(已隔离,会话继续): ${message.slice(0, 300)}`,
|
|
159
|
+
evidence: `[incident:tool_execute_error@${new Date().toISOString()}]`,
|
|
80
160
|
};
|
|
81
161
|
return failure;
|
|
82
162
|
}
|
|
83
163
|
};
|
|
84
164
|
}
|
|
85
|
-
|
|
165
|
+
/** CLI 调试:最小 fatal 子进程会留下事故证据并按 Node 原生语义非零退出。 */
|
|
166
|
+
if (process.argv[1] !== undefined && pathToFileURL(process.argv[1]).href === import.meta.url && process.argv[2] === '--smoke') {
|
|
86
167
|
const stateRoot = process.argv[3] ?? '.';
|
|
87
|
-
installProcessGuards(stateRoot, {
|
|
88
|
-
|
|
89
|
-
unhandledRejection: 'smoke'
|
|
90
|
-
});
|
|
91
|
-
writeFileSync(resolveIncidentsPath(stateRoot) + '.smoke', 'ok');
|
|
92
|
-
setTimeout(()=>Promise.reject(new Error('intentional smoke-rejection')), 50);
|
|
93
|
-
setTimeout(()=>{
|
|
94
|
-
console.log('SMOKE OK');
|
|
95
|
-
process.exit(0);
|
|
96
|
-
}, 500);
|
|
168
|
+
installProcessGuards(stateRoot, { uncaughtException: 'smoke', unhandledRejection: 'smoke' });
|
|
169
|
+
setTimeout(() => { throw new Error('intentional smoke-uncaught'); }, 50);
|
|
97
170
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
//# sourceURL=ts/capabilities/incident-log.ts
|
|
@@ -1,18 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM_MODEL 会话面覆盖(issue #77)。
|
|
3
|
+
*
|
|
4
|
+
* dsh settings 分层:schema 默认 < composition 配置(cordis patch)< 用户层
|
|
5
|
+
*(~/.dsh/settings.yaml)。bin/gotry-inner.js 已把 LLM_MODEL 注入 composition
|
|
6
|
+
* 层(agent-default-model 默认模型 + llm-deepseek 目录),但用户在 dsh web UI
|
|
7
|
+
* 选过模型时,用户层压过 composition 层——单靠 patch,.env 承诺仍落空。
|
|
8
|
+
*
|
|
9
|
+
* 这里在 agent/request 瀑布(dsh-agent-loop 每次发起 LLM 请求前解析
|
|
10
|
+
* provider/model 的闸口,dsh-agent installModelSelection 也在此处施加选择)挂
|
|
11
|
+
* 监听,post-next 覆盖 provider/model:本插件随 cordis patch 在根组装载,先于
|
|
12
|
+
* 任何 agent 创建;瀑布按注册序嵌套(先注册 = 最外层,post-next 最后生效),
|
|
13
|
+
* 因此显式 .env 意图压过一切持久层选择。内存态零持久化,进程退即散,不改写
|
|
14
|
+
* 用户 ~/.dsh 设置。
|
|
15
|
+
*
|
|
16
|
+
* GOTRY_LLM_MODEL 未设置时不挂监听:默认路径(组合配置或用户 web 设置)面不变。
|
|
17
|
+
*
|
|
18
|
+
* @module gotry/capabilities/model-override
|
|
19
|
+
*/
|
|
1
20
|
export const MODEL_OVERRIDE_ENV = 'GOTRY_LLM_MODEL';
|
|
21
|
+
/**
|
|
22
|
+
* 挂载模型覆盖;返回是否实际挂载(未设环境变量或 ctx 无事件总线时为 false)。
|
|
23
|
+
* provider 固定 deepseek-official:llm-deepseek 是 gotry 组合里唯一持有
|
|
24
|
+
* DEEPSEEK_API_KEY/DEEPSEEK_BASE_URL 映射凭证的 provider 路由。
|
|
25
|
+
*/
|
|
2
26
|
export function installModelOverride(ctx, env = process.env) {
|
|
3
27
|
const model = env[MODEL_OVERRIDE_ENV];
|
|
4
|
-
if (!model || typeof ctx.on !== 'function')
|
|
5
|
-
|
|
28
|
+
if (!model || typeof ctx.on !== 'function')
|
|
29
|
+
return false;
|
|
30
|
+
ctx.on('agent/request', async (_payload, next) => {
|
|
6
31
|
const resolved = await next();
|
|
32
|
+
// 与 installModelSelection 同语义:选中项无 effort 时清掉继承值,回落
|
|
33
|
+
// provider 默认推理档——不把上一个模型的 effort 错配到被覆盖的模型上
|
|
7
34
|
const { reasoningEffort: _inherited, ...rest } = resolved;
|
|
8
|
-
return {
|
|
9
|
-
...rest,
|
|
10
|
-
provider: 'deepseek-official',
|
|
11
|
-
model
|
|
12
|
-
};
|
|
35
|
+
return { ...rest, provider: 'deepseek-official', model };
|
|
13
36
|
});
|
|
14
37
|
return true;
|
|
15
38
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//# sourceURL=ts/capabilities/model-override.ts
|
|
@@ -1,33 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenSky 实时观测桥(免费匿名,~400 credits/天,~4 req/s burst):
|
|
3
|
+
* 查某 callsign 的飞机是否在 ADS-B 网络的当前观测里——航线层的实时印证。
|
|
4
|
+
*
|
|
5
|
+
* 契约(与 weather.ts / hbcli.ts 同构):
|
|
6
|
+
* - 永不抛错:网络失败/超时/解析失败一律降级返回;
|
|
7
|
+
* - 三值语义:命中=强肯定;查空≠证伪(ADS-B 覆盖有限,降权不排除);服务不可用=无结论;
|
|
8
|
+
* - 证据链:成功 [实时API:opensky@ts];失败 [实时API:opensky@error@ts]。
|
|
9
|
+
*
|
|
10
|
+
* 接口面(2026-08 实际可达,匿名路径):
|
|
11
|
+
* GET /api/states/all — 当前全球 ADS-B 状态,~10000 架
|
|
12
|
+
* GET /api/states/all?icao24=<hex> — 单飞机
|
|
13
|
+
* /api/flights/airport 历史查 = 需鉴权(匿名 403)
|
|
14
|
+
*
|
|
15
|
+
* 数据意义:与 OpenFlights 骨架(通航性)+ 静态班次包(班期时刻) 三值互补——
|
|
16
|
+
* OpenFlights = 历史上是否通航过(offline)
|
|
17
|
+
* flights_2026.json = 调研估算的班次时刻
|
|
18
|
+
* OpenSky = 该 callsign 的飞机「现在」是否在观测网络里(实时刻正飞)
|
|
19
|
+
*
|
|
20
|
+
* v0.0.1-rc.3 +: 之前是 scripts/opensky-check.ts 一次性 CLI(M2 时设计为 7 天窗
|
|
21
|
+
* 历史校验,但匿名路径实际不可达),现升级为能力层封装 + dsh 插件工具。
|
|
22
|
+
*/
|
|
1
23
|
const BASE = 'https://opensky-network.org/api';
|
|
2
24
|
async function fetchOpenSky(url, timeoutMs) {
|
|
3
25
|
const ctrl = new AbortController();
|
|
4
|
-
const timer = setTimeout(()=>ctrl.abort(), timeoutMs);
|
|
26
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
5
27
|
try {
|
|
6
28
|
const res = await fetch(url, {
|
|
7
29
|
signal: ctrl.signal,
|
|
8
|
-
headers: {
|
|
9
|
-
Accept: 'application/json',
|
|
10
|
-
'User-Agent': 'gotry/0.0.1-rc.3 (+https://github.com/Danceiny/gotry)'
|
|
11
|
-
}
|
|
30
|
+
headers: { Accept: 'application/json', 'User-Agent': 'gotry/0.0.1-rc.3 (+https://github.com/Danceiny/gotry)' },
|
|
12
31
|
});
|
|
13
|
-
if (!res.ok)
|
|
14
|
-
ok: false,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
} catch (e) {
|
|
22
|
-
return {
|
|
23
|
-
ok: false,
|
|
24
|
-
error: e.message.slice(0, 200)
|
|
25
|
-
};
|
|
26
|
-
} finally{
|
|
32
|
+
if (!res.ok)
|
|
33
|
+
return { ok: false, error: `opensky HTTP ${res.status}${res.status === 429 ? ' (rate limited, 400 credits/day anonymous)' : ''}` };
|
|
34
|
+
return { ok: true, data: await res.json() };
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
return { ok: false, error: e.message.slice(0, 200) };
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
27
40
|
clearTimeout(timer);
|
|
28
41
|
}
|
|
29
42
|
}
|
|
30
|
-
const upper = (s)=>s.trim().toUpperCase();
|
|
43
|
+
const upper = (s) => s.trim().toUpperCase();
|
|
31
44
|
export async function verifyFlight(query) {
|
|
32
45
|
const started = Date.now();
|
|
33
46
|
const ts = new Date().toISOString();
|
|
@@ -38,37 +51,35 @@ export async function verifyFlight(query) {
|
|
|
38
51
|
if (!r.ok) {
|
|
39
52
|
return {
|
|
40
53
|
verdict: 'unavailable',
|
|
41
|
-
callsign: cs,
|
|
42
|
-
|
|
43
|
-
sampleSize: 0,
|
|
44
|
-
hits: [],
|
|
54
|
+
callsign: cs, airport,
|
|
55
|
+
sampleSize: 0, hits: [],
|
|
45
56
|
evidence: `[实时API:opensky@error@${ts}] ${r.error}`,
|
|
46
57
|
via: 'opensky-error',
|
|
47
|
-
latencyMs,
|
|
48
|
-
error: r.error
|
|
58
|
+
latencyMs, error: r.error,
|
|
49
59
|
};
|
|
50
60
|
}
|
|
61
|
+
// /api/states/all → { time, states: Array<17 元素数组> }
|
|
62
|
+
// [0 icao24, 1 callsign, 2 origin_country, 3 time_position, 4 last_contact,
|
|
63
|
+
// 5 lon, 6 lat, 7 baro_altitude, 8 on_ground, ...]
|
|
51
64
|
const rawStates = (r.data ?? {}).states ?? [];
|
|
52
|
-
const observed = rawStates
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
const observed = rawStates
|
|
66
|
+
.filter(row => (row[1] ?? '').trim().toUpperCase().includes(cs))
|
|
67
|
+
.map(row => ({
|
|
68
|
+
icao24: String(row[0] ?? ''),
|
|
69
|
+
callsign: (row[1] ?? '').trim() || null,
|
|
70
|
+
lastSeen: Number(row[4] ?? 0),
|
|
71
|
+
firstSeen: Number(row[4] ?? 0),
|
|
72
|
+
estArrivalAirport: airport,
|
|
73
|
+
}));
|
|
59
74
|
const verdict = observed.length > 0 ? 'observed' : 'not_observed';
|
|
60
|
-
const evidence = observed.length > 0
|
|
75
|
+
const evidence = observed.length > 0
|
|
76
|
+
? `[实时API:opensky@${ts}] ✅ 全球 ADS-B 当前观测命中 ${cs} (${observed.length} 架)`
|
|
77
|
+
: `[实时API:opensky@${ts}] ○ 当前 ADS-B 全球观测列表未见 ${cs}(ADS-B 覆盖有限,不作否定结论)`;
|
|
61
78
|
return {
|
|
62
|
-
verdict,
|
|
63
|
-
callsign: cs,
|
|
64
|
-
airport,
|
|
79
|
+
verdict, callsign: cs, airport,
|
|
65
80
|
sampleSize: rawStates.length,
|
|
66
|
-
hits: observed,
|
|
67
|
-
evidence,
|
|
81
|
+
hits: observed, evidence,
|
|
68
82
|
via: 'opensky',
|
|
69
|
-
latencyMs
|
|
83
|
+
latencyMs,
|
|
70
84
|
};
|
|
71
85
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
//# sourceURL=ts/capabilities/opensky.ts
|
|
@@ -1,23 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 外部依赖韧性原语(issue #16 采纳,ADR-18):effect_interpreter.v1 解译器层的横切面。
|
|
3
|
+
*
|
|
4
|
+
* 对外通道的三类故障形态在此归一一处,能力层 handler(永不抛错契约)零改写:
|
|
5
|
+
* - 指数退避重试 withRetry:只对「瞬时类失败」按策略重试,不改变单次失败语义;
|
|
6
|
+
* - 断路器 CircuitBreaker:closed→open→half-open,连续失败达阈值即熔断,
|
|
7
|
+
* 冷却后放行单探测——保护配额与上游,拒绝也是显式结构化观察(不抛错);
|
|
8
|
+
*
|
|
9
|
+
* 策略边界(issue #16 三个解译器主张在本仓的保守落法,详见 docs/design/effect-interpreter.md):
|
|
10
|
+
* - 重试只属于被判定「值得重试」的瞬时失败;风控/挑战/限流类上游明确说「不」的
|
|
11
|
+
* 失败(如 FlyAI Sentinel)永不重试——重试是放大器,不是修复器;
|
|
12
|
+
* - 断路器状态是进程内瞬态(同 session-search 节律闸的先例),重启即重置;
|
|
13
|
+
* 不把瞬态熔断写盘成持久资产。
|
|
14
|
+
*/
|
|
15
|
+
/** 第 failedAttempt 次失败后的等待时长(纯函数,单测锁定 500→1000→2000 封顶链) */
|
|
1
16
|
export function backoffDelayMs(policy, failedAttempt) {
|
|
2
17
|
const raw = policy.baseDelayMs * 2 ** (Math.max(1, failedAttempt) - 1);
|
|
3
18
|
return Math.min(raw, policy.maxDelayMs);
|
|
4
19
|
}
|
|
5
|
-
const defaultSleep = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
|
|
20
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
21
|
+
/**
|
|
22
|
+
* 带指数退避的受控重试:handler 本身永不抛错(能力层契约),这里兼容抛错的
|
|
23
|
+
* 「裸调用方」——异常按 isRetryable(result=null, error) 判定后并入同一计数。
|
|
24
|
+
* 重试只针对瞬时失败;上限到点即返回最后结果,永不向上抛。
|
|
25
|
+
*/
|
|
6
26
|
export async function withRetry(fn, policy, onBackoff) {
|
|
7
27
|
const sleep = policy.sleep ?? defaultSleep;
|
|
8
28
|
const maxAttempts = Math.max(1, policy.maxAttempts);
|
|
9
29
|
let attempts = 0;
|
|
10
30
|
let backoffMs = 0;
|
|
11
|
-
for(;;){
|
|
31
|
+
for (;;) {
|
|
12
32
|
attempts += 1;
|
|
13
33
|
let result = null;
|
|
14
34
|
let error = null;
|
|
15
35
|
try {
|
|
16
36
|
result = await fn();
|
|
17
|
-
}
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
18
39
|
error = e;
|
|
19
40
|
}
|
|
20
|
-
const retryable = policy.isRetryable?.(result, error)
|
|
41
|
+
const retryable = policy.isRetryable?.(result, error)
|
|
42
|
+
?? (error != null || result?.ok === false);
|
|
21
43
|
if (attempts < maxAttempts && retryable) {
|
|
22
44
|
const delay = backoffDelayMs(policy, attempts);
|
|
23
45
|
onBackoff?.(attempts, delay);
|
|
@@ -25,20 +47,22 @@ export async function withRetry(fn, policy, onBackoff) {
|
|
|
25
47
|
await sleep(delay);
|
|
26
48
|
continue;
|
|
27
49
|
}
|
|
28
|
-
return {
|
|
29
|
-
result,
|
|
30
|
-
error,
|
|
31
|
-
attempts,
|
|
32
|
-
backoffMs
|
|
33
|
-
};
|
|
50
|
+
return { result, error, attempts, backoffMs };
|
|
34
51
|
}
|
|
35
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* 断路器(每效应一个实例):
|
|
55
|
+
* closed 常态放行;连续失败达 failureThreshold → open;
|
|
56
|
+
* open 拒绝(new attempts=0,.fail-fast 保护上游/配额);冷却满 openMs → half-open;
|
|
57
|
+
* half-open 仅放行单个探测:onSuccess → closed 清零;onFailure → 重新 open(冷却重启)。
|
|
58
|
+
* 「失败」按效应的 isFailure 语义计数(一次调用一次计数,重试耗尽才计,非每 attempt)。
|
|
59
|
+
*/
|
|
36
60
|
export class CircuitBreaker {
|
|
37
61
|
o;
|
|
38
62
|
consecutiveFailures = 0;
|
|
39
63
|
openedAt = 0;
|
|
40
64
|
probeInFlight = false;
|
|
41
|
-
constructor(o){
|
|
65
|
+
constructor(o) {
|
|
42
66
|
this.o = o;
|
|
43
67
|
}
|
|
44
68
|
threshold() {
|
|
@@ -48,35 +72,32 @@ export class CircuitBreaker {
|
|
|
48
72
|
return (this.o.now ?? Date.now)();
|
|
49
73
|
}
|
|
50
74
|
state() {
|
|
51
|
-
if (this.openedAt > 0 && this.now() - this.openedAt >= this.o.openMs)
|
|
52
|
-
|
|
75
|
+
if (this.openedAt > 0 && this.now() - this.openedAt >= this.o.openMs)
|
|
76
|
+
return 'half-open';
|
|
77
|
+
if (this.openedAt > 0)
|
|
78
|
+
return 'open';
|
|
53
79
|
return 'closed';
|
|
54
80
|
}
|
|
81
|
+
/** 发起一次调用前问闸:open 回 {allowed:false}(调用方须返回显式降级观察,不重试) */
|
|
55
82
|
canAttempt() {
|
|
56
83
|
const s = this.state();
|
|
57
|
-
if (s === 'closed')
|
|
58
|
-
allowed: true,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
state: s
|
|
64
|
-
};
|
|
65
|
-
if (this.probeInFlight) return {
|
|
66
|
-
allowed: false,
|
|
67
|
-
state: s
|
|
68
|
-
};
|
|
84
|
+
if (s === 'closed')
|
|
85
|
+
return { allowed: true, state: s };
|
|
86
|
+
if (s === 'open')
|
|
87
|
+
return { allowed: false, state: s };
|
|
88
|
+
// half-open:单探测语义——已有探测在途拒其余并发
|
|
89
|
+
if (this.probeInFlight)
|
|
90
|
+
return { allowed: false, state: s };
|
|
69
91
|
this.probeInFlight = true;
|
|
70
|
-
return {
|
|
71
|
-
allowed: true,
|
|
72
|
-
state: s
|
|
73
|
-
};
|
|
92
|
+
return { allowed: true, state: s };
|
|
74
93
|
}
|
|
94
|
+
/** 一次解译调用成功(非 isFailure)——清零;half-open 探测成功即回 closed */
|
|
75
95
|
onSuccess() {
|
|
76
96
|
this.consecutiveFailures = 0;
|
|
77
97
|
this.openedAt = 0;
|
|
78
98
|
this.probeInFlight = false;
|
|
79
99
|
}
|
|
100
|
+
/** 一次解译调用失败(含重试耗尽)——半开探测失败 = 重新开闸 */
|
|
80
101
|
onFailure() {
|
|
81
102
|
this.probeInFlight = false;
|
|
82
103
|
this.consecutiveFailures += 1;
|
|
@@ -85,6 +106,3 @@ export class CircuitBreaker {
|
|
|
85
106
|
}
|
|
86
107
|
}
|
|
87
108
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
//# sourceURL=ts/capabilities/resilience.ts
|