botmux 3.18.6 → 3.18.7
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/dist/.runtime-build-id +1 -1
- package/dist/adapters/backend/riff-backend.d.ts +158 -0
- package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
- package/dist/adapters/backend/riff-backend.js +330 -7
- package/dist/adapters/backend/riff-backend.js.map +1 -1
- package/dist/adapters/backend/sandbox.d.ts +42 -0
- package/dist/adapters/backend/sandbox.d.ts.map +1 -1
- package/dist/adapters/backend/sandbox.js +95 -5
- package/dist/adapters/backend/sandbox.js.map +1 -1
- package/dist/adapters/cli/codex-app.d.ts.map +1 -1
- package/dist/adapters/cli/codex-app.js +2 -1
- package/dist/adapters/cli/codex-app.js.map +1 -1
- package/dist/adapters/cli/dsh.d.ts.map +1 -1
- package/dist/adapters/cli/dsh.js +2 -1
- package/dist/adapters/cli/dsh.js.map +1 -1
- package/dist/adapters/cli/mir.d.ts.map +1 -1
- package/dist/adapters/cli/mir.js +2 -1
- package/dist/adapters/cli/mir.js.map +1 -1
- package/dist/adapters/cli/mira.d.ts.map +1 -1
- package/dist/adapters/cli/mira.js +2 -1
- package/dist/adapters/cli/mira.js.map +1 -1
- package/dist/adapters/hook-command.d.ts +3 -5
- package/dist/adapters/hook-command.d.ts.map +1 -1
- package/dist/adapters/hook-command.js +59 -13
- package/dist/adapters/hook-command.js.map +1 -1
- package/dist/autostart.d.ts +76 -0
- package/dist/autostart.d.ts.map +1 -1
- package/dist/autostart.js +105 -28
- package/dist/autostart.js.map +1 -1
- package/dist/bot-registry.d.ts +12 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +43 -3
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/dashboard-command.d.ts +142 -0
- package/dist/cli/dashboard-command.d.ts.map +1 -1
- package/dist/cli/dashboard-command.js +160 -0
- package/dist/cli/dashboard-command.js.map +1 -1
- package/dist/cli/dashboard-endpoint.d.ts +66 -0
- package/dist/cli/dashboard-endpoint.d.ts.map +1 -1
- package/dist/cli/dashboard-endpoint.js +144 -30
- package/dist/cli/dashboard-endpoint.js.map +1 -1
- package/dist/cli/send-dispatch.d.ts +2 -0
- package/dist/cli/send-dispatch.d.ts.map +1 -1
- package/dist/cli/send-dispatch.js +8 -2
- package/dist/cli/send-dispatch.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +270 -38
- package/dist/cli.js.map +1 -1
- package/dist/core/binary-self-update.d.ts +7 -0
- package/dist/core/binary-self-update.d.ts.map +1 -1
- package/dist/core/binary-self-update.js +17 -0
- package/dist/core/binary-self-update.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +52 -1
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/cost-calculator.d.ts +9 -1
- package/dist/core/cost-calculator.d.ts.map +1 -1
- package/dist/core/cost-calculator.js +5 -0
- package/dist/core/cost-calculator.js.map +1 -1
- package/dist/core/passthrough-commands.d.ts.map +1 -1
- package/dist/core/passthrough-commands.js +1 -1
- package/dist/core/passthrough-commands.js.map +1 -1
- package/dist/core/self-spawn.d.ts +46 -1
- package/dist/core/self-spawn.d.ts.map +1 -1
- package/dist/core/self-spawn.js +57 -0
- package/dist/core/self-spawn.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +81 -5
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +93 -0
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/autostart-api.d.ts.map +1 -1
- package/dist/dashboard/autostart-api.js +16 -3
- package/dist/dashboard/autostart-api.js.map +1 -1
- package/dist/dashboard/bot-onboarding.d.ts +2 -1
- package/dist/dashboard/bot-onboarding.d.ts.map +1 -1
- package/dist/dashboard/bot-onboarding.js +22 -6
- package/dist/dashboard/bot-onboarding.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +1 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +3 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts.map +1 -1
- package/dist/dashboard/connector-api.js +28 -0
- package/dist/dashboard/connector-api.js.map +1 -1
- package/dist/dashboard/trigger-api.d.ts.map +1 -1
- package/dist/dashboard/trigger-api.js +33 -21
- package/dist/dashboard/trigger-api.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +12 -2
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +2 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/bot-onboarding.d.ts +40 -1
- package/dist/dashboard/web/bot-onboarding.d.ts.map +1 -1
- package/dist/dashboard/web/bot-onboarding.js +52 -2
- package/dist/dashboard/web/bot-onboarding.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +2 -0
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/webhook-routes.d.ts.map +1 -1
- package/dist/dashboard/webhook-routes.js +272 -30
- package/dist/dashboard/webhook-routes.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/{agent-workbench-dock-page-XHJUYFGN.js → agent-workbench-dock-page-6LC422N6.js} +1 -1
- package/dist/dashboard-web/chunks/{agent-workbench-page-FLKTLIRM.js → agent-workbench-page-RSAWY7O2.js} +1 -1
- package/dist/dashboard-web/chunks/{bot-defaults-page-XMGLZZ2E.js → bot-defaults-page-67RAWCSQ.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-TLG45URB.js → chunk-IA5KMD5O.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-BU3Q6XC6.js → chunk-K232GT3M.js} +2 -2
- package/dist/dashboard-web/chunks/chunk-MMLTCXHP.js +10 -0
- package/dist/dashboard-web/chunks/{chunk-ECFC7UG2.js → chunk-R3LJUCEO.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-Z67OSM5P.js → chunk-YJA3KY3J.js} +1 -1
- package/dist/dashboard-web/chunks/{connectors-page-WDM4CEAU.js → connectors-page-DJCTHSFB.js} +1 -1
- package/dist/dashboard-web/chunks/{feedback-page-GMJ4FLJ4.js → feedback-page-O7SZRKNM.js} +1 -1
- package/dist/dashboard-web/chunks/{groups-page-L4ZJNWVR.js → groups-page-7PONUYHE.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-7LUJU2CX.js → insights-page-J6DAAJ6D.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-7LZUJQPF.js → monitor-room-F3QNEW7N.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-AUSXMPYO.js → monitoring-page-JHDD2VS2.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-6ZU6MAZN.js → office-page-MMTN4IKD.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-QBC5CIYH.js → overview-page-IQ2UJAJP.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-QO3YCA7D.js → roles-page-FNI4MGX6.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-YNGSKDQW.js → schedules-page-6PUU63KS.js} +1 -1
- package/dist/dashboard-web/chunks/{sessions-page-BWR4UB6X.js → sessions-page-O4N72EXT.js} +4 -4
- package/dist/dashboard-web/chunks/{settings-page-2IRIS3FP.js → settings-page-G7B3STUF.js} +1 -1
- package/dist/dashboard-web/chunks/{skills-page-5RZPM6HJ.js → skills-page-CEWKQOIG.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-KERG6LKC.js → team-federation-page-NUP4VK4H.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-766CBD7H.js → v3-page-M4XF6D5Z.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-2M6XZD5C.js → whiteboards-page-L755NZX6.js} +1 -1
- package/dist/dashboard-web/style.css +13 -0
- package/dist/dashboard.js +3 -0
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +3 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +10 -3
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +19 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +19 -1
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/api-gate.d.ts +53 -0
- package/dist/im/lark/api-gate.d.ts.map +1 -0
- package/dist/im/lark/api-gate.js +322 -0
- package/dist/im/lark/api-gate.js.map +1 -0
- package/dist/im/lark/audio-transcribe.d.ts +23 -0
- package/dist/im/lark/audio-transcribe.d.ts.map +1 -0
- package/dist/im/lark/audio-transcribe.js +126 -0
- package/dist/im/lark/audio-transcribe.js.map +1 -0
- package/dist/im/lark/card-builder.d.ts +6 -0
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +53 -1
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +10 -0
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +68 -1
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +247 -224
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +34 -3
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts +16 -0
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +31 -0
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/services/budget-tracker.d.ts +61 -0
- package/dist/services/budget-tracker.d.ts.map +1 -0
- package/dist/services/budget-tracker.js +306 -0
- package/dist/services/budget-tracker.js.map +1 -0
- package/dist/services/connector-store.d.ts +16 -0
- package/dist/services/connector-store.d.ts.map +1 -1
- package/dist/services/connector-store.js.map +1 -1
- package/dist/services/failed-turn-retry.d.ts +43 -0
- package/dist/services/failed-turn-retry.d.ts.map +1 -0
- package/dist/services/failed-turn-retry.js +50 -0
- package/dist/services/failed-turn-retry.js.map +1 -0
- package/dist/services/model-pricing.d.ts +80 -0
- package/dist/services/model-pricing.d.ts.map +1 -0
- package/dist/services/model-pricing.js +182 -0
- package/dist/services/model-pricing.js.map +1 -0
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +13 -3
- package/dist/services/session-store.js.map +1 -1
- package/dist/services/trigger-types.d.ts +10 -0
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/services/usage-ledger.d.ts +10 -0
- package/dist/services/usage-ledger.d.ts.map +1 -1
- package/dist/services/usage-ledger.js +36 -0
- package/dist/services/usage-ledger.js.map +1 -1
- package/dist/services/voice/asr.d.ts +17 -0
- package/dist/services/voice/asr.d.ts.map +1 -0
- package/dist/services/voice/asr.js +64 -0
- package/dist/services/voice/asr.js.map +1 -0
- package/dist/services/voice/index.d.ts +17 -2
- package/dist/services/voice/index.d.ts.map +1 -1
- package/dist/services/voice/index.js +43 -0
- package/dist/services/voice/index.js.map +1 -1
- package/dist/services/voice/types.d.ts +15 -0
- package/dist/services/voice/types.d.ts.map +1 -1
- package/dist/services/webhook-idempotency.d.ts +134 -0
- package/dist/services/webhook-idempotency.d.ts.map +1 -0
- package/dist/services/webhook-idempotency.js +265 -0
- package/dist/services/webhook-idempotency.js.map +1 -0
- package/dist/setup/app-name.d.ts +1 -0
- package/dist/setup/app-name.d.ts.map +1 -1
- package/dist/setup/app-name.js +7 -0
- package/dist/setup/app-name.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts +28 -0
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +73 -0
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/setup/open-platform-automation.d.ts +26 -0
- package/dist/setup/open-platform-automation.d.ts.map +1 -1
- package/dist/setup/open-platform-automation.js +90 -1
- package/dist/setup/open-platform-automation.js.map +1 -1
- package/dist/setup/open-platform-outcome.d.ts.map +1 -1
- package/dist/setup/open-platform-outcome.js +8 -2
- package/dist/setup/open-platform-outcome.js.map +1 -1
- package/dist/types.d.ts +20 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/usage-limit-tracker.d.ts +29 -9
- package/dist/utils/usage-limit-tracker.d.ts.map +1 -1
- package/dist/utils/usage-limit-tracker.js +122 -14
- package/dist/utils/usage-limit-tracker.js.map +1 -1
- package/dist/worker.js +27 -6
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +56 -56
- package/dist/workflows/events/payloads.d.ts +2 -2
- package/dist/workflows/events/schema.d.ts +430 -430
- package/dist/workflows/migration/v2-run-archive-schema.d.ts +2 -2
- package/dist/workflows/v3/run-envelope.d.ts +56 -56
- package/package.json +9 -8
- package/scripts/postinstall-bin.mjs +25 -0
- package/dist/dashboard-web/chunks/chunk-NJCJR3U2.js +0 -10
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget tracker — per-bot monthly cost budget state machine.
|
|
3
|
+
*
|
|
4
|
+
* Sits passively on the usage-ledger write path: the daemon wires a record
|
|
5
|
+
* sink (see usage-ledger.ts `setUsageLedgerRecordSink`) that feeds every
|
|
6
|
+
* positive-delta ledger record's `costCny` here. The tracker accumulates the
|
|
7
|
+
* month's spend per bot in a small state file
|
|
8
|
+
* (`budget-state-<appId>.json`, same per-bot partitioning + temp+rename
|
|
9
|
+
* durability as the ledger's `state-<appId>.json`) and decides when a
|
|
10
|
+
* budget threshold has been crossed.
|
|
11
|
+
*
|
|
12
|
+
* Alerting is edge-triggered and de-duplicated: each threshold fires at most
|
|
13
|
+
* once per month (`alertedThresholds` persisted), and a single jump that
|
|
14
|
+
* crosses several thresholds fires exactly one alert carrying the HIGHEST
|
|
15
|
+
* crossed threshold while marking ALL crossed thresholds as alerted.
|
|
16
|
+
*
|
|
17
|
+
* Pure decision + file I/O: no bot-registry, no lark client. The daemon owns
|
|
18
|
+
* notification delivery (DM to the bot owner) and the hard-stop admission
|
|
19
|
+
* gate; this module only answers "how much have we spent" and "what crossed".
|
|
20
|
+
*
|
|
21
|
+
* 升级注意:升级前的 ledger 记录没有 costCny,状态丢失重建时一律计 0——
|
|
22
|
+
* 预算统计从升级后开始累加,不回溯历史花费。
|
|
23
|
+
*/
|
|
24
|
+
import { mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { logger } from '../utils/logger.js';
|
|
27
|
+
import { defaultLedgerDir } from './usage-ledger.js';
|
|
28
|
+
// ─── Config parsing (loose; garbage in → feature off, never throws) ──────────
|
|
29
|
+
/** Parse a `budget` block from bots.json. `monthlyCny` is the gate: a
|
|
30
|
+
* missing/non-positive/non-finite value disables the feature (null).
|
|
31
|
+
* Thresholds default to [80]; non-finite or out-of-(0,1000] entries are
|
|
32
|
+
* dropped, duplicates removed, ascending order. hardStop defaults false. */
|
|
33
|
+
export function parseBudgetConfig(raw) {
|
|
34
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
35
|
+
return null;
|
|
36
|
+
const r = raw;
|
|
37
|
+
const monthlyCny = typeof r.monthlyCny === 'number' && Number.isFinite(r.monthlyCny) && r.monthlyCny > 0
|
|
38
|
+
? r.monthlyCny
|
|
39
|
+
: null;
|
|
40
|
+
if (monthlyCny === null)
|
|
41
|
+
return null;
|
|
42
|
+
const rawThresholds = Array.isArray(r.alertThresholdPercent) ? r.alertThresholdPercent : [];
|
|
43
|
+
const thresholds = [...new Set(rawThresholds.filter((t) => typeof t === 'number' && Number.isFinite(t) && t > 0 && t <= 1000))].sort((a, b) => a - b);
|
|
44
|
+
return {
|
|
45
|
+
monthlyCny,
|
|
46
|
+
alertThresholdPercent: thresholds.length > 0 ? thresholds : [80],
|
|
47
|
+
hardStop: r.hardStop === true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// ─── State persistence ───────────────────────────────────────────────────────
|
|
51
|
+
function monthOf(now) {
|
|
52
|
+
return now.toISOString().slice(0, 7);
|
|
53
|
+
}
|
|
54
|
+
function freshState(month) {
|
|
55
|
+
return { month, spentCny: 0, alertedThresholds: [] };
|
|
56
|
+
}
|
|
57
|
+
function sanitizeAppId(larkAppId) {
|
|
58
|
+
return larkAppId.replace(/[^A-Za-z0-9_-]/g, '') || 'default';
|
|
59
|
+
}
|
|
60
|
+
function statePath(dir, larkAppId) {
|
|
61
|
+
return join(dir, `budget-state-${sanitizeAppId(larkAppId)}.json`);
|
|
62
|
+
}
|
|
63
|
+
const stateCache = new Map();
|
|
64
|
+
export function _resetBudgetTrackerForTest() {
|
|
65
|
+
stateCache.clear();
|
|
66
|
+
}
|
|
67
|
+
function normalizeState(parsed, month) {
|
|
68
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
69
|
+
return null;
|
|
70
|
+
const p = parsed;
|
|
71
|
+
if (typeof p.month !== 'string' || !/^\d{4}-\d{2}$/.test(p.month))
|
|
72
|
+
return null;
|
|
73
|
+
if (p.month !== month)
|
|
74
|
+
return freshState(month); // lazy rollover
|
|
75
|
+
const spentCny = typeof p.spentCny === 'number' && Number.isFinite(p.spentCny) && p.spentCny >= 0
|
|
76
|
+
? p.spentCny
|
|
77
|
+
: 0;
|
|
78
|
+
const alerted = Array.isArray(p.alertedThresholds)
|
|
79
|
+
? [...new Set(p.alertedThresholds.filter((t) => typeof t === 'number' && Number.isFinite(t)))]
|
|
80
|
+
: [];
|
|
81
|
+
return { month: p.month, spentCny, alertedThresholds: alerted };
|
|
82
|
+
}
|
|
83
|
+
/** Rebuild this month's spend from the daily ledger files when the state
|
|
84
|
+
* file is missing (deleted, or first run after upgrade). Records without
|
|
85
|
+
* costCny (pre-upgrade) count as 0 — historical spend is NOT backfilled. */
|
|
86
|
+
function rebuildBudgetStateFromLedger(dir, larkAppId, month) {
|
|
87
|
+
const state = freshState(month);
|
|
88
|
+
let files;
|
|
89
|
+
try {
|
|
90
|
+
files = readdirSync(dir).filter((f) => f.startsWith(`usage-${month}-`) && f.endsWith('.jsonl'));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return state;
|
|
94
|
+
}
|
|
95
|
+
for (const name of files.sort()) {
|
|
96
|
+
let content;
|
|
97
|
+
try {
|
|
98
|
+
content = readFileSync(join(dir, name), 'utf8');
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
for (const line of content.split('\n')) {
|
|
104
|
+
if (!line.includes(larkAppId))
|
|
105
|
+
continue;
|
|
106
|
+
try {
|
|
107
|
+
const rec = JSON.parse(line);
|
|
108
|
+
if (rec?.larkAppId === larkAppId
|
|
109
|
+
&& typeof rec.costCny === 'number'
|
|
110
|
+
&& Number.isFinite(rec.costCny)
|
|
111
|
+
&& rec.costCny > 0) {
|
|
112
|
+
state.spentCny += rec.costCny;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch { /* skip malformed lines */ }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return state;
|
|
119
|
+
}
|
|
120
|
+
function loadState(dir, larkAppId, month) {
|
|
121
|
+
const path = statePath(dir, larkAppId);
|
|
122
|
+
let fileMtime = null;
|
|
123
|
+
try {
|
|
124
|
+
fileMtime = statSync(path).mtimeMs;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
fileMtime = null;
|
|
128
|
+
}
|
|
129
|
+
if (fileMtime !== null) {
|
|
130
|
+
const cached = stateCache.get(path);
|
|
131
|
+
if (cached && cached.mtimeMs === fileMtime) {
|
|
132
|
+
// Cache hit — still apply lazy rollover on the way out.
|
|
133
|
+
return cached.state.month === month ? cached.state : freshState(month);
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const state = normalizeState(JSON.parse(readFileSync(path, 'utf8')), month);
|
|
137
|
+
if (state) {
|
|
138
|
+
stateCache.set(path, { mtimeMs: fileMtime, state });
|
|
139
|
+
return state;
|
|
140
|
+
}
|
|
141
|
+
// Unparseable shape: fall through to fresh. Do NOT cache — the next
|
|
142
|
+
// successful save overwrites the file.
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Corrupt JSON: same, fall through.
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
// State file missing (or externally deleted): rebuild once from the
|
|
150
|
+
// current month's ledger so a lost state file doesn't lose the month's
|
|
151
|
+
// accumulated spend.
|
|
152
|
+
const rebuilt = rebuildBudgetStateFromLedger(dir, larkAppId, month);
|
|
153
|
+
if (rebuilt.spentCny > 0)
|
|
154
|
+
persistState(dir, larkAppId, rebuilt);
|
|
155
|
+
return rebuilt;
|
|
156
|
+
}
|
|
157
|
+
return freshState(month);
|
|
158
|
+
}
|
|
159
|
+
function persistState(dir, larkAppId, state) {
|
|
160
|
+
mkdirSync(dir, { recursive: true });
|
|
161
|
+
const target = statePath(dir, larkAppId);
|
|
162
|
+
// temp+rename keeps a crash from truncating state; the pid suffix keeps
|
|
163
|
+
// concurrent daemons from stomping each other's tmp file (same scheme as
|
|
164
|
+
// the ledger's state-<appId>.json).
|
|
165
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
166
|
+
writeFileSync(tmp, JSON.stringify(state));
|
|
167
|
+
renameSync(tmp, target);
|
|
168
|
+
try {
|
|
169
|
+
stateCache.set(target, { mtimeMs: statSync(target).mtimeMs, state });
|
|
170
|
+
}
|
|
171
|
+
catch { /* mtime refresh is best-effort */ }
|
|
172
|
+
}
|
|
173
|
+
function resolveOpts(opts) {
|
|
174
|
+
const now = opts?.now ?? new Date();
|
|
175
|
+
return {
|
|
176
|
+
now,
|
|
177
|
+
dir: opts?.ledgerDir ?? defaultLedgerDir(),
|
|
178
|
+
month: monthOf(now),
|
|
179
|
+
budget: opts?.budget ?? null,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Accumulate one positive-delta record's cost into the bot's monthly spend.
|
|
184
|
+
* Returns an alert when this spend newly crosses one or more configured
|
|
185
|
+
* thresholds (highest crossed threshold wins; all crossed thresholds are
|
|
186
|
+
* marked alerted so they never re-fire this month). Returns null otherwise
|
|
187
|
+
* and on ANY error — budget tracking must never break the ledger path.
|
|
188
|
+
*/
|
|
189
|
+
export function trackBudgetSpend(larkAppId, costCny, opts) {
|
|
190
|
+
try {
|
|
191
|
+
if (!larkAppId || typeof larkAppId !== 'string')
|
|
192
|
+
return null;
|
|
193
|
+
if (!Number.isFinite(costCny))
|
|
194
|
+
return null;
|
|
195
|
+
const { dir, month, budget } = resolveOpts(opts);
|
|
196
|
+
const state = loadState(dir, larkAppId, month);
|
|
197
|
+
state.month = month;
|
|
198
|
+
state.spentCny += costCny;
|
|
199
|
+
let alert = null;
|
|
200
|
+
if (budget && budget.monthlyCny > 0) {
|
|
201
|
+
const percent = (state.spentCny / budget.monthlyCny) * 100;
|
|
202
|
+
const effective = [...budget.alertThresholdPercent];
|
|
203
|
+
// hardStop implies a 100% line even when not explicitly configured.
|
|
204
|
+
if (budget.hardStop === true && !effective.includes(100))
|
|
205
|
+
effective.push(100);
|
|
206
|
+
const newlyCrossed = effective.filter((t) => percent >= t && !state.alertedThresholds.includes(t));
|
|
207
|
+
if (newlyCrossed.length > 0) {
|
|
208
|
+
const threshold = Math.max(...newlyCrossed);
|
|
209
|
+
for (const t of effective) {
|
|
210
|
+
if (percent >= t && !state.alertedThresholds.includes(t))
|
|
211
|
+
state.alertedThresholds.push(t);
|
|
212
|
+
}
|
|
213
|
+
alert = {
|
|
214
|
+
larkAppId,
|
|
215
|
+
month,
|
|
216
|
+
spentCny: state.spentCny,
|
|
217
|
+
monthlyCny: budget.monthlyCny,
|
|
218
|
+
percent,
|
|
219
|
+
threshold,
|
|
220
|
+
hardStop: budget.hardStop === true,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
persistState(dir, larkAppId, state);
|
|
225
|
+
return alert;
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
logger.warn(`budget-tracker: trackBudgetSpend failed for ${larkAppId}: ${err?.message ?? err}`);
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** Current month's status. Null only when there is neither a budget nor any
|
|
233
|
+
* recorded spend. With a budget but no spend → zero values (monthlyCny and
|
|
234
|
+
* percent present). With spend but no budget → monthlyCny/percent omitted. */
|
|
235
|
+
export function getBudgetStatus(larkAppId, opts) {
|
|
236
|
+
try {
|
|
237
|
+
if (!larkAppId || typeof larkAppId !== 'string')
|
|
238
|
+
return null;
|
|
239
|
+
const { dir, month, budget } = resolveOpts(opts);
|
|
240
|
+
const state = loadState(dir, larkAppId, month);
|
|
241
|
+
if (!budget && state.spentCny <= 0)
|
|
242
|
+
return null;
|
|
243
|
+
const status = {
|
|
244
|
+
month: state.month,
|
|
245
|
+
spentCny: state.spentCny,
|
|
246
|
+
hardStop: budget?.hardStop === true,
|
|
247
|
+
};
|
|
248
|
+
if (budget && budget.monthlyCny > 0) {
|
|
249
|
+
status.monthlyCny = budget.monthlyCny;
|
|
250
|
+
status.percent = (state.spentCny / budget.monthlyCny) * 100;
|
|
251
|
+
}
|
|
252
|
+
return status;
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
logger.warn(`budget-tracker: getBudgetStatus failed for ${larkAppId}: ${err?.message ?? err}`);
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/** Whether new turns should be refused for this bot this month. Fails OPEN:
|
|
260
|
+
* any error (unreadable state, bad config) returns false.
|
|
261
|
+
*
|
|
262
|
+
* ⚠️ 判定原语已就绪但**尚未接入任何 turn 准入路径**——目前没有调用方,
|
|
263
|
+
* 所以 `hardStop: true` 只影响告警口径(隐含 100% 阈值),不会真的拦下
|
|
264
|
+
* 任务。要落地拦截需在 daemon 的入站入口(handleNewTopicAdmitted /
|
|
265
|
+
* handleThreadReplyAdmitted)调用本函数并回复用户;那是全 bot 共用的最热
|
|
266
|
+
* 路径,属独立改动。改动时请同步 formatBudgetAlert 的文案口径。 */
|
|
267
|
+
export function isBudgetHardStopped(larkAppId, opts) {
|
|
268
|
+
try {
|
|
269
|
+
if (!larkAppId || typeof larkAppId !== 'string')
|
|
270
|
+
return false;
|
|
271
|
+
const budget = opts?.budget ?? null;
|
|
272
|
+
if (!budget || budget.hardStop !== true || !(budget.monthlyCny > 0))
|
|
273
|
+
return false;
|
|
274
|
+
const { dir, month } = resolveOpts(opts);
|
|
275
|
+
const state = loadState(dir, larkAppId, month);
|
|
276
|
+
if (state.month !== month)
|
|
277
|
+
return false; // rolled over before we persisted
|
|
278
|
+
return state.spentCny >= budget.monthlyCny;
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function formatMoney(v) {
|
|
285
|
+
return v.toFixed(2);
|
|
286
|
+
}
|
|
287
|
+
function formatThreshold(v) {
|
|
288
|
+
// Thresholds are config numbers (e.g. 80, 80.5); trim float noise.
|
|
289
|
+
return String(Math.round(v * 100) / 100);
|
|
290
|
+
}
|
|
291
|
+
/** Plain-text alert body for the owner DM.
|
|
292
|
+
*
|
|
293
|
+
* ⚠️ 文案不得承诺「拒绝新任务」:hardStop 目前只有判定原语
|
|
294
|
+
* (isBudgetHardStopped),没有任何 turn 准入路径调用它,所以配了
|
|
295
|
+
* hardStop 也不会真的拦下任何任务。承诺拦截会让 owner 以为已被保护
|
|
296
|
+
* 而不再人工关注(比不告警更糟)。真接入准入路径后再改回强口径。 */
|
|
297
|
+
export function formatBudgetAlert(alert) {
|
|
298
|
+
const spent = formatMoney(alert.spentCny);
|
|
299
|
+
const budget = formatMoney(alert.monthlyCny);
|
|
300
|
+
const percent = Math.round(alert.percent);
|
|
301
|
+
if (alert.hardStop && alert.threshold >= 100) {
|
|
302
|
+
return `🚫 月度预算已用尽:本月已用 ¥${spent} / ¥${budget}(${percent}%)。请人工关注并按需暂停会话——当前版本不会自动拒绝新任务。下月 1 日预算重置。`;
|
|
303
|
+
}
|
|
304
|
+
return `⚠️ 月度预算告警:本月已用 ¥${spent} / ¥${budget}(${percent}%),越过 ${formatThreshold(alert.threshold)}% 告警线。`;
|
|
305
|
+
}
|
|
306
|
+
//# sourceMappingURL=budget-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget-tracker.js","sourceRoot":"","sources":["../../src/services/budget-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -64,6 +64,22 @@ export interface ConnectorDefinition {
|
|
|
64
64
|
lifecycleExtractors: null | {
|
|
65
65
|
dedupKey: string;
|
|
66
66
|
};
|
|
67
|
+
/** Inbound duplicate-delivery suppression for an at-least-once upstream.
|
|
68
|
+
*
|
|
69
|
+
* Header/query carriage needs NO config — a key presented by the sender is
|
|
70
|
+
* always honoured (sending it IS the declaration that the delivery is
|
|
71
|
+
* uniquely identified), which is what makes this fix land on existing
|
|
72
|
+
* connectors without an edit. These two knobs only cover what can't be
|
|
73
|
+
* inferred:
|
|
74
|
+
* - `keyPath`: dotted body path for senders that can't set a header (they can
|
|
75
|
+
* only paste a URL), so the unique id lives inside the event JSON.
|
|
76
|
+
* - `disabled`: escape hatch for an upstream that reuses one id across
|
|
77
|
+
* genuinely distinct events, where suppression would lose real events.
|
|
78
|
+
* Absent (older stores) = header/query honoured, no body path. */
|
|
79
|
+
idempotency?: {
|
|
80
|
+
keyPath?: string;
|
|
81
|
+
disabled?: boolean;
|
|
82
|
+
};
|
|
67
83
|
rateLimit?: {
|
|
68
84
|
windowSeconds: number;
|
|
69
85
|
maxRequests: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connector-store.d.ts","sourceRoot":"","sources":["../../src/services/connector-store.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connector-store.d.ts","sourceRoot":"","sources":["../../src/services/connector-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connector-store.js","sourceRoot":"","sources":["../../src/services/connector-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"connector-store.js","sourceRoot":"","sources":["../../src/services/connector-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Failed-turn bookkeeping for the `/retry` command.
|
|
3
|
+
*
|
|
4
|
+
* A turn that ends `failed` or `ambiguous` records its exact CLI input here
|
|
5
|
+
* (persisted on the Session as `lastFailedTurn`) so the user can re-inject it
|
|
6
|
+
* with `/retry` instead of copy-pasting. Recording is guarded by the session's
|
|
7
|
+
* current reply target: a type-ahead successor already owns the prompt, so the
|
|
8
|
+
* failed predecessor must not capture it.
|
|
9
|
+
*
|
|
10
|
+
* Pure functions only — persistence and IPC stay with the daemon/command-handler
|
|
11
|
+
* callers, mirroring the ordinary-turn-recovery split.
|
|
12
|
+
*/
|
|
13
|
+
import type { CodexAppTurnInput, FailedTurnRecord } from '../types.js';
|
|
14
|
+
/** Cooldown between two `/retry` attempts on the same session. */
|
|
15
|
+
export declare const RETRY_COOLDOWN_MS = 10000;
|
|
16
|
+
/** Whether a turn_terminal event should be recorded as the session's last
|
|
17
|
+
* failed turn. `completed`/`cancelled` never qualify. When the session's
|
|
18
|
+
* current reply target already points at a NEWER turn (type-ahead), the
|
|
19
|
+
* failed turn's prompt has been superseded and must not be captured. */
|
|
20
|
+
export declare function shouldRecordFailedTurn(terminal: {
|
|
21
|
+
turnId: string;
|
|
22
|
+
status: string;
|
|
23
|
+
}, currentReplyTargetTurnId: string | undefined): boolean;
|
|
24
|
+
/** Build the persisted record for a failed/interrupted turn. Returns undefined
|
|
25
|
+
* when there is no CLI input to re-inject (e.g. a turn that died before its
|
|
26
|
+
* prompt was wrapped). */
|
|
27
|
+
export declare function buildFailedTurnRecord(terminal: {
|
|
28
|
+
turnId: string;
|
|
29
|
+
status: 'failed' | 'ambiguous';
|
|
30
|
+
errorCode?: string;
|
|
31
|
+
}, sources: {
|
|
32
|
+
userPrompt?: string;
|
|
33
|
+
cliInput?: string;
|
|
34
|
+
codexAppInput?: CodexAppTurnInput;
|
|
35
|
+
}, now?: Date): FailedTurnRecord | undefined;
|
|
36
|
+
/** Remaining cooldown in ms before `/retry` may fire again (0 = ready). */
|
|
37
|
+
export declare function retryCooldownRemaining(record: FailedTurnRecord, now?: Date, cooldownMs?: number): number;
|
|
38
|
+
/** Stamp one accepted `/retry` attempt onto the session's record. No-op when
|
|
39
|
+
* the session has no failed turn recorded. */
|
|
40
|
+
export declare function markRetryAttempt(session: {
|
|
41
|
+
lastFailedTurn?: FailedTurnRecord;
|
|
42
|
+
}, now?: Date): void;
|
|
43
|
+
//# sourceMappingURL=failed-turn-retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed-turn-retry.d.ts","sourceRoot":"","sources":["../../src/services/failed-turn-retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEvE,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAExC;;;yEAGyE;AACzE,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC5C,wBAAwB,EAAE,MAAM,GAAG,SAAS,GAC3C,OAAO,CAMT;AAED;;2BAE2B;AAC3B,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,WAAW,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAChF,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,iBAAiB,CAAA;CAAE,EACtF,GAAG,GAAE,IAAiB,GACrB,gBAAgB,GAAG,SAAS,CAY9B;AAED,2EAA2E;AAC3E,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,gBAAgB,EACxB,GAAG,GAAE,IAAiB,EACtB,UAAU,GAAE,MAA0B,GACrC,MAAM,CAKR;AAED;+CAC+C;AAC/C,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE;IAAE,cAAc,CAAC,EAAE,gBAAgB,CAAA;CAAE,EAC9C,GAAG,GAAE,IAAiB,GACrB,IAAI,CAKN"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Cooldown between two `/retry` attempts on the same session. */
|
|
2
|
+
export const RETRY_COOLDOWN_MS = 10_000;
|
|
3
|
+
/** Whether a turn_terminal event should be recorded as the session's last
|
|
4
|
+
* failed turn. `completed`/`cancelled` never qualify. When the session's
|
|
5
|
+
* current reply target already points at a NEWER turn (type-ahead), the
|
|
6
|
+
* failed turn's prompt has been superseded and must not be captured. */
|
|
7
|
+
export function shouldRecordFailedTurn(terminal, currentReplyTargetTurnId) {
|
|
8
|
+
if (terminal.status !== 'failed' && terminal.status !== 'ambiguous')
|
|
9
|
+
return false;
|
|
10
|
+
if (currentReplyTargetTurnId !== undefined && currentReplyTargetTurnId !== terminal.turnId) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
/** Build the persisted record for a failed/interrupted turn. Returns undefined
|
|
16
|
+
* when there is no CLI input to re-inject (e.g. a turn that died before its
|
|
17
|
+
* prompt was wrapped). */
|
|
18
|
+
export function buildFailedTurnRecord(terminal, sources, now = new Date()) {
|
|
19
|
+
if (!sources.cliInput)
|
|
20
|
+
return undefined;
|
|
21
|
+
return {
|
|
22
|
+
turnId: terminal.turnId,
|
|
23
|
+
userPrompt: sources.userPrompt ?? sources.cliInput,
|
|
24
|
+
cliInput: sources.cliInput,
|
|
25
|
+
...(sources.codexAppInput ? { codexAppInput: sources.codexAppInput } : {}),
|
|
26
|
+
failedAt: now.toISOString(),
|
|
27
|
+
...(terminal.errorCode ? { errorCode: terminal.errorCode } : {}),
|
|
28
|
+
status: terminal.status,
|
|
29
|
+
retryCount: 0,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Remaining cooldown in ms before `/retry` may fire again (0 = ready). */
|
|
33
|
+
export function retryCooldownRemaining(record, now = new Date(), cooldownMs = RETRY_COOLDOWN_MS) {
|
|
34
|
+
if (!record.lastRetryAt)
|
|
35
|
+
return 0;
|
|
36
|
+
const last = new Date(record.lastRetryAt).getTime();
|
|
37
|
+
if (Number.isNaN(last))
|
|
38
|
+
return 0;
|
|
39
|
+
return Math.max(0, cooldownMs - (now.getTime() - last));
|
|
40
|
+
}
|
|
41
|
+
/** Stamp one accepted `/retry` attempt onto the session's record. No-op when
|
|
42
|
+
* the session has no failed turn recorded. */
|
|
43
|
+
export function markRetryAttempt(session, now = new Date()) {
|
|
44
|
+
const record = session.lastFailedTurn;
|
|
45
|
+
if (!record)
|
|
46
|
+
return;
|
|
47
|
+
record.retryCount += 1;
|
|
48
|
+
record.lastRetryAt = now.toISOString();
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=failed-turn-retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed-turn-retry.js","sourceRoot":"","sources":["../../src/services/failed-turn-retry.ts"],"names":[],"mappings":"AAcA,kEAAkE;AAClE,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAExC;;;yEAGyE;AACzE,MAAM,UAAU,sBAAsB,CACpC,QAA4C,EAC5C,wBAA4C;IAE5C,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAClF,IAAI,wBAAwB,KAAK,SAAS,IAAI,wBAAwB,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC3F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;2BAE2B;AAC3B,MAAM,UAAU,qBAAqB,CACnC,QAAgF,EAChF,OAAsF,EACtF,MAAY,IAAI,IAAI,EAAE;IAEtB,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,QAAQ;QAClD,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,QAAQ,EAAE,GAAG,CAAC,WAAW,EAAE;QAC3B,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,CAAC;KACd,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,sBAAsB,CACpC,MAAwB,EACxB,MAAY,IAAI,IAAI,EAAE,EACtB,aAAqB,iBAAiB;IAEtC,IAAI,CAAC,MAAM,CAAC,WAAW;QAAE,OAAO,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;IACpD,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;+CAC+C;AAC/C,MAAM,UAAU,gBAAgB,CAC9B,OAA8C,EAC9C,MAAY,IAAI,IAAI,EAAE;IAEtB,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IACtC,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model pricing — token→金额估算的内置公开价格表与解析函数。
|
|
3
|
+
*
|
|
4
|
+
* 价格采集日:2026-08-21。来源(USD / 1M token,公开标价):
|
|
5
|
+
* - https://www.anthropic.com/pricing
|
|
6
|
+
* - https://openai.com/api/pricing/
|
|
7
|
+
* - https://ai.google.dev/pricing
|
|
8
|
+
* 汇率默认 USD→CNY = 7.2(DEFAULT_USD_CNY),可被 bots.json 的 pricing.usdCny 覆盖。
|
|
9
|
+
*
|
|
10
|
+
* 纯函数模块:禁止 import bot-registry / daemon / lark client,保证
|
|
11
|
+
* cost-calculator、usage-ledger 与单测的依赖图不被污染。未收录模型一律
|
|
12
|
+
* fail-closed(resolveModelPrice 返回 null,绝不猜价),调用方据此省略金额
|
|
13
|
+
* 字段而非错报。新模型发布需补 BUILT_IN_MODEL_PRICES。
|
|
14
|
+
*/
|
|
15
|
+
/** USD / 1M token。cacheRead/cacheWrite 缺省时回退 input 价。 */
|
|
16
|
+
export interface ModelPrice {
|
|
17
|
+
input: number;
|
|
18
|
+
output: number;
|
|
19
|
+
cacheRead?: number;
|
|
20
|
+
cacheWrite?: number;
|
|
21
|
+
}
|
|
22
|
+
/** bots.json `pricing` 块经宽松解析后的形态。 */
|
|
23
|
+
export interface PricingOverrides {
|
|
24
|
+
usdCny?: number;
|
|
25
|
+
/** 键为模型族前缀(如 'claude-sonnet-4'),整项替换内置同键。 */
|
|
26
|
+
models?: Record<string, ModelPrice>;
|
|
27
|
+
}
|
|
28
|
+
/** 一次定价解析的结果:汇率 + 可选覆盖表。 */
|
|
29
|
+
export interface ResolvedModelPricing {
|
|
30
|
+
usdCny: number;
|
|
31
|
+
overrides?: PricingOverrides;
|
|
32
|
+
}
|
|
33
|
+
/** estimateCostCny 的输入(与 SessionTokenUsage 结构兼容)。 */
|
|
34
|
+
export interface TokenUsagePricingInput {
|
|
35
|
+
inputTokens: number;
|
|
36
|
+
outputTokens: number;
|
|
37
|
+
cacheReadTokens: number;
|
|
38
|
+
cacheCreateTokens: number;
|
|
39
|
+
model: string;
|
|
40
|
+
}
|
|
41
|
+
export declare const DEFAULT_USD_CNY = 7.2;
|
|
42
|
+
/**
|
|
43
|
+
* 把一个 bot 的 `pricing` 块(已经过 normalizePricingOverrides)提升为一次
|
|
44
|
+
* 定价解析结果:填上汇率默认值。未配置 pricing 时返回 undefined,调用方据此
|
|
45
|
+
* 省略金额字段(fail-closed,绝不猜价)。
|
|
46
|
+
*
|
|
47
|
+
* 这里刻意只接 pricing 对象、不接 larkAppId——本模块是纯函数模块,不能 import
|
|
48
|
+
* bot-registry(会污染 cost-calculator / usage-ledger / 单测的依赖图)。各调用方
|
|
49
|
+
* 自己做 `getBot(larkAppId).config.pricing` 查表,再把结果喂进来。
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolvePricingConfig(pricing?: PricingOverrides): ResolvedModelPricing | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* 内置价格表,键用模型族前缀(resolveModelPrice 做精确 + 最长前缀匹配)。
|
|
54
|
+
* 每项为 [input, output, cacheRead?, cacheWrite?],单位 USD/1M token;
|
|
55
|
+
* cacheWrite 缺省 = input。
|
|
56
|
+
*/
|
|
57
|
+
export declare const BUILT_IN_MODEL_PRICES: Record<string, ModelPrice>;
|
|
58
|
+
/**
|
|
59
|
+
* 宽松解析 bots.json 的 `pricing` 块:对象才接受;usdCny 须为有限正数;
|
|
60
|
+
* models 逐项校验(非法项丢弃)。没有任何有效字段时返回 undefined
|
|
61
|
+
* (整体非法 → 功能关闭,fail-open 回退内置表),绝不抛错。
|
|
62
|
+
*/
|
|
63
|
+
export declare function normalizePricingOverrides(raw: unknown): PricingOverrides | undefined;
|
|
64
|
+
/** 剥掉 provider 前缀与日期后缀,得到可匹配内置表的规范形态。 */
|
|
65
|
+
export declare function normalizeModelId(modelId: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* 解析模型单价(USD/1M token)。匹配顺序:
|
|
68
|
+
* overrides 精确 → 内置精确 → overrides 最长前缀 → 内置最长前缀 → null。
|
|
69
|
+
* 未定价模型返回 null(fail-closed,绝不猜价)。
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveModelPrice(modelId: string, overrides?: PricingOverrides): ModelPrice | null;
|
|
72
|
+
/**
|
|
73
|
+
* 按四桶 token × 单价估算人民币金额:
|
|
74
|
+
* (input*p.input + output*p.output + cacheRead*p.cacheRead + cacheWrite*p.cacheWrite)
|
|
75
|
+
* / 1e6 * usdCny
|
|
76
|
+
* cacheRead/cacheWrite 缺省回退 input 价。无 pricing 或模型未定价 → null
|
|
77
|
+
* (调用方省略金额字段,绝不错报)。
|
|
78
|
+
*/
|
|
79
|
+
export declare function estimateCostCny(usage: TokenUsagePricingInput, pricing?: ResolvedModelPricing): number | null;
|
|
80
|
+
//# sourceMappingURL=model-pricing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-pricing.d.ts","sourceRoot":"","sources":["../../src/services/model-pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACrC;AAED,4BAA4B;AAC5B,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,qDAAqD;AACrD,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,SAAS,CAMjG;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAuB5D,CAAC;AAsBF;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAkBpF;AASD,yCAAyC;AACzC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAsBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,gBAAgB,GAAG,UAAU,GAAG,IAAI,CAUlG;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,sBAAsB,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,MAAM,GAAG,IAAI,CAa5G"}
|