oasis_test_v2 2.2.26 → 2.2.27
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/index.js +692 -450
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1399,6 +1399,13 @@ var init_chat_turn = __esm({
|
|
|
1399
1399
|
});
|
|
1400
1400
|
|
|
1401
1401
|
// ../contract/src/delegation.ts
|
|
1402
|
+
function isLegacyDelegationOutboundPath(path41) {
|
|
1403
|
+
return path41.startsWith(`${LEGACY_DELEGATION_OUTBOUND_DIR}/`);
|
|
1404
|
+
}
|
|
1405
|
+
function delegationOutboundRoot(roundSummaries) {
|
|
1406
|
+
const legacy = roundSummaries.some((round) => (round.files ?? []).some((file) => isLegacyDelegationOutboundPath(file.name)));
|
|
1407
|
+
return legacy ? LEGACY_DELEGATION_OUTBOUND_DIR : DELEGATION_OUTBOUND_DIR;
|
|
1408
|
+
}
|
|
1402
1409
|
function trimRoundSummariesForReturn(summaries, recentRounds = DELEGATION_RETURN_RECENT_ROUNDS) {
|
|
1403
1410
|
const keep = Math.max(0, recentRounds);
|
|
1404
1411
|
const cut = Math.max(0, summaries.length - keep);
|
|
@@ -1439,11 +1446,12 @@ function touchAnchorMessageId(messageId) {
|
|
|
1439
1446
|
const hash2 = messageId.indexOf("#");
|
|
1440
1447
|
return hash2 >= 0 ? messageId.slice(0, hash2) : messageId;
|
|
1441
1448
|
}
|
|
1442
|
-
var DelegationLabelConflictError, DELEGATION_FILE_LIMITS, DELEGATION_INBOUND_DIR, DELEGATION_OUTBOUND_DIR, DELEGATION_RETURN_RECENT_ROUNDS;
|
|
1449
|
+
var DelegationLabelConflictError, DELEGATION_FILE_LIMITS, DELEGATION_INBOUND_DIR, DELEGATION_OUTBOUND_DIR, LEGACY_DELEGATION_OUTBOUND_DIR, DELEGATION_RETURN_RECENT_ROUNDS;
|
|
1443
1450
|
var init_delegation = __esm({
|
|
1444
1451
|
"../contract/src/delegation.ts"() {
|
|
1445
1452
|
"use strict";
|
|
1446
1453
|
init_session_outputs();
|
|
1454
|
+
init_session_outputs();
|
|
1447
1455
|
DelegationLabelConflictError = class extends Error {
|
|
1448
1456
|
constructor(parentSessionId, label) {
|
|
1449
1457
|
super(`delegation label conflict in parent session: parent=${parentSessionId} label=${label ?? "<null>"}`);
|
|
@@ -1461,7 +1469,8 @@ var init_delegation = __esm({
|
|
|
1461
1469
|
maxTotalBytes: 20 * 1024 * 1024
|
|
1462
1470
|
};
|
|
1463
1471
|
DELEGATION_INBOUND_DIR = "inputs/attachments";
|
|
1464
|
-
DELEGATION_OUTBOUND_DIR =
|
|
1472
|
+
DELEGATION_OUTBOUND_DIR = `${SESSION_OUTPUTS_DIR}/delegations`;
|
|
1473
|
+
LEGACY_DELEGATION_OUTBOUND_DIR = "delegations";
|
|
1465
1474
|
DELEGATION_RETURN_RECENT_ROUNDS = 3;
|
|
1466
1475
|
}
|
|
1467
1476
|
});
|
|
@@ -196277,7 +196286,7 @@ async function collectInboundFiles(port, parent, paths, limits, lenient) {
|
|
|
196277
196286
|
}
|
|
196278
196287
|
return { prepared, skipped, totalBytes, unchanged: [] };
|
|
196279
196288
|
}
|
|
196280
|
-
async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FILE_LIMITS, known) {
|
|
196289
|
+
async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FILE_LIMITS, known, outboundRoot = DELEGATION_OUTBOUND_DIR) {
|
|
196281
196290
|
const out = { prepared: [], skipped: [], totalBytes: 0, unchanged: [] };
|
|
196282
196291
|
if (!port) return out;
|
|
196283
196292
|
if (port.locate) {
|
|
@@ -196354,7 +196363,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
196354
196363
|
out.skipped.push({ source: file.rel, code: "PATH_REJECTED", message: "\u8DEF\u5F84\u4E0D\u5408\u6CD5\uFF0C\u6CA1\u642C" });
|
|
196355
196364
|
continue;
|
|
196356
196365
|
}
|
|
196357
|
-
const target = `${
|
|
196366
|
+
const target = `${outboundRoot}/${dirName}/${relative5}`;
|
|
196358
196367
|
const seen = known?.get(target);
|
|
196359
196368
|
if (seen && file.mtime && seen.size === file.size && seen.mtime === file.mtime) {
|
|
196360
196369
|
out.unchanged.push(target);
|
|
@@ -196435,6 +196444,12 @@ function digestOf(text5) {
|
|
|
196435
196444
|
const line = text5.trim().replace(/\s+/g, " ");
|
|
196436
196445
|
return line.length > DIGEST_CHARS ? `${line.slice(0, DIGEST_CHARS)}\u2026` : line;
|
|
196437
196446
|
}
|
|
196447
|
+
function delegationArtifactsView(record8) {
|
|
196448
|
+
return collectDelegationArtifacts(record8.roundSummaries).map((a) => ({
|
|
196449
|
+
...a,
|
|
196450
|
+
childMessageId: childRoundMessageIds(record8.childSessionId, a.round).placeholderId
|
|
196451
|
+
}));
|
|
196452
|
+
}
|
|
196438
196453
|
function childRoundMessageIds(childSessionId, round) {
|
|
196439
196454
|
const suffix = round <= 1 ? "" : `:${round}`;
|
|
196440
196455
|
return {
|
|
@@ -197560,7 +197575,7 @@ ${inboundBundle.paths.map((path41) => `- ${path41}`).join("\n")}` : text5,
|
|
|
197560
197575
|
...record8.label ? { label: record8.label } : {},
|
|
197561
197576
|
...record8.taskDigest ? { taskDigest: record8.taskDigest } : {},
|
|
197562
197577
|
roundSummaries: record8.roundSummaries,
|
|
197563
|
-
artifacts:
|
|
197578
|
+
artifacts: delegationArtifactsView(record8),
|
|
197564
197579
|
consecutiveFails: record8.consecutiveFails,
|
|
197565
197580
|
settledSeq: record8.settledSeq,
|
|
197566
197581
|
pendingInputs: await this.pendingInputs(store, record8.id),
|
|
@@ -198188,7 +198203,9 @@ async function deliverDelegationReturn(deps, terminal) {
|
|
|
198188
198203
|
{ chatSessionId: record8.childSessionId, ...parentSession.companyId ? { companyId: parentSession.companyId } : {} },
|
|
198189
198204
|
dirName,
|
|
198190
198205
|
void 0,
|
|
198191
|
-
known
|
|
198206
|
+
known,
|
|
198207
|
+
// 已经往老落点(工作区根的 `delegations/`)交过文件的委派继续落那儿——协调者手里的是老路径。
|
|
198208
|
+
delegationOutboundRoot(record8.roundSummaries)
|
|
198192
198209
|
).catch((err) => {
|
|
198193
198210
|
log3(`[delegation-return] \u4EA7\u7269\u642C\u8FD0\u5931\u8D25\uFF08delegation=${record8.id}\uFF09: ${String(err)}`);
|
|
198194
198211
|
return {
|
|
@@ -211852,6 +211869,415 @@ var init_uncategorized_project = __esm({
|
|
|
211852
211869
|
}
|
|
211853
211870
|
});
|
|
211854
211871
|
|
|
211872
|
+
// ../server/src/domains/actors/model-pricing.ts
|
|
211873
|
+
function lookupInMap(map, prefixKeys, candidates) {
|
|
211874
|
+
for (const candidate of candidates) {
|
|
211875
|
+
const exact = map instanceof Map ? map.get(candidate) : map[candidate];
|
|
211876
|
+
if (exact) return exact;
|
|
211877
|
+
}
|
|
211878
|
+
const prefix = prefixKeys.find((p2) => candidates.some((candidate) => candidate.startsWith(`${p2}-`) || candidate.startsWith(`${p2}:`)));
|
|
211879
|
+
if (!prefix) return null;
|
|
211880
|
+
return map instanceof Map ? map.get(prefix) ?? null : map[prefix] ?? null;
|
|
211881
|
+
}
|
|
211882
|
+
function stripBracketTier(model) {
|
|
211883
|
+
return model.replace(/\[[^\]]*\]/g, "").trim();
|
|
211884
|
+
}
|
|
211885
|
+
function modelCandidates(model) {
|
|
211886
|
+
const normalized4 = model.trim().toLowerCase();
|
|
211887
|
+
if (!normalized4) return [];
|
|
211888
|
+
const base = [
|
|
211889
|
+
normalized4,
|
|
211890
|
+
normalized4.includes(":") ? normalized4.slice(normalized4.lastIndexOf(":") + 1) : "",
|
|
211891
|
+
normalized4.includes("/") ? normalized4.slice(normalized4.lastIndexOf("/") + 1) : ""
|
|
211892
|
+
].filter(Boolean);
|
|
211893
|
+
const stripped = base.map(stripBracketTier).filter((m2) => m2 && !base.includes(m2));
|
|
211894
|
+
return [...base, ...new Set(stripped)];
|
|
211895
|
+
}
|
|
211896
|
+
function setModelPriceCacheLoader(loader) {
|
|
211897
|
+
cacheLoader = loader;
|
|
211898
|
+
}
|
|
211899
|
+
async function warmModelPriceCache() {
|
|
211900
|
+
if (!cacheLoader) return 0;
|
|
211901
|
+
try {
|
|
211902
|
+
const next = await cacheLoader();
|
|
211903
|
+
dbPrices = next;
|
|
211904
|
+
dbPrefixKeys = [...next.keys()].sort((a, b2) => b2.length - a.length);
|
|
211905
|
+
cacheLoadedAt = Date.now();
|
|
211906
|
+
return next.size;
|
|
211907
|
+
} catch (err) {
|
|
211908
|
+
console.warn(`[model-pricing] \u9884\u70ED\u4EF7\u683C\u7F13\u5B58\u5931\u8D25\uFF0C\u6CBF\u7528\u9759\u6001\u5FEB\u7167\uFF1A${String(err)}`);
|
|
211909
|
+
return 0;
|
|
211910
|
+
}
|
|
211911
|
+
}
|
|
211912
|
+
function maybeRefreshCache() {
|
|
211913
|
+
if (!cacheLoader) return;
|
|
211914
|
+
if (dbPrices && Date.now() - cacheLoadedAt < CACHE_TTL_MS2) return;
|
|
211915
|
+
if (cacheRefreshInFlight) return;
|
|
211916
|
+
cacheRefreshInFlight = warmModelPriceCache().catch(() => 0).then(() => {
|
|
211917
|
+
cacheRefreshInFlight = null;
|
|
211918
|
+
});
|
|
211919
|
+
}
|
|
211920
|
+
function priceForModel(model) {
|
|
211921
|
+
if (!model) return null;
|
|
211922
|
+
const candidates = modelCandidates(model);
|
|
211923
|
+
if (candidates.length === 0) return null;
|
|
211924
|
+
maybeRefreshCache();
|
|
211925
|
+
if (dbPrices && dbPrices.size > 0) {
|
|
211926
|
+
const fromDb = lookupInMap(dbPrices, dbPrefixKeys, candidates);
|
|
211927
|
+
if (fromDb) return fromDb;
|
|
211928
|
+
}
|
|
211929
|
+
return lookupInMap(MODEL_PRICES, STATIC_PREFIX_PRICES, candidates);
|
|
211930
|
+
}
|
|
211931
|
+
function estimateUsageCostBreakdown(model, usage) {
|
|
211932
|
+
const price = priceForModel(model);
|
|
211933
|
+
if (!price) return null;
|
|
211934
|
+
const inputTokens = Math.max(usage.inputTokens ?? 0, 0);
|
|
211935
|
+
const outputTokens = Math.max(usage.outputTokens ?? 0, 0);
|
|
211936
|
+
const cacheReadTokens = Math.max(usage.cacheReadTokens ?? 0, 0);
|
|
211937
|
+
const cacheCreationTokens = Math.max(usage.cacheCreationTokens ?? 0, 0);
|
|
211938
|
+
const cacheReadPrice = price.cachedInputPerMillion ?? price.inputPerMillion * 0.1;
|
|
211939
|
+
return {
|
|
211940
|
+
currency: price.currency,
|
|
211941
|
+
inputMicros: Math.round(inputTokens * price.inputPerMillion / 1e6 * 1e6),
|
|
211942
|
+
cacheMicros: Math.round(
|
|
211943
|
+
(cacheCreationTokens * price.inputPerMillion + cacheReadTokens * cacheReadPrice) / 1e6 * 1e6
|
|
211944
|
+
),
|
|
211945
|
+
outputMicros: Math.round(outputTokens * price.outputPerMillion / 1e6 * 1e6)
|
|
211946
|
+
};
|
|
211947
|
+
}
|
|
211948
|
+
function estimateUsageCost(model, usage) {
|
|
211949
|
+
const breakdown = estimateUsageCostBreakdown(model, usage);
|
|
211950
|
+
if (!breakdown) return null;
|
|
211951
|
+
return {
|
|
211952
|
+
currency: breakdown.currency,
|
|
211953
|
+
micros: breakdown.inputMicros + breakdown.cacheMicros + breakdown.outputMicros
|
|
211954
|
+
};
|
|
211955
|
+
}
|
|
211956
|
+
function displayCostUsdMicros(model, usage) {
|
|
211957
|
+
const stored = Math.max(usage.costUsdMicros ?? 0, 0);
|
|
211958
|
+
if (stored > 0) return stored;
|
|
211959
|
+
const estimated = estimateUsageCost(model, usage);
|
|
211960
|
+
if (!estimated || estimated.micros <= 0) return 0;
|
|
211961
|
+
if (estimated.currency === "USD") return estimated.micros;
|
|
211962
|
+
return Math.round(estimated.micros / DISPLAY_CNY_PER_USD);
|
|
211963
|
+
}
|
|
211964
|
+
var FOREIGN_MODEL_MARKUP, usd, cny, MODEL_PRICES, STATIC_PREFIX_PRICES, CACHE_TTL_MS2, dbPrices, dbPrefixKeys, cacheLoadedAt, cacheLoader, cacheRefreshInFlight;
|
|
211965
|
+
var init_model_pricing = __esm({
|
|
211966
|
+
"../server/src/domains/actors/model-pricing.ts"() {
|
|
211967
|
+
"use strict";
|
|
211968
|
+
init_src();
|
|
211969
|
+
FOREIGN_MODEL_MARKUP = 1.08;
|
|
211970
|
+
usd = (inputPerMillion, outputPerMillion, cachedInputPerMillion) => ({
|
|
211971
|
+
currency: "USD",
|
|
211972
|
+
inputPerMillion: inputPerMillion * FOREIGN_MODEL_MARKUP,
|
|
211973
|
+
outputPerMillion: outputPerMillion * FOREIGN_MODEL_MARKUP,
|
|
211974
|
+
...cachedInputPerMillion !== void 0 ? { cachedInputPerMillion: cachedInputPerMillion * FOREIGN_MODEL_MARKUP } : {}
|
|
211975
|
+
});
|
|
211976
|
+
cny = (inputPerMillion, outputPerMillion, cachedInputPerMillion) => ({
|
|
211977
|
+
currency: "CNY",
|
|
211978
|
+
inputPerMillion,
|
|
211979
|
+
outputPerMillion,
|
|
211980
|
+
...cachedInputPerMillion !== void 0 ? { cachedInputPerMillion } : {}
|
|
211981
|
+
});
|
|
211982
|
+
MODEL_PRICES = {
|
|
211983
|
+
"gpt-5.5": usd(5, 30, 0.5),
|
|
211984
|
+
"gpt-5.5-pro": usd(30, 180),
|
|
211985
|
+
"chat-latest": usd(5, 30, 0.5),
|
|
211986
|
+
"gpt-5.4-mini": usd(0.75, 4.5, 0.075),
|
|
211987
|
+
"gpt-5.4-nano": usd(0.2, 1.25, 0.02),
|
|
211988
|
+
"gpt-5.4": usd(2.5, 15, 0.25),
|
|
211989
|
+
"gpt-5.4-pro": usd(30, 180),
|
|
211990
|
+
"gpt-5.3-codex": usd(1.75, 14, 0.175),
|
|
211991
|
+
"gpt-5.2": usd(1.75, 14, 0.175),
|
|
211992
|
+
"gpt-5.2-chat-latest": usd(1.75, 14, 0.175),
|
|
211993
|
+
"gpt-5.2-pro": usd(21, 168),
|
|
211994
|
+
"gpt-5.2-codex": usd(1.75, 14, 0.175),
|
|
211995
|
+
"gpt-5.1": usd(1.25, 10, 0.125),
|
|
211996
|
+
"gpt-5.1-chat-latest": usd(1.25, 10, 0.125),
|
|
211997
|
+
"gpt-5.1-codex": usd(1.25, 10, 0.125),
|
|
211998
|
+
"gpt-5.1-codex-max": usd(1.25, 10, 0.125),
|
|
211999
|
+
"gpt-5.1-codex-mini": usd(0.25, 2, 0.025),
|
|
212000
|
+
"gpt-5-pro": usd(15, 120),
|
|
212001
|
+
"gpt-5-codex": usd(1.25, 10, 0.125),
|
|
212002
|
+
"gpt-5-search-api": usd(1.25, 10, 0.125),
|
|
212003
|
+
"gpt-5": usd(1.25, 10, 0.125),
|
|
212004
|
+
"gpt-5-mini": usd(0.25, 2, 0.025),
|
|
212005
|
+
"gpt-5-nano": usd(0.05, 0.4, 5e-3),
|
|
212006
|
+
"gpt-5-chat-latest": usd(1.25, 10, 0.125),
|
|
212007
|
+
"gpt-4.1": usd(2, 8, 0.5),
|
|
212008
|
+
"gpt-4.1-2025-04-14": usd(2, 8, 0.5),
|
|
212009
|
+
"gpt-4.1-mini": usd(0.4, 1.6, 0.1),
|
|
212010
|
+
"gpt-4.1-mini-2025-04-14": usd(0.4, 1.6, 0.1),
|
|
212011
|
+
"gpt-4.1-nano": usd(0.1, 0.4, 0.025),
|
|
212012
|
+
"gpt-4.1-nano-2025-04-14": usd(0.1, 0.4, 0.025),
|
|
212013
|
+
"o4-mini": usd(1.1, 4.4, 0.275),
|
|
212014
|
+
// o3 / o3-mini 在 codex 静态目录里可选,却一直不在价目快照里(费用记 0)。
|
|
212015
|
+
// 这两行按 OpenAI 公开价填,不是智增增快照里的行;供应商价目表同步到时以 DB 为准。
|
|
212016
|
+
"o3": usd(2, 8),
|
|
212017
|
+
"o3-mini": usd(1.1, 4.4),
|
|
212018
|
+
"claude-fable-5-1": usd(10, 50),
|
|
212019
|
+
"claude-fable-5": usd(10, 50),
|
|
212020
|
+
"claude-mythos-5-1": usd(10, 50),
|
|
212021
|
+
"claude-mythos-5": usd(10, 50),
|
|
212022
|
+
// claude-opus-5 是 Claude Code 当代默认款,却一直不在这张快照里:查不到价 → 费用记 0
|
|
212023
|
+
// (与 2026-09-04 那次 `claude-opus-5[1m]` 记 0 同一类坑)。按 Opus 档 $5/$25 补上。
|
|
212024
|
+
"claude-opus-5": usd(5, 25),
|
|
212025
|
+
"claude-opus-4-8": usd(5, 25),
|
|
212026
|
+
"claude-opus-4-7": usd(5, 25),
|
|
212027
|
+
"claude-sonnet-5": usd(2, 10),
|
|
212028
|
+
"claude-sonnet-4-6": usd(3, 15),
|
|
212029
|
+
"claude-opus-4-6": usd(5, 25),
|
|
212030
|
+
"claude-opus-4-5-20251101": usd(5, 25),
|
|
212031
|
+
"claude-opus-4-5": usd(5, 25),
|
|
212032
|
+
"claude-sonnet-4-5-20250929": usd(3, 15),
|
|
212033
|
+
// 短名同样要有:前缀比对是「表里的键是模型名的前缀」,带日期的那行匹配不上不带日期的短名。
|
|
212034
|
+
// claude-sonnet-4-5 从 2026-07-04 起就在目录里可选,却一直查不到价(费用记 0),本次一并补。
|
|
212035
|
+
"claude-sonnet-4-5": usd(3, 15),
|
|
212036
|
+
"claude-opus-4-1-20250805": usd(15, 75),
|
|
212037
|
+
"claude-opus-4-20250514": usd(15, 75),
|
|
212038
|
+
"claude-sonnet-4-20250514": usd(3, 15),
|
|
212039
|
+
"claude-3-7-sonnet-20250219": usd(3, 15),
|
|
212040
|
+
"claude-3-5-sonnet-20241022": usd(3, 15),
|
|
212041
|
+
"claude-3-5-sonnet-20240620": usd(3, 15),
|
|
212042
|
+
"claude-3-sonnet-20240229": usd(3, 15),
|
|
212043
|
+
"claude-haiku-4-5-20251001": usd(1, 5),
|
|
212044
|
+
// codebuddy 目录用点号写法(claude-sonnet-4.6),与上面的连字符写法是同一批模型,
|
|
212045
|
+
// 但字符串对不上就查不到价 → 记 0。同价另起别名行。
|
|
212046
|
+
"claude-sonnet-4.6": usd(3, 15),
|
|
212047
|
+
"claude-opus-4.7": usd(5, 25),
|
|
212048
|
+
"gemini-3.1-pro": usd(2, 12),
|
|
212049
|
+
"gemini-3-pro-preview": usd(2, 12),
|
|
212050
|
+
// Claude Code 回报的可能是不带日期的短名,短名匹配不上带日期的那行(前缀比对是「表里的键是模型名的前缀」)。
|
|
212051
|
+
"claude-haiku-4-5": usd(1, 5),
|
|
212052
|
+
"gemini-3.5-flash": usd(1.5, 9),
|
|
212053
|
+
"gemini-3.1-pro-preview": usd(2, 12),
|
|
212054
|
+
"gemini-3.1-pro-preview-customtools": usd(2, 12),
|
|
212055
|
+
"gemini-3.1-flash-lite-preview": usd(0.25, 1.5),
|
|
212056
|
+
"gemini-3-flash-preview": usd(0.5, 3),
|
|
212057
|
+
"gemini-2.5-pro": usd(1.25, 10),
|
|
212058
|
+
"gemini-2.5-flash": usd(0.3, 2.5),
|
|
212059
|
+
"gemini-2.5-flash-lite": usd(0.1, 0.4),
|
|
212060
|
+
"gemini-2.5-flash-preview-09-2025": usd(0.3, 2.5),
|
|
212061
|
+
"gemini-2.0-flash": usd(0.1, 0.4),
|
|
212062
|
+
"deepseek-v4-flash": cny(1, 2, 0.02),
|
|
212063
|
+
"deepseek-v4-pro": cny(3, 6, 0.025),
|
|
212064
|
+
"deepseek-chat": cny(2, 3),
|
|
212065
|
+
"deepseek-reasoner": cny(2, 3),
|
|
212066
|
+
"deepseek-v3.2": cny(2, 3),
|
|
212067
|
+
"deepseek-coder": cny(2, 3),
|
|
212068
|
+
"deepseek-r1": cny(4, 16),
|
|
212069
|
+
"deepseek-r1-0528": cny(4, 16),
|
|
212070
|
+
"deepseek-v3": cny(2, 8),
|
|
212071
|
+
"deepseek-v3-0324": cny(2, 8),
|
|
212072
|
+
"deepseek-v3.1": cny(4, 12),
|
|
212073
|
+
"qwen3-max": cny(6, 24),
|
|
212074
|
+
"qwen3-max-preview": cny(6, 24),
|
|
212075
|
+
"qwen3-coder-plus": cny(4, 16),
|
|
212076
|
+
"qwen3-235b-a22b": cny(4, 40),
|
|
212077
|
+
"qwen3-32b": cny(2, 20),
|
|
212078
|
+
"qwen3-30b-a3b": cny(1.5, 15),
|
|
212079
|
+
"qwen3-14b": cny(1, 10),
|
|
212080
|
+
"qwen3-8b": cny(0.5, 5),
|
|
212081
|
+
"qwen3-4b": cny(0.3, 3),
|
|
212082
|
+
"qwen3-1.7b": cny(0.3, 3),
|
|
212083
|
+
"qwen3-0.6b": cny(0.3, 3),
|
|
212084
|
+
"qwq-plus": cny(1.6, 4),
|
|
212085
|
+
"qwq-plus-latest": cny(1.6, 4),
|
|
212086
|
+
"qwq-plus-2025-03-05": cny(1.6, 4),
|
|
212087
|
+
"kimi-k2.7-code": cny(6.5, 27),
|
|
212088
|
+
"kimi-k2.6": cny(6.5, 27),
|
|
212089
|
+
"kimi-k2.5": cny(4, 21),
|
|
212090
|
+
"kimi-k2-0905-preview": cny(4, 16),
|
|
212091
|
+
"kimi-k2-0711-preview": cny(4, 16),
|
|
212092
|
+
"kimi-k2-turbo-preview": cny(8, 58)
|
|
212093
|
+
};
|
|
212094
|
+
STATIC_PREFIX_PRICES = Object.keys(MODEL_PRICES).sort((a, b2) => b2.length - a.length);
|
|
212095
|
+
CACHE_TTL_MS2 = 5 * 6e4;
|
|
212096
|
+
dbPrices = null;
|
|
212097
|
+
dbPrefixKeys = [];
|
|
212098
|
+
cacheLoadedAt = 0;
|
|
212099
|
+
cacheLoader = null;
|
|
212100
|
+
cacheRefreshInFlight = null;
|
|
212101
|
+
}
|
|
212102
|
+
});
|
|
212103
|
+
|
|
212104
|
+
// ../server/src/domains/usage/accounting.ts
|
|
212105
|
+
function nonNegative(v2) {
|
|
212106
|
+
return Math.max(v2 ?? 0, 0);
|
|
212107
|
+
}
|
|
212108
|
+
function resolvedModel(model) {
|
|
212109
|
+
const trimmed = model?.trim();
|
|
212110
|
+
return trimmed ? trimmed : null;
|
|
212111
|
+
}
|
|
212112
|
+
function splitByWeight(total, w2) {
|
|
212113
|
+
const sum = w2.input + w2.cache + w2.output;
|
|
212114
|
+
if (total === 0) return { input: 0, cache: 0, output: 0 };
|
|
212115
|
+
if (sum <= 0) return { input: 0, cache: 0, output: total };
|
|
212116
|
+
const input = Math.round(total * w2.input / sum);
|
|
212117
|
+
const cache = Math.round(total * w2.cache / sum);
|
|
212118
|
+
return { input, cache, output: total - input - cache };
|
|
212119
|
+
}
|
|
212120
|
+
function toDisplayUsd(currency, micros) {
|
|
212121
|
+
return currency === "USD" ? micros : Math.round(micros / DISPLAY_CNY_PER_USD);
|
|
212122
|
+
}
|
|
212123
|
+
function accountUsage(model, usage) {
|
|
212124
|
+
const resolved = resolvedModel(model);
|
|
212125
|
+
if (!resolved || !usage) return null;
|
|
212126
|
+
const input = nonNegative(usage.inputTokens);
|
|
212127
|
+
const output = nonNegative(usage.outputTokens);
|
|
212128
|
+
const cache = nonNegative(usage.cacheReadTokens) + nonNegative(usage.cacheCreationTokens);
|
|
212129
|
+
const tokens = { input, cache, output, total: input + cache + output };
|
|
212130
|
+
const estimated = estimateUsageCostBreakdown(resolved, usage);
|
|
212131
|
+
const weights = estimated ? { input: estimated.inputMicros, cache: estimated.cacheMicros, output: estimated.outputMicros } : null;
|
|
212132
|
+
const reported = Math.max(usage.costUsdMicros ?? 0, 0);
|
|
212133
|
+
if (reported > 0) {
|
|
212134
|
+
const parts = weights && estimated?.currency === "USD" ? splitByWeight(reported, weights) : { input: 0, cache: 0, output: reported };
|
|
212135
|
+
return {
|
|
212136
|
+
tokens,
|
|
212137
|
+
currency: "USD",
|
|
212138
|
+
amountMicros: reported,
|
|
212139
|
+
parts,
|
|
212140
|
+
displayUsdMicros: reported,
|
|
212141
|
+
displayParts: parts,
|
|
212142
|
+
source: "reported"
|
|
212143
|
+
};
|
|
212144
|
+
}
|
|
212145
|
+
if (estimated && weights) {
|
|
212146
|
+
const amount = weights.input + weights.cache + weights.output;
|
|
212147
|
+
const displayUsdMicros = toDisplayUsd(estimated.currency, amount);
|
|
212148
|
+
return {
|
|
212149
|
+
tokens,
|
|
212150
|
+
currency: estimated.currency,
|
|
212151
|
+
amountMicros: amount,
|
|
212152
|
+
parts: weights,
|
|
212153
|
+
displayUsdMicros,
|
|
212154
|
+
displayParts: estimated.currency === "USD" ? weights : splitByWeight(displayUsdMicros, weights),
|
|
212155
|
+
source: "estimated"
|
|
212156
|
+
};
|
|
212157
|
+
}
|
|
212158
|
+
return {
|
|
212159
|
+
tokens,
|
|
212160
|
+
currency: "USD",
|
|
212161
|
+
amountMicros: 0,
|
|
212162
|
+
parts: { input: 0, cache: 0, output: 0 },
|
|
212163
|
+
displayUsdMicros: 0,
|
|
212164
|
+
displayParts: { input: 0, cache: 0, output: 0 },
|
|
212165
|
+
source: "unpriced"
|
|
212166
|
+
};
|
|
212167
|
+
}
|
|
212168
|
+
function isUnpriced(account) {
|
|
212169
|
+
return account.source === "unpriced" && account.tokens.total > 0;
|
|
212170
|
+
}
|
|
212171
|
+
var init_accounting = __esm({
|
|
212172
|
+
"../server/src/domains/usage/accounting.ts"() {
|
|
212173
|
+
"use strict";
|
|
212174
|
+
init_model_pricing();
|
|
212175
|
+
}
|
|
212176
|
+
});
|
|
212177
|
+
|
|
212178
|
+
// ../server/src/domains/actors/stats.ts
|
|
212179
|
+
function emptyCostBreakdown() {
|
|
212180
|
+
return {
|
|
212181
|
+
inputUsdMicros: 0,
|
|
212182
|
+
cacheUsdMicros: 0,
|
|
212183
|
+
outputUsdMicros: 0,
|
|
212184
|
+
inputCnyMicros: 0,
|
|
212185
|
+
cacheCnyMicros: 0,
|
|
212186
|
+
outputCnyMicros: 0
|
|
212187
|
+
};
|
|
212188
|
+
}
|
|
212189
|
+
function emptySummary() {
|
|
212190
|
+
return {
|
|
212191
|
+
tokenWeek: 0,
|
|
212192
|
+
tokenWeekBreakdown: { input: 0, cache: 0, output: 0 },
|
|
212193
|
+
costWeekUsdMicros: 0,
|
|
212194
|
+
costWeekCnyMicros: 0,
|
|
212195
|
+
costWeekBreakdown: emptyCostBreakdown(),
|
|
212196
|
+
unpricedModels: []
|
|
212197
|
+
};
|
|
212198
|
+
}
|
|
212199
|
+
function addAccount(target, account) {
|
|
212200
|
+
target.tokenWeekBreakdown.input += account.tokens.input;
|
|
212201
|
+
target.tokenWeekBreakdown.cache += account.tokens.cache;
|
|
212202
|
+
target.tokenWeekBreakdown.output += account.tokens.output;
|
|
212203
|
+
target.tokenWeek += account.tokens.total;
|
|
212204
|
+
if (account.currency === "USD") {
|
|
212205
|
+
target.costWeekUsdMicros += account.amountMicros;
|
|
212206
|
+
target.costWeekBreakdown.inputUsdMicros += account.parts.input;
|
|
212207
|
+
target.costWeekBreakdown.cacheUsdMicros += account.parts.cache;
|
|
212208
|
+
target.costWeekBreakdown.outputUsdMicros += account.parts.output;
|
|
212209
|
+
} else {
|
|
212210
|
+
target.costWeekCnyMicros += account.amountMicros;
|
|
212211
|
+
target.costWeekBreakdown.inputCnyMicros += account.parts.input;
|
|
212212
|
+
target.costWeekBreakdown.cacheCnyMicros += account.parts.cache;
|
|
212213
|
+
target.costWeekBreakdown.outputCnyMicros += account.parts.output;
|
|
212214
|
+
}
|
|
212215
|
+
}
|
|
212216
|
+
function summarize2(rows) {
|
|
212217
|
+
const out = emptySummary();
|
|
212218
|
+
const unpriced = /* @__PURE__ */ new Set();
|
|
212219
|
+
for (const row of rows) {
|
|
212220
|
+
const account = accountUsage(row.model, row.usage);
|
|
212221
|
+
if (!account) continue;
|
|
212222
|
+
addAccount(out, account);
|
|
212223
|
+
if (isUnpriced(account)) unpriced.add(resolvedModel(row.model));
|
|
212224
|
+
}
|
|
212225
|
+
out.unpricedModels = [...unpriced].sort();
|
|
212226
|
+
return out;
|
|
212227
|
+
}
|
|
212228
|
+
async function actorStats(actorId, opts = {}) {
|
|
212229
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
212230
|
+
const from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3).toISOString();
|
|
212231
|
+
const to = now.toISOString();
|
|
212232
|
+
const weeklyUsage = opts.usage?.listRuns ? summarizeWeeklyRuns(await listWeeklyRuns(opts.usage, actorId, from, to, opts.artifactIds)) : summarizeWeeklyUsage(!opts.artifactIds && opts.usage?.queryUsageStats ? await opts.usage.queryUsageStats({
|
|
212233
|
+
actorIds: [actorId],
|
|
212234
|
+
from,
|
|
212235
|
+
to,
|
|
212236
|
+
granularity: "hour"
|
|
212237
|
+
}) : []);
|
|
212238
|
+
return {
|
|
212239
|
+
actorId,
|
|
212240
|
+
usageAvailable: !!(opts.usage?.listRuns || !opts.artifactIds && opts.usage?.queryUsageStats),
|
|
212241
|
+
...opts.model ? { participatingTaskCount: participatingTaskCount(opts.model, actorId) } : {},
|
|
212242
|
+
tokenWeek: weeklyUsage.tokenWeek,
|
|
212243
|
+
tokenWeekBreakdown: weeklyUsage.tokenWeekBreakdown,
|
|
212244
|
+
costWeekUsdMicros: weeklyUsage.costWeekUsdMicros,
|
|
212245
|
+
costWeekCnyMicros: weeklyUsage.costWeekCnyMicros,
|
|
212246
|
+
costWeekBreakdown: weeklyUsage.costWeekBreakdown,
|
|
212247
|
+
usageWindow: { from, to },
|
|
212248
|
+
unpricedModels: weeklyUsage.unpricedModels
|
|
212249
|
+
};
|
|
212250
|
+
}
|
|
212251
|
+
async function listWeeklyRuns(trace, actorId, from, to, artifactIds) {
|
|
212252
|
+
const out = [];
|
|
212253
|
+
let cursor;
|
|
212254
|
+
do {
|
|
212255
|
+
const page = await trace.listRuns({ actorId, from, to, limit: 500, ...cursor ? { cursor } : {} });
|
|
212256
|
+
out.push(...page.items.filter((run) => !artifactIds || run.artifactId != null && artifactIds.has(run.artifactId)));
|
|
212257
|
+
cursor = page.nextCursor ?? void 0;
|
|
212258
|
+
} while (cursor);
|
|
212259
|
+
return out;
|
|
212260
|
+
}
|
|
212261
|
+
function summarizeWeeklyRuns(runs) {
|
|
212262
|
+
return summarize2(runs.map((run) => ({ model: run.effectiveModel, usage: run.usage })));
|
|
212263
|
+
}
|
|
212264
|
+
function summarizeWeeklyUsage(rows) {
|
|
212265
|
+
return summarize2(rows.map((row) => ({ model: row.effectiveModel, usage: row })));
|
|
212266
|
+
}
|
|
212267
|
+
function participatingTaskCount(model, actorId) {
|
|
212268
|
+
const workorders = /* @__PURE__ */ new Set();
|
|
212269
|
+
for (const artifact of model.artifacts.values()) {
|
|
212270
|
+
if (artifact.owner === actorId) workorders.add(artifact.workspace);
|
|
212271
|
+
}
|
|
212272
|
+
return workorders.size;
|
|
212273
|
+
}
|
|
212274
|
+
var init_stats = __esm({
|
|
212275
|
+
"../server/src/domains/actors/stats.ts"() {
|
|
212276
|
+
"use strict";
|
|
212277
|
+
init_accounting();
|
|
212278
|
+
}
|
|
212279
|
+
});
|
|
212280
|
+
|
|
211855
212281
|
// ../server/src/domains/projects/service.ts
|
|
211856
212282
|
function sanitizeCreatorActorId(raw) {
|
|
211857
212283
|
const id = raw?.trim();
|
|
@@ -212132,6 +212558,7 @@ var init_service4 = __esm({
|
|
|
212132
212558
|
init_projection();
|
|
212133
212559
|
init_uncategorized_project();
|
|
212134
212560
|
init_workorders();
|
|
212561
|
+
init_stats();
|
|
212135
212562
|
init_node_state();
|
|
212136
212563
|
DEFAULT_AGENT_ACTOR = "actor:agent:system";
|
|
212137
212564
|
MemoryProjectStateStore = class {
|
|
@@ -212491,6 +212918,17 @@ var init_service4 = __esm({
|
|
|
212491
212918
|
artifacts.sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt));
|
|
212492
212919
|
return { artifacts, summary: summarizeArtifacts(artifacts) };
|
|
212493
212920
|
}
|
|
212921
|
+
/** 成员概览只读本公司的项目记录与当前任务模型,不加载项目文件/用量旁账。 */
|
|
212922
|
+
async getActorParticipationStats(actorId) {
|
|
212923
|
+
if (!this.actorDirectory) throw new Error("participation source unavailable");
|
|
212924
|
+
if (!await this.actorDirectory.getActor(actorId)) return null;
|
|
212925
|
+
const projects = await this.projects.listProjects();
|
|
212926
|
+
return {
|
|
212927
|
+
actorId,
|
|
212928
|
+
createdProjectCount: projects.filter((project) => !isUncategorizedProjectId(project.id) && project.createdBy?.id === actorId).length,
|
|
212929
|
+
participatingTaskCount: participatingTaskCount(this.kernel.model, actorId)
|
|
212930
|
+
};
|
|
212931
|
+
}
|
|
212494
212932
|
/**
|
|
212495
212933
|
* 项目列表(带派生态)。
|
|
212496
212934
|
*
|
|
@@ -215491,17 +215929,30 @@ function joinRel(dir, name) {
|
|
|
215491
215929
|
return dir ? `${dir}/${name}` : name;
|
|
215492
215930
|
}
|
|
215493
215931
|
async function scanSessionOutputs(bridge, loc, now = Date.now) {
|
|
215932
|
+
const locator = { workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId };
|
|
215933
|
+
if (typeof bridge.walk === "function") {
|
|
215934
|
+
const walked = await bridge.walk(loc.nodeId, { ...locator, path: SESSION_OUTPUTS_DIR }, WALK_MAX_ENTRIES).catch(() => null);
|
|
215935
|
+
if (!walked) return null;
|
|
215936
|
+
if (walked.ok) {
|
|
215937
|
+
const files2 = walked.entries.filter((entry) => entry.type === "file").map((entry) => ({ path: entry.path, size: entry.size, mtime: entry.mtime }));
|
|
215938
|
+
return { files: files2, via: "walk", tree: walked.entries };
|
|
215939
|
+
}
|
|
215940
|
+
if (walked.code === "NOT_FOUND") return { files: [], via: "walk", tree: [] };
|
|
215941
|
+
if (walked.code !== "UNSUPPORTED") return null;
|
|
215942
|
+
}
|
|
215943
|
+
const files = await scanLayered(
|
|
215944
|
+
(path41) => bridge.list(loc.nodeId, { ...locator, path: path41 }),
|
|
215945
|
+
now
|
|
215946
|
+
);
|
|
215947
|
+
return files === null ? null : { files, via: "layered" };
|
|
215948
|
+
}
|
|
215949
|
+
async function scanLayered(list2, now) {
|
|
215494
215950
|
const deadline = now() + BUDGET_MS;
|
|
215495
215951
|
const out = [];
|
|
215496
215952
|
let listCalls = 0;
|
|
215497
215953
|
const listDir = (path41) => {
|
|
215498
215954
|
listCalls += 1;
|
|
215499
|
-
return
|
|
215500
|
-
workdirKey: loc.workdirKey,
|
|
215501
|
-
runtimeKind: loc.runtimeKind,
|
|
215502
|
-
rtId: loc.rtId,
|
|
215503
|
-
path: path41
|
|
215504
|
-
});
|
|
215955
|
+
return list2(path41);
|
|
215505
215956
|
};
|
|
215506
215957
|
const root = await listDir(SESSION_OUTPUTS_DIR).catch(() => null);
|
|
215507
215958
|
if (!root) return null;
|
|
@@ -215534,23 +215985,44 @@ async function scanSessionOutputs(bridge, loc, now = Date.now) {
|
|
|
215534
215985
|
}
|
|
215535
215986
|
return out;
|
|
215536
215987
|
}
|
|
215988
|
+
async function legacyReach(tree) {
|
|
215989
|
+
const children = /* @__PURE__ */ new Map();
|
|
215990
|
+
for (const entry of tree) {
|
|
215991
|
+
const slash = entry.path.lastIndexOf("/");
|
|
215992
|
+
const parent = slash >= 0 ? entry.path.slice(0, slash) : "";
|
|
215993
|
+
const siblings = children.get(parent) ?? [];
|
|
215994
|
+
siblings.push({ name: entry.path.slice(slash + 1), type: entry.type, size: entry.size, mtime: entry.mtime });
|
|
215995
|
+
children.set(parent, siblings);
|
|
215996
|
+
}
|
|
215997
|
+
const files = await scanLayered(async (path41) => ({ ok: true, entries: children.get(path41) ?? [], absPath: "" }), () => 0);
|
|
215998
|
+
return new Set((files ?? []).map((file) => file.path));
|
|
215999
|
+
}
|
|
215537
216000
|
async function settleSessionOutputs(deps) {
|
|
215538
|
-
const
|
|
215539
|
-
if (
|
|
216001
|
+
const scan2 = await scanSessionOutputs(deps.bridge, deps.loc, deps.now);
|
|
216002
|
+
if (scan2 === null) return null;
|
|
215540
216003
|
const previous3 = await deps.readFingerprints(deps.sessionId).catch(() => null);
|
|
215541
|
-
const diff = diffSessionOutputs(
|
|
216004
|
+
const diff = diffSessionOutputs(scan2.files, previous3 ?? void 0);
|
|
216005
|
+
if (scan2.via === "walk") {
|
|
216006
|
+
if (previous3 && previous3[WALK_TABLE_MARK] === void 0) {
|
|
216007
|
+
const reach = await legacyReach(scan2.tree ?? []);
|
|
216008
|
+
diff.changed = diff.changed.filter((file) => previous3[file.path] !== void 0 || reach.has(file.path));
|
|
216009
|
+
}
|
|
216010
|
+
diff.fingerprints[WALK_TABLE_MARK] = { size: 0, mtime: "" };
|
|
216011
|
+
}
|
|
215542
216012
|
await deps.writeFingerprints(deps.sessionId, diff.fingerprints).catch(() => void 0);
|
|
215543
216013
|
return diff;
|
|
215544
216014
|
}
|
|
215545
|
-
var MAX_DEPTH, MAX_ENTRIES, MAX_LIST_CALLS, BUDGET_MS;
|
|
216015
|
+
var WALK_MAX_ENTRIES, MAX_DEPTH, MAX_ENTRIES, MAX_LIST_CALLS, BUDGET_MS, WALK_TABLE_MARK;
|
|
215546
216016
|
var init_outputs_scan = __esm({
|
|
215547
216017
|
"../server/src/domains/chat-sessions/outputs-scan.ts"() {
|
|
215548
216018
|
"use strict";
|
|
215549
216019
|
init_src();
|
|
216020
|
+
WALK_MAX_ENTRIES = 2e3;
|
|
215550
216021
|
MAX_DEPTH = 3;
|
|
215551
216022
|
MAX_ENTRIES = 100;
|
|
215552
216023
|
MAX_LIST_CALLS = 12;
|
|
215553
216024
|
BUDGET_MS = 5e3;
|
|
216025
|
+
WALK_TABLE_MARK = "#walk";
|
|
215554
216026
|
}
|
|
215555
216027
|
});
|
|
215556
216028
|
|
|
@@ -221536,415 +222008,6 @@ var init_skill_bundle = __esm({
|
|
|
221536
222008
|
}
|
|
221537
222009
|
});
|
|
221538
222010
|
|
|
221539
|
-
// ../server/src/domains/actors/model-pricing.ts
|
|
221540
|
-
function lookupInMap(map, prefixKeys, candidates) {
|
|
221541
|
-
for (const candidate of candidates) {
|
|
221542
|
-
const exact = map instanceof Map ? map.get(candidate) : map[candidate];
|
|
221543
|
-
if (exact) return exact;
|
|
221544
|
-
}
|
|
221545
|
-
const prefix = prefixKeys.find((p2) => candidates.some((candidate) => candidate.startsWith(`${p2}-`) || candidate.startsWith(`${p2}:`)));
|
|
221546
|
-
if (!prefix) return null;
|
|
221547
|
-
return map instanceof Map ? map.get(prefix) ?? null : map[prefix] ?? null;
|
|
221548
|
-
}
|
|
221549
|
-
function stripBracketTier(model) {
|
|
221550
|
-
return model.replace(/\[[^\]]*\]/g, "").trim();
|
|
221551
|
-
}
|
|
221552
|
-
function modelCandidates(model) {
|
|
221553
|
-
const normalized4 = model.trim().toLowerCase();
|
|
221554
|
-
if (!normalized4) return [];
|
|
221555
|
-
const base = [
|
|
221556
|
-
normalized4,
|
|
221557
|
-
normalized4.includes(":") ? normalized4.slice(normalized4.lastIndexOf(":") + 1) : "",
|
|
221558
|
-
normalized4.includes("/") ? normalized4.slice(normalized4.lastIndexOf("/") + 1) : ""
|
|
221559
|
-
].filter(Boolean);
|
|
221560
|
-
const stripped = base.map(stripBracketTier).filter((m2) => m2 && !base.includes(m2));
|
|
221561
|
-
return [...base, ...new Set(stripped)];
|
|
221562
|
-
}
|
|
221563
|
-
function setModelPriceCacheLoader(loader) {
|
|
221564
|
-
cacheLoader = loader;
|
|
221565
|
-
}
|
|
221566
|
-
async function warmModelPriceCache() {
|
|
221567
|
-
if (!cacheLoader) return 0;
|
|
221568
|
-
try {
|
|
221569
|
-
const next = await cacheLoader();
|
|
221570
|
-
dbPrices = next;
|
|
221571
|
-
dbPrefixKeys = [...next.keys()].sort((a, b2) => b2.length - a.length);
|
|
221572
|
-
cacheLoadedAt = Date.now();
|
|
221573
|
-
return next.size;
|
|
221574
|
-
} catch (err) {
|
|
221575
|
-
console.warn(`[model-pricing] \u9884\u70ED\u4EF7\u683C\u7F13\u5B58\u5931\u8D25\uFF0C\u6CBF\u7528\u9759\u6001\u5FEB\u7167\uFF1A${String(err)}`);
|
|
221576
|
-
return 0;
|
|
221577
|
-
}
|
|
221578
|
-
}
|
|
221579
|
-
function maybeRefreshCache() {
|
|
221580
|
-
if (!cacheLoader) return;
|
|
221581
|
-
if (dbPrices && Date.now() - cacheLoadedAt < CACHE_TTL_MS2) return;
|
|
221582
|
-
if (cacheRefreshInFlight) return;
|
|
221583
|
-
cacheRefreshInFlight = warmModelPriceCache().catch(() => 0).then(() => {
|
|
221584
|
-
cacheRefreshInFlight = null;
|
|
221585
|
-
});
|
|
221586
|
-
}
|
|
221587
|
-
function priceForModel(model) {
|
|
221588
|
-
if (!model) return null;
|
|
221589
|
-
const candidates = modelCandidates(model);
|
|
221590
|
-
if (candidates.length === 0) return null;
|
|
221591
|
-
maybeRefreshCache();
|
|
221592
|
-
if (dbPrices && dbPrices.size > 0) {
|
|
221593
|
-
const fromDb = lookupInMap(dbPrices, dbPrefixKeys, candidates);
|
|
221594
|
-
if (fromDb) return fromDb;
|
|
221595
|
-
}
|
|
221596
|
-
return lookupInMap(MODEL_PRICES, STATIC_PREFIX_PRICES, candidates);
|
|
221597
|
-
}
|
|
221598
|
-
function estimateUsageCostBreakdown(model, usage) {
|
|
221599
|
-
const price = priceForModel(model);
|
|
221600
|
-
if (!price) return null;
|
|
221601
|
-
const inputTokens = Math.max(usage.inputTokens ?? 0, 0);
|
|
221602
|
-
const outputTokens = Math.max(usage.outputTokens ?? 0, 0);
|
|
221603
|
-
const cacheReadTokens = Math.max(usage.cacheReadTokens ?? 0, 0);
|
|
221604
|
-
const cacheCreationTokens = Math.max(usage.cacheCreationTokens ?? 0, 0);
|
|
221605
|
-
const cacheReadPrice = price.cachedInputPerMillion ?? price.inputPerMillion * 0.1;
|
|
221606
|
-
return {
|
|
221607
|
-
currency: price.currency,
|
|
221608
|
-
inputMicros: Math.round(inputTokens * price.inputPerMillion / 1e6 * 1e6),
|
|
221609
|
-
cacheMicros: Math.round(
|
|
221610
|
-
(cacheCreationTokens * price.inputPerMillion + cacheReadTokens * cacheReadPrice) / 1e6 * 1e6
|
|
221611
|
-
),
|
|
221612
|
-
outputMicros: Math.round(outputTokens * price.outputPerMillion / 1e6 * 1e6)
|
|
221613
|
-
};
|
|
221614
|
-
}
|
|
221615
|
-
function estimateUsageCost(model, usage) {
|
|
221616
|
-
const breakdown = estimateUsageCostBreakdown(model, usage);
|
|
221617
|
-
if (!breakdown) return null;
|
|
221618
|
-
return {
|
|
221619
|
-
currency: breakdown.currency,
|
|
221620
|
-
micros: breakdown.inputMicros + breakdown.cacheMicros + breakdown.outputMicros
|
|
221621
|
-
};
|
|
221622
|
-
}
|
|
221623
|
-
function displayCostUsdMicros(model, usage) {
|
|
221624
|
-
const stored = Math.max(usage.costUsdMicros ?? 0, 0);
|
|
221625
|
-
if (stored > 0) return stored;
|
|
221626
|
-
const estimated = estimateUsageCost(model, usage);
|
|
221627
|
-
if (!estimated || estimated.micros <= 0) return 0;
|
|
221628
|
-
if (estimated.currency === "USD") return estimated.micros;
|
|
221629
|
-
return Math.round(estimated.micros / DISPLAY_CNY_PER_USD);
|
|
221630
|
-
}
|
|
221631
|
-
var FOREIGN_MODEL_MARKUP, usd, cny, MODEL_PRICES, STATIC_PREFIX_PRICES, CACHE_TTL_MS2, dbPrices, dbPrefixKeys, cacheLoadedAt, cacheLoader, cacheRefreshInFlight;
|
|
221632
|
-
var init_model_pricing = __esm({
|
|
221633
|
-
"../server/src/domains/actors/model-pricing.ts"() {
|
|
221634
|
-
"use strict";
|
|
221635
|
-
init_src();
|
|
221636
|
-
FOREIGN_MODEL_MARKUP = 1.08;
|
|
221637
|
-
usd = (inputPerMillion, outputPerMillion, cachedInputPerMillion) => ({
|
|
221638
|
-
currency: "USD",
|
|
221639
|
-
inputPerMillion: inputPerMillion * FOREIGN_MODEL_MARKUP,
|
|
221640
|
-
outputPerMillion: outputPerMillion * FOREIGN_MODEL_MARKUP,
|
|
221641
|
-
...cachedInputPerMillion !== void 0 ? { cachedInputPerMillion: cachedInputPerMillion * FOREIGN_MODEL_MARKUP } : {}
|
|
221642
|
-
});
|
|
221643
|
-
cny = (inputPerMillion, outputPerMillion, cachedInputPerMillion) => ({
|
|
221644
|
-
currency: "CNY",
|
|
221645
|
-
inputPerMillion,
|
|
221646
|
-
outputPerMillion,
|
|
221647
|
-
...cachedInputPerMillion !== void 0 ? { cachedInputPerMillion } : {}
|
|
221648
|
-
});
|
|
221649
|
-
MODEL_PRICES = {
|
|
221650
|
-
"gpt-5.5": usd(5, 30, 0.5),
|
|
221651
|
-
"gpt-5.5-pro": usd(30, 180),
|
|
221652
|
-
"chat-latest": usd(5, 30, 0.5),
|
|
221653
|
-
"gpt-5.4-mini": usd(0.75, 4.5, 0.075),
|
|
221654
|
-
"gpt-5.4-nano": usd(0.2, 1.25, 0.02),
|
|
221655
|
-
"gpt-5.4": usd(2.5, 15, 0.25),
|
|
221656
|
-
"gpt-5.4-pro": usd(30, 180),
|
|
221657
|
-
"gpt-5.3-codex": usd(1.75, 14, 0.175),
|
|
221658
|
-
"gpt-5.2": usd(1.75, 14, 0.175),
|
|
221659
|
-
"gpt-5.2-chat-latest": usd(1.75, 14, 0.175),
|
|
221660
|
-
"gpt-5.2-pro": usd(21, 168),
|
|
221661
|
-
"gpt-5.2-codex": usd(1.75, 14, 0.175),
|
|
221662
|
-
"gpt-5.1": usd(1.25, 10, 0.125),
|
|
221663
|
-
"gpt-5.1-chat-latest": usd(1.25, 10, 0.125),
|
|
221664
|
-
"gpt-5.1-codex": usd(1.25, 10, 0.125),
|
|
221665
|
-
"gpt-5.1-codex-max": usd(1.25, 10, 0.125),
|
|
221666
|
-
"gpt-5.1-codex-mini": usd(0.25, 2, 0.025),
|
|
221667
|
-
"gpt-5-pro": usd(15, 120),
|
|
221668
|
-
"gpt-5-codex": usd(1.25, 10, 0.125),
|
|
221669
|
-
"gpt-5-search-api": usd(1.25, 10, 0.125),
|
|
221670
|
-
"gpt-5": usd(1.25, 10, 0.125),
|
|
221671
|
-
"gpt-5-mini": usd(0.25, 2, 0.025),
|
|
221672
|
-
"gpt-5-nano": usd(0.05, 0.4, 5e-3),
|
|
221673
|
-
"gpt-5-chat-latest": usd(1.25, 10, 0.125),
|
|
221674
|
-
"gpt-4.1": usd(2, 8, 0.5),
|
|
221675
|
-
"gpt-4.1-2025-04-14": usd(2, 8, 0.5),
|
|
221676
|
-
"gpt-4.1-mini": usd(0.4, 1.6, 0.1),
|
|
221677
|
-
"gpt-4.1-mini-2025-04-14": usd(0.4, 1.6, 0.1),
|
|
221678
|
-
"gpt-4.1-nano": usd(0.1, 0.4, 0.025),
|
|
221679
|
-
"gpt-4.1-nano-2025-04-14": usd(0.1, 0.4, 0.025),
|
|
221680
|
-
"o4-mini": usd(1.1, 4.4, 0.275),
|
|
221681
|
-
// o3 / o3-mini 在 codex 静态目录里可选,却一直不在价目快照里(费用记 0)。
|
|
221682
|
-
// 这两行按 OpenAI 公开价填,不是智增增快照里的行;供应商价目表同步到时以 DB 为准。
|
|
221683
|
-
"o3": usd(2, 8),
|
|
221684
|
-
"o3-mini": usd(1.1, 4.4),
|
|
221685
|
-
"claude-fable-5-1": usd(10, 50),
|
|
221686
|
-
"claude-fable-5": usd(10, 50),
|
|
221687
|
-
"claude-mythos-5-1": usd(10, 50),
|
|
221688
|
-
"claude-mythos-5": usd(10, 50),
|
|
221689
|
-
// claude-opus-5 是 Claude Code 当代默认款,却一直不在这张快照里:查不到价 → 费用记 0
|
|
221690
|
-
// (与 2026-09-04 那次 `claude-opus-5[1m]` 记 0 同一类坑)。按 Opus 档 $5/$25 补上。
|
|
221691
|
-
"claude-opus-5": usd(5, 25),
|
|
221692
|
-
"claude-opus-4-8": usd(5, 25),
|
|
221693
|
-
"claude-opus-4-7": usd(5, 25),
|
|
221694
|
-
"claude-sonnet-5": usd(2, 10),
|
|
221695
|
-
"claude-sonnet-4-6": usd(3, 15),
|
|
221696
|
-
"claude-opus-4-6": usd(5, 25),
|
|
221697
|
-
"claude-opus-4-5-20251101": usd(5, 25),
|
|
221698
|
-
"claude-opus-4-5": usd(5, 25),
|
|
221699
|
-
"claude-sonnet-4-5-20250929": usd(3, 15),
|
|
221700
|
-
// 短名同样要有:前缀比对是「表里的键是模型名的前缀」,带日期的那行匹配不上不带日期的短名。
|
|
221701
|
-
// claude-sonnet-4-5 从 2026-07-04 起就在目录里可选,却一直查不到价(费用记 0),本次一并补。
|
|
221702
|
-
"claude-sonnet-4-5": usd(3, 15),
|
|
221703
|
-
"claude-opus-4-1-20250805": usd(15, 75),
|
|
221704
|
-
"claude-opus-4-20250514": usd(15, 75),
|
|
221705
|
-
"claude-sonnet-4-20250514": usd(3, 15),
|
|
221706
|
-
"claude-3-7-sonnet-20250219": usd(3, 15),
|
|
221707
|
-
"claude-3-5-sonnet-20241022": usd(3, 15),
|
|
221708
|
-
"claude-3-5-sonnet-20240620": usd(3, 15),
|
|
221709
|
-
"claude-3-sonnet-20240229": usd(3, 15),
|
|
221710
|
-
"claude-haiku-4-5-20251001": usd(1, 5),
|
|
221711
|
-
// codebuddy 目录用点号写法(claude-sonnet-4.6),与上面的连字符写法是同一批模型,
|
|
221712
|
-
// 但字符串对不上就查不到价 → 记 0。同价另起别名行。
|
|
221713
|
-
"claude-sonnet-4.6": usd(3, 15),
|
|
221714
|
-
"claude-opus-4.7": usd(5, 25),
|
|
221715
|
-
"gemini-3.1-pro": usd(2, 12),
|
|
221716
|
-
"gemini-3-pro-preview": usd(2, 12),
|
|
221717
|
-
// Claude Code 回报的可能是不带日期的短名,短名匹配不上带日期的那行(前缀比对是「表里的键是模型名的前缀」)。
|
|
221718
|
-
"claude-haiku-4-5": usd(1, 5),
|
|
221719
|
-
"gemini-3.5-flash": usd(1.5, 9),
|
|
221720
|
-
"gemini-3.1-pro-preview": usd(2, 12),
|
|
221721
|
-
"gemini-3.1-pro-preview-customtools": usd(2, 12),
|
|
221722
|
-
"gemini-3.1-flash-lite-preview": usd(0.25, 1.5),
|
|
221723
|
-
"gemini-3-flash-preview": usd(0.5, 3),
|
|
221724
|
-
"gemini-2.5-pro": usd(1.25, 10),
|
|
221725
|
-
"gemini-2.5-flash": usd(0.3, 2.5),
|
|
221726
|
-
"gemini-2.5-flash-lite": usd(0.1, 0.4),
|
|
221727
|
-
"gemini-2.5-flash-preview-09-2025": usd(0.3, 2.5),
|
|
221728
|
-
"gemini-2.0-flash": usd(0.1, 0.4),
|
|
221729
|
-
"deepseek-v4-flash": cny(1, 2, 0.02),
|
|
221730
|
-
"deepseek-v4-pro": cny(3, 6, 0.025),
|
|
221731
|
-
"deepseek-chat": cny(2, 3),
|
|
221732
|
-
"deepseek-reasoner": cny(2, 3),
|
|
221733
|
-
"deepseek-v3.2": cny(2, 3),
|
|
221734
|
-
"deepseek-coder": cny(2, 3),
|
|
221735
|
-
"deepseek-r1": cny(4, 16),
|
|
221736
|
-
"deepseek-r1-0528": cny(4, 16),
|
|
221737
|
-
"deepseek-v3": cny(2, 8),
|
|
221738
|
-
"deepseek-v3-0324": cny(2, 8),
|
|
221739
|
-
"deepseek-v3.1": cny(4, 12),
|
|
221740
|
-
"qwen3-max": cny(6, 24),
|
|
221741
|
-
"qwen3-max-preview": cny(6, 24),
|
|
221742
|
-
"qwen3-coder-plus": cny(4, 16),
|
|
221743
|
-
"qwen3-235b-a22b": cny(4, 40),
|
|
221744
|
-
"qwen3-32b": cny(2, 20),
|
|
221745
|
-
"qwen3-30b-a3b": cny(1.5, 15),
|
|
221746
|
-
"qwen3-14b": cny(1, 10),
|
|
221747
|
-
"qwen3-8b": cny(0.5, 5),
|
|
221748
|
-
"qwen3-4b": cny(0.3, 3),
|
|
221749
|
-
"qwen3-1.7b": cny(0.3, 3),
|
|
221750
|
-
"qwen3-0.6b": cny(0.3, 3),
|
|
221751
|
-
"qwq-plus": cny(1.6, 4),
|
|
221752
|
-
"qwq-plus-latest": cny(1.6, 4),
|
|
221753
|
-
"qwq-plus-2025-03-05": cny(1.6, 4),
|
|
221754
|
-
"kimi-k2.7-code": cny(6.5, 27),
|
|
221755
|
-
"kimi-k2.6": cny(6.5, 27),
|
|
221756
|
-
"kimi-k2.5": cny(4, 21),
|
|
221757
|
-
"kimi-k2-0905-preview": cny(4, 16),
|
|
221758
|
-
"kimi-k2-0711-preview": cny(4, 16),
|
|
221759
|
-
"kimi-k2-turbo-preview": cny(8, 58)
|
|
221760
|
-
};
|
|
221761
|
-
STATIC_PREFIX_PRICES = Object.keys(MODEL_PRICES).sort((a, b2) => b2.length - a.length);
|
|
221762
|
-
CACHE_TTL_MS2 = 5 * 6e4;
|
|
221763
|
-
dbPrices = null;
|
|
221764
|
-
dbPrefixKeys = [];
|
|
221765
|
-
cacheLoadedAt = 0;
|
|
221766
|
-
cacheLoader = null;
|
|
221767
|
-
cacheRefreshInFlight = null;
|
|
221768
|
-
}
|
|
221769
|
-
});
|
|
221770
|
-
|
|
221771
|
-
// ../server/src/domains/usage/accounting.ts
|
|
221772
|
-
function nonNegative(v2) {
|
|
221773
|
-
return Math.max(v2 ?? 0, 0);
|
|
221774
|
-
}
|
|
221775
|
-
function resolvedModel(model) {
|
|
221776
|
-
const trimmed = model?.trim();
|
|
221777
|
-
return trimmed ? trimmed : null;
|
|
221778
|
-
}
|
|
221779
|
-
function splitByWeight(total, w2) {
|
|
221780
|
-
const sum = w2.input + w2.cache + w2.output;
|
|
221781
|
-
if (total === 0) return { input: 0, cache: 0, output: 0 };
|
|
221782
|
-
if (sum <= 0) return { input: 0, cache: 0, output: total };
|
|
221783
|
-
const input = Math.round(total * w2.input / sum);
|
|
221784
|
-
const cache = Math.round(total * w2.cache / sum);
|
|
221785
|
-
return { input, cache, output: total - input - cache };
|
|
221786
|
-
}
|
|
221787
|
-
function toDisplayUsd(currency, micros) {
|
|
221788
|
-
return currency === "USD" ? micros : Math.round(micros / DISPLAY_CNY_PER_USD);
|
|
221789
|
-
}
|
|
221790
|
-
function accountUsage(model, usage) {
|
|
221791
|
-
const resolved = resolvedModel(model);
|
|
221792
|
-
if (!resolved || !usage) return null;
|
|
221793
|
-
const input = nonNegative(usage.inputTokens);
|
|
221794
|
-
const output = nonNegative(usage.outputTokens);
|
|
221795
|
-
const cache = nonNegative(usage.cacheReadTokens) + nonNegative(usage.cacheCreationTokens);
|
|
221796
|
-
const tokens = { input, cache, output, total: input + cache + output };
|
|
221797
|
-
const estimated = estimateUsageCostBreakdown(resolved, usage);
|
|
221798
|
-
const weights = estimated ? { input: estimated.inputMicros, cache: estimated.cacheMicros, output: estimated.outputMicros } : null;
|
|
221799
|
-
const reported = Math.max(usage.costUsdMicros ?? 0, 0);
|
|
221800
|
-
if (reported > 0) {
|
|
221801
|
-
const parts = weights && estimated?.currency === "USD" ? splitByWeight(reported, weights) : { input: 0, cache: 0, output: reported };
|
|
221802
|
-
return {
|
|
221803
|
-
tokens,
|
|
221804
|
-
currency: "USD",
|
|
221805
|
-
amountMicros: reported,
|
|
221806
|
-
parts,
|
|
221807
|
-
displayUsdMicros: reported,
|
|
221808
|
-
displayParts: parts,
|
|
221809
|
-
source: "reported"
|
|
221810
|
-
};
|
|
221811
|
-
}
|
|
221812
|
-
if (estimated && weights) {
|
|
221813
|
-
const amount = weights.input + weights.cache + weights.output;
|
|
221814
|
-
const displayUsdMicros = toDisplayUsd(estimated.currency, amount);
|
|
221815
|
-
return {
|
|
221816
|
-
tokens,
|
|
221817
|
-
currency: estimated.currency,
|
|
221818
|
-
amountMicros: amount,
|
|
221819
|
-
parts: weights,
|
|
221820
|
-
displayUsdMicros,
|
|
221821
|
-
displayParts: estimated.currency === "USD" ? weights : splitByWeight(displayUsdMicros, weights),
|
|
221822
|
-
source: "estimated"
|
|
221823
|
-
};
|
|
221824
|
-
}
|
|
221825
|
-
return {
|
|
221826
|
-
tokens,
|
|
221827
|
-
currency: "USD",
|
|
221828
|
-
amountMicros: 0,
|
|
221829
|
-
parts: { input: 0, cache: 0, output: 0 },
|
|
221830
|
-
displayUsdMicros: 0,
|
|
221831
|
-
displayParts: { input: 0, cache: 0, output: 0 },
|
|
221832
|
-
source: "unpriced"
|
|
221833
|
-
};
|
|
221834
|
-
}
|
|
221835
|
-
function isUnpriced(account) {
|
|
221836
|
-
return account.source === "unpriced" && account.tokens.total > 0;
|
|
221837
|
-
}
|
|
221838
|
-
var init_accounting = __esm({
|
|
221839
|
-
"../server/src/domains/usage/accounting.ts"() {
|
|
221840
|
-
"use strict";
|
|
221841
|
-
init_model_pricing();
|
|
221842
|
-
}
|
|
221843
|
-
});
|
|
221844
|
-
|
|
221845
|
-
// ../server/src/domains/actors/stats.ts
|
|
221846
|
-
function emptyCostBreakdown() {
|
|
221847
|
-
return {
|
|
221848
|
-
inputUsdMicros: 0,
|
|
221849
|
-
cacheUsdMicros: 0,
|
|
221850
|
-
outputUsdMicros: 0,
|
|
221851
|
-
inputCnyMicros: 0,
|
|
221852
|
-
cacheCnyMicros: 0,
|
|
221853
|
-
outputCnyMicros: 0
|
|
221854
|
-
};
|
|
221855
|
-
}
|
|
221856
|
-
function emptySummary() {
|
|
221857
|
-
return {
|
|
221858
|
-
tokenWeek: 0,
|
|
221859
|
-
tokenWeekBreakdown: { input: 0, cache: 0, output: 0 },
|
|
221860
|
-
costWeekUsdMicros: 0,
|
|
221861
|
-
costWeekCnyMicros: 0,
|
|
221862
|
-
costWeekBreakdown: emptyCostBreakdown(),
|
|
221863
|
-
unpricedModels: []
|
|
221864
|
-
};
|
|
221865
|
-
}
|
|
221866
|
-
function addAccount(target, account) {
|
|
221867
|
-
target.tokenWeekBreakdown.input += account.tokens.input;
|
|
221868
|
-
target.tokenWeekBreakdown.cache += account.tokens.cache;
|
|
221869
|
-
target.tokenWeekBreakdown.output += account.tokens.output;
|
|
221870
|
-
target.tokenWeek += account.tokens.total;
|
|
221871
|
-
if (account.currency === "USD") {
|
|
221872
|
-
target.costWeekUsdMicros += account.amountMicros;
|
|
221873
|
-
target.costWeekBreakdown.inputUsdMicros += account.parts.input;
|
|
221874
|
-
target.costWeekBreakdown.cacheUsdMicros += account.parts.cache;
|
|
221875
|
-
target.costWeekBreakdown.outputUsdMicros += account.parts.output;
|
|
221876
|
-
} else {
|
|
221877
|
-
target.costWeekCnyMicros += account.amountMicros;
|
|
221878
|
-
target.costWeekBreakdown.inputCnyMicros += account.parts.input;
|
|
221879
|
-
target.costWeekBreakdown.cacheCnyMicros += account.parts.cache;
|
|
221880
|
-
target.costWeekBreakdown.outputCnyMicros += account.parts.output;
|
|
221881
|
-
}
|
|
221882
|
-
}
|
|
221883
|
-
function summarize2(rows) {
|
|
221884
|
-
const out = emptySummary();
|
|
221885
|
-
const unpriced = /* @__PURE__ */ new Set();
|
|
221886
|
-
for (const row of rows) {
|
|
221887
|
-
const account = accountUsage(row.model, row.usage);
|
|
221888
|
-
if (!account) continue;
|
|
221889
|
-
addAccount(out, account);
|
|
221890
|
-
if (isUnpriced(account)) unpriced.add(resolvedModel(row.model));
|
|
221891
|
-
}
|
|
221892
|
-
out.unpricedModels = [...unpriced].sort();
|
|
221893
|
-
return out;
|
|
221894
|
-
}
|
|
221895
|
-
async function actorStats(actorId, opts = {}) {
|
|
221896
|
-
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
221897
|
-
const from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3).toISOString();
|
|
221898
|
-
const to = now.toISOString();
|
|
221899
|
-
const weeklyUsage = opts.usage?.listRuns ? summarizeWeeklyRuns(await listWeeklyRuns(opts.usage, actorId, from, to, opts.artifactIds)) : summarizeWeeklyUsage(!opts.artifactIds && opts.usage?.queryUsageStats ? await opts.usage.queryUsageStats({
|
|
221900
|
-
actorIds: [actorId],
|
|
221901
|
-
from,
|
|
221902
|
-
to,
|
|
221903
|
-
granularity: "hour"
|
|
221904
|
-
}) : []);
|
|
221905
|
-
return {
|
|
221906
|
-
actorId,
|
|
221907
|
-
usageAvailable: !!(opts.usage?.listRuns || !opts.artifactIds && opts.usage?.queryUsageStats),
|
|
221908
|
-
...opts.model ? { participatingTaskCount: participatingTaskCount(opts.model, actorId) } : {},
|
|
221909
|
-
tokenWeek: weeklyUsage.tokenWeek,
|
|
221910
|
-
tokenWeekBreakdown: weeklyUsage.tokenWeekBreakdown,
|
|
221911
|
-
costWeekUsdMicros: weeklyUsage.costWeekUsdMicros,
|
|
221912
|
-
costWeekCnyMicros: weeklyUsage.costWeekCnyMicros,
|
|
221913
|
-
costWeekBreakdown: weeklyUsage.costWeekBreakdown,
|
|
221914
|
-
usageWindow: { from, to },
|
|
221915
|
-
unpricedModels: weeklyUsage.unpricedModels
|
|
221916
|
-
};
|
|
221917
|
-
}
|
|
221918
|
-
async function listWeeklyRuns(trace, actorId, from, to, artifactIds) {
|
|
221919
|
-
const out = [];
|
|
221920
|
-
let cursor;
|
|
221921
|
-
do {
|
|
221922
|
-
const page = await trace.listRuns({ actorId, from, to, limit: 500, ...cursor ? { cursor } : {} });
|
|
221923
|
-
out.push(...page.items.filter((run) => !artifactIds || run.artifactId != null && artifactIds.has(run.artifactId)));
|
|
221924
|
-
cursor = page.nextCursor ?? void 0;
|
|
221925
|
-
} while (cursor);
|
|
221926
|
-
return out;
|
|
221927
|
-
}
|
|
221928
|
-
function summarizeWeeklyRuns(runs) {
|
|
221929
|
-
return summarize2(runs.map((run) => ({ model: run.effectiveModel, usage: run.usage })));
|
|
221930
|
-
}
|
|
221931
|
-
function summarizeWeeklyUsage(rows) {
|
|
221932
|
-
return summarize2(rows.map((row) => ({ model: row.effectiveModel, usage: row })));
|
|
221933
|
-
}
|
|
221934
|
-
function participatingTaskCount(model, actorId) {
|
|
221935
|
-
const workorders = /* @__PURE__ */ new Set();
|
|
221936
|
-
for (const artifact of model.artifacts.values()) {
|
|
221937
|
-
if (artifact.owner === actorId) workorders.add(artifact.workspace);
|
|
221938
|
-
}
|
|
221939
|
-
return workorders.size;
|
|
221940
|
-
}
|
|
221941
|
-
var init_stats = __esm({
|
|
221942
|
-
"../server/src/domains/actors/stats.ts"() {
|
|
221943
|
-
"use strict";
|
|
221944
|
-
init_accounting();
|
|
221945
|
-
}
|
|
221946
|
-
});
|
|
221947
|
-
|
|
221948
222011
|
// ../server/src/domains/actors/list-usage.ts
|
|
221949
222012
|
function aggregateUsageByActor(runs) {
|
|
221950
222013
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -224235,6 +224298,20 @@ ${errs.join("\n")}`);
|
|
|
224235
224298
|
});
|
|
224236
224299
|
return { status: 200, body: toApiProjectArtifacts(list2, req.query) };
|
|
224237
224300
|
});
|
|
224301
|
+
router.get("/api/actors/:id/participation-stats", async (req) => {
|
|
224302
|
+
const service = await svc(req);
|
|
224303
|
+
let stats;
|
|
224304
|
+
try {
|
|
224305
|
+
stats = await service.getActorParticipationStats(req.params.id);
|
|
224306
|
+
} catch (error2) {
|
|
224307
|
+
if (error2 instanceof Error && error2.message === "participation source unavailable") {
|
|
224308
|
+
throw new ApiError(503, "UNAVAILABLE", "\u6210\u5458\u7EDF\u8BA1\u6570\u636E\u6E90\u672A\u63A5\u5165");
|
|
224309
|
+
}
|
|
224310
|
+
throw error2;
|
|
224311
|
+
}
|
|
224312
|
+
if (!stats) throw new ApiError(404, "NOT_FOUND", "\u8FD9\u4E2A\u7EC4\u7EC7\u91CC\u6CA1\u6709\u8FD9\u4F4D\u6210\u5458");
|
|
224313
|
+
return { status: 200, body: stats };
|
|
224314
|
+
});
|
|
224238
224315
|
router.get("/api/actors/:id/files-view", async (req) => {
|
|
224239
224316
|
const items = await (await svc(req)).listActorFiles(req.params.id);
|
|
224240
224317
|
return {
|
|
@@ -229122,9 +229199,50 @@ function reasonFromWorkdirCode(code2) {
|
|
|
229122
229199
|
function joinRel2(dir, name) {
|
|
229123
229200
|
return dir ? `${dir}/${name}` : name;
|
|
229124
229201
|
}
|
|
229202
|
+
function workdirFileItem(rel, name, size, mtime) {
|
|
229203
|
+
return {
|
|
229204
|
+
id: `workdir:${rel}`,
|
|
229205
|
+
name,
|
|
229206
|
+
kind: "file",
|
|
229207
|
+
// 节点侧列目录只报 name/type/size/mtime,没有 MIME——诚实留 null,
|
|
229208
|
+
// 不拿后缀猜(红线:取不到就是取不到,别拿别的字段顶)。
|
|
229209
|
+
content_type: null,
|
|
229210
|
+
size,
|
|
229211
|
+
updated_at: mtime,
|
|
229212
|
+
source: "workdir",
|
|
229213
|
+
blob_ref: null,
|
|
229214
|
+
path: rel,
|
|
229215
|
+
message_id: null,
|
|
229216
|
+
// 工作区文件本期不开写回(brief §4①:要动 node↔server 协议帧,超预算)。
|
|
229217
|
+
// 这里是**按能力不渲染**的判据,不是置灰假钮。
|
|
229218
|
+
editable: false
|
|
229219
|
+
};
|
|
229220
|
+
}
|
|
229125
229221
|
async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
229126
|
-
const deadline = now() + WORKDIR_WALK_BUDGET_MS;
|
|
229127
229222
|
const items = [];
|
|
229223
|
+
if (typeof bridge.walk === "function") {
|
|
229224
|
+
const walked = await bridge.walk(
|
|
229225
|
+
loc.nodeId,
|
|
229226
|
+
{ workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: "" },
|
|
229227
|
+
WORKDIR_TREE_MAX_ENTRIES
|
|
229228
|
+
);
|
|
229229
|
+
if (walked.ok) {
|
|
229230
|
+
let truncated2 = walked.truncated === true;
|
|
229231
|
+
for (const entry of walked.entries) {
|
|
229232
|
+
if (entry.type !== "file") continue;
|
|
229233
|
+
if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
|
|
229234
|
+
truncated2 = true;
|
|
229235
|
+
break;
|
|
229236
|
+
}
|
|
229237
|
+
const slash = entry.path.lastIndexOf("/");
|
|
229238
|
+
items.push(workdirFileItem(entry.path, entry.path.slice(slash + 1), entry.size, entry.mtime));
|
|
229239
|
+
}
|
|
229240
|
+
return { ok: true, items, truncated: truncated2 };
|
|
229241
|
+
}
|
|
229242
|
+
if (walked.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
|
|
229243
|
+
if (walked.code !== "UNSUPPORTED") return { ok: false, reason: reasonFromWorkdirCode(walked.code) };
|
|
229244
|
+
}
|
|
229245
|
+
const deadline = now() + WORKDIR_WALK_BUDGET_MS;
|
|
229128
229246
|
let listCalls = 0;
|
|
229129
229247
|
let truncated = false;
|
|
229130
229248
|
const listDir = (path41) => {
|
|
@@ -229159,24 +229277,7 @@ async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
|
229159
229277
|
truncated = true;
|
|
229160
229278
|
continue;
|
|
229161
229279
|
}
|
|
229162
|
-
|
|
229163
|
-
items.push({
|
|
229164
|
-
id: `workdir:${rel}`,
|
|
229165
|
-
name: entry.name,
|
|
229166
|
-
kind: "file",
|
|
229167
|
-
// 节点侧列目录只报 name/type/size/mtime,没有 MIME——诚实留 null,
|
|
229168
|
-
// 不拿后缀猜(红线:取不到就是取不到,别拿别的字段顶)。
|
|
229169
|
-
content_type: null,
|
|
229170
|
-
size: entry.size,
|
|
229171
|
-
updated_at: entry.mtime,
|
|
229172
|
-
source: "workdir",
|
|
229173
|
-
blob_ref: null,
|
|
229174
|
-
path: rel,
|
|
229175
|
-
message_id: null,
|
|
229176
|
-
// 工作区文件本期不开写回(brief §4①:要动 node↔server 协议帧,超预算)。
|
|
229177
|
-
// 这里是**按能力不渲染**的判据,不是置灰假钮。
|
|
229178
|
-
editable: false
|
|
229179
|
-
});
|
|
229280
|
+
items.push(workdirFileItem(joinRel2(dir, entry.name), entry.name, entry.size, entry.mtime));
|
|
229180
229281
|
}
|
|
229181
229282
|
}
|
|
229182
229283
|
if (depth === WORKDIR_WALK_MAX_DEPTH || nextDirs.length === 0) {
|
|
@@ -229298,7 +229399,7 @@ async function findMaterializedAttachmentCopies(messageItems, workdirItems, read
|
|
|
229298
229399
|
}
|
|
229299
229400
|
return copies;
|
|
229300
229401
|
}
|
|
229301
|
-
var import_node_crypto51, WORKDIR_WALK_MAX_DEPTH, WORKDIR_WALK_MAX_ENTRIES, WORKDIR_WALK_MAX_LIST_CALLS, WORKDIR_WALK_BUDGET_MS;
|
|
229402
|
+
var import_node_crypto51, WORKDIR_WALK_MAX_DEPTH, WORKDIR_WALK_MAX_ENTRIES, WORKDIR_WALK_MAX_LIST_CALLS, WORKDIR_WALK_BUDGET_MS, WORKDIR_TREE_MAX_ENTRIES;
|
|
229302
229403
|
var init_files_view = __esm({
|
|
229303
229404
|
"../server/src/domains/chat-sessions/files-view.ts"() {
|
|
229304
229405
|
"use strict";
|
|
@@ -229309,6 +229410,7 @@ var init_files_view = __esm({
|
|
|
229309
229410
|
WORKDIR_WALK_MAX_ENTRIES = 200;
|
|
229310
229411
|
WORKDIR_WALK_MAX_LIST_CALLS = 20;
|
|
229311
229412
|
WORKDIR_WALK_BUDGET_MS = 1e4;
|
|
229413
|
+
WORKDIR_TREE_MAX_ENTRIES = 2e3;
|
|
229312
229414
|
}
|
|
229313
229415
|
});
|
|
229314
229416
|
|
|
@@ -231752,6 +231854,19 @@ function nodesDomain(deps) {
|
|
|
231752
231854
|
hubWired = true;
|
|
231753
231855
|
const { nodeStore } = deps;
|
|
231754
231856
|
const now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
231857
|
+
const HEARTBEAT_PERSIST_MS = 6e4;
|
|
231858
|
+
const heartbeatPersistedAt = /* @__PURE__ */ new Map();
|
|
231859
|
+
hub.addMessageListener((daemonId) => {
|
|
231860
|
+
if (!nodeStore.touchNodeSeen) return;
|
|
231861
|
+
const ms = Date.now();
|
|
231862
|
+
const prev = heartbeatPersistedAt.get(daemonId) ?? 0;
|
|
231863
|
+
if (ms - prev < HEARTBEAT_PERSIST_MS) return;
|
|
231864
|
+
heartbeatPersistedAt.set(daemonId, ms);
|
|
231865
|
+
void nodeStore.touchNodeSeen(daemonId, new Date(ms).toISOString()).catch((err) => {
|
|
231866
|
+
heartbeatPersistedAt.delete(daemonId);
|
|
231867
|
+
console.warn(`[nodes] \u5FC3\u8DF3\u843D\u5E93\u5931\u8D25(node=${daemonId})\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
231868
|
+
});
|
|
231869
|
+
});
|
|
231755
231870
|
hub.addMessageListener(async (daemonId, msg) => {
|
|
231756
231871
|
if (msg.type !== "hello") return;
|
|
231757
231872
|
const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
|
|
@@ -231820,6 +231935,15 @@ function nodesDomain(deps) {
|
|
|
231820
231935
|
};
|
|
231821
231936
|
tryWire();
|
|
231822
231937
|
if (!hubWired) wirePoll = setInterval(tryWire, 100);
|
|
231938
|
+
async function forbidUnlessOrgManager(req, verb) {
|
|
231939
|
+
if (!deps.resolveMemberRole) return null;
|
|
231940
|
+
const role = await deps.resolveMemberRole(req.auth.companyId, req.auth.actor);
|
|
231941
|
+
if (role === "owner" || role === "admin") return null;
|
|
231942
|
+
return {
|
|
231943
|
+
status: 403,
|
|
231944
|
+
body: { error: "ORG_MANAGER_REQUIRED", message: `${verb}\u4EC5\u7EC4\u7EC7\u7BA1\u7406\u8005\u53EF\u64CD\u4F5C\uFF0C\u8BF7\u8054\u7CFB\u7EC4\u7EC7\u7BA1\u7406\u5458` }
|
|
231945
|
+
};
|
|
231946
|
+
}
|
|
231823
231947
|
async function syncConnectedDaemon(daemonId, hub) {
|
|
231824
231948
|
const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
|
|
231825
231949
|
if (!d) return;
|
|
@@ -231848,6 +231972,13 @@ function nodesDomain(deps) {
|
|
|
231848
231972
|
const outOfSync = nodes.filter((n) => onlineIds.has(n.id) && (n.status !== "online" || (runtimesByNode.get(n.id) ?? []).some((r) => r.status !== "online")));
|
|
231849
231973
|
await Promise.all(outOfSync.map((n) => syncConnectedDaemon(n.id, hub)));
|
|
231850
231974
|
}
|
|
231975
|
+
function heartbeatMsOf(n, offline) {
|
|
231976
|
+
const live = hub?.lastSeenAt(n.id);
|
|
231977
|
+
if (live !== void 0) return live;
|
|
231978
|
+
if (!offline) return void 0;
|
|
231979
|
+
const ms = Date.parse(n.lastSeenAt);
|
|
231980
|
+
return Number.isNaN(ms) ? void 0 : ms;
|
|
231981
|
+
}
|
|
231851
231982
|
const items = await Promise.all(nodes.map(async (n) => {
|
|
231852
231983
|
const rts = runtimesByNode.get(n.id) ?? [];
|
|
231853
231984
|
const daemonVersion = daemonVersionById.get(n.id);
|
|
@@ -231877,7 +232008,16 @@ function nodesDomain(deps) {
|
|
|
231877
232008
|
...deps.nodeHealth ? {
|
|
231878
232009
|
health: deriveNodeHealth({
|
|
231879
232010
|
online: onlineIds.has(n.id),
|
|
231880
|
-
|
|
232011
|
+
/*
|
|
232012
|
+
* 心跳时钟:在线节点读 hub 内存(每帧刷新,最准);**离线节点回落到落库的
|
|
232013
|
+
* `lastSeenAt`**——那是这台机器最后一次跟服务端说上话的时刻(心跳每分钟落一次库,
|
|
232014
|
+
* 见上面的 `HEARTBEAT_PERSIST_MS`;断开那一刻 `setNodeOnline(false)` 也会写一次)。
|
|
232015
|
+
* 运行时页「工作情况」列的离线行显示的「断连时间 X」读的就是它。
|
|
232016
|
+
*
|
|
232017
|
+
* 不影响健康判定:`healthy = online && 心跳新鲜 && 连败未超阈`,离线节点 `online=false`
|
|
232018
|
+
* 已经把结论钉死成不健康,这里补进来的只是**解释**用的时刻(设计稿 4.5 ⑤ 的口径)。
|
|
232019
|
+
*/
|
|
232020
|
+
...heartbeatMsOf(n, nodeOffline) !== void 0 ? { lastHeartbeatMs: heartbeatMsOf(n, nodeOffline) } : {},
|
|
231881
232021
|
activeRunCount,
|
|
231882
232022
|
...deps.nodeHealth.snapshot(n.id) ? { snapshot: deps.nodeHealth.snapshot(n.id) } : {},
|
|
231883
232023
|
now: healthNow
|
|
@@ -232047,6 +232187,8 @@ function nodesDomain(deps) {
|
|
|
232047
232187
|
router.delete("/api/runtimes/:runtimeId", async (req) => {
|
|
232048
232188
|
const runtimeId = req.params["runtimeId"];
|
|
232049
232189
|
if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
|
|
232190
|
+
const forbidden = await forbidUnlessOrgManager(req, "\u5220\u9664\u8FD0\u884C\u65F6");
|
|
232191
|
+
if (forbidden) return forbidden;
|
|
232050
232192
|
const runtime = (await deps.nodeStore.listRuntimes()).find((r) => r.id === runtimeId);
|
|
232051
232193
|
const { nodeDeleted } = await deps.nodeStore.deleteRuntime(runtimeId);
|
|
232052
232194
|
if (nodeDeleted && runtime) revokeNodeAccess(runtime.nodeId);
|
|
@@ -232114,6 +232256,8 @@ function nodesDomain(deps) {
|
|
|
232114
232256
|
router.put("/api/runtimes/:runtimeId/config", async (req) => {
|
|
232115
232257
|
const runtimeId = req.params["runtimeId"];
|
|
232116
232258
|
if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
|
|
232259
|
+
const forbidden = await forbidUnlessOrgManager(req, "\u4FEE\u6539\u56E2\u961F\u9ED8\u8BA4\u6A21\u578B");
|
|
232260
|
+
if (forbidden) return forbidden;
|
|
232117
232261
|
const body2 = req.body ?? {};
|
|
232118
232262
|
const cfg = {
|
|
232119
232263
|
runtimeId,
|
|
@@ -232325,6 +232469,10 @@ var init_node_store = __esm({
|
|
|
232325
232469
|
n.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
232326
232470
|
}
|
|
232327
232471
|
}
|
|
232472
|
+
async touchNodeSeen(id, atIso) {
|
|
232473
|
+
const n = this.nodes.get(id);
|
|
232474
|
+
if (n && n.status !== "deleted") n.lastSeenAt = atIso;
|
|
232475
|
+
}
|
|
232328
232476
|
async setNodeRuntimesOnline(nodeId, online) {
|
|
232329
232477
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
232330
232478
|
for (const rt of this.runtimes.values()) {
|
|
@@ -232444,6 +232592,10 @@ var init_node_store = __esm({
|
|
|
232444
232592
|
await super.setNodeOnline(id, online);
|
|
232445
232593
|
this.flush();
|
|
232446
232594
|
}
|
|
232595
|
+
async touchNodeSeen(id, atIso) {
|
|
232596
|
+
await super.touchNodeSeen(id, atIso);
|
|
232597
|
+
this.flush();
|
|
232598
|
+
}
|
|
232447
232599
|
async setNodeRuntimesOnline(nodeId, online) {
|
|
232448
232600
|
await super.setNodeRuntimesOnline(nodeId, online);
|
|
232449
232601
|
this.flush();
|
|
@@ -235958,7 +236110,9 @@ function createChatSessionsDomain(opts) {
|
|
|
235958
236110
|
的去重装置,直接发给前端等于给了一条 `messages[]` 里不存在的 id ——追加转达卡全部掉进
|
|
235959
236111
|
floating 堆在流末尾,同一条消息上的两条委派还会因后缀不同被拆成两张单专家卡。 */
|
|
235960
236112
|
touches: touches.map((t) => ({ messageId: touchAnchorMessageId(t.messageId), kind: t.kind, at: t.at })),
|
|
235961
|
-
artifacts
|
|
236113
|
+
/* 与协调者读口**同一个函数**——两侧 artifacts 有「逐字同源」的既有断言。它顺带给每份产物
|
|
236114
|
+
填上 `childMessageId`(子会话面板把产出卡挂到产出它的那一轮下面要用)。 */
|
|
236115
|
+
artifacts: delegationArtifactsView(record8),
|
|
235962
236116
|
createdAt: record8.createdAt,
|
|
235963
236117
|
...record8.settledAt ? { settledAt: record8.settledAt } : {}
|
|
235964
236118
|
});
|
|
@@ -236246,6 +236400,7 @@ var init_chat_sessions = __esm({
|
|
|
236246
236400
|
init_escalation_opening();
|
|
236247
236401
|
init_workorders();
|
|
236248
236402
|
init_chat_parts();
|
|
236403
|
+
init_service3();
|
|
236249
236404
|
init_injected_context();
|
|
236250
236405
|
init_deletion();
|
|
236251
236406
|
init_chat_turn_gate();
|
|
@@ -240644,6 +240799,29 @@ var init_daemon_adapter = __esm({
|
|
|
240644
240799
|
maxBytes
|
|
240645
240800
|
}));
|
|
240646
240801
|
}
|
|
240802
|
+
/**
|
|
240803
|
+
* 一次列全 `loc.path` 下的整棵子树(ADR 0554)。节点没自报能力位 → 不发帧、直接回 `UNSUPPORTED`,
|
|
240804
|
+
* 调用方退回逐层 `list`。判据取 hello 里的能力位,不靠「发了看回不回」:老节点收到不认识的帧
|
|
240805
|
+
* 只会静默丢掉,那样要白等满 30 秒超时。
|
|
240806
|
+
*/
|
|
240807
|
+
walk(nodeId, loc, maxEntries) {
|
|
240808
|
+
if (!this.nodeSupportsWalk(nodeId)) return Promise.resolve({ ok: false, code: "UNSUPPORTED" });
|
|
240809
|
+
return this.request(nodeId, (requestId) => ({
|
|
240810
|
+
type: "workdir_walk",
|
|
240811
|
+
requestId,
|
|
240812
|
+
workdirKey: loc.workdirKey,
|
|
240813
|
+
runtimeKind: loc.runtimeKind,
|
|
240814
|
+
rtId: loc.rtId ?? null,
|
|
240815
|
+
path: loc.path,
|
|
240816
|
+
maxEntries
|
|
240817
|
+
}));
|
|
240818
|
+
}
|
|
240819
|
+
nodeSupportsWalk(nodeId) {
|
|
240820
|
+
const connectedDaemons = this.hub.connectedDaemons;
|
|
240821
|
+
if (typeof connectedDaemons !== "function") return false;
|
|
240822
|
+
const daemon = connectedDaemons.call(this.hub).find((candidate) => candidate.daemonId === nodeId);
|
|
240823
|
+
return daemon?.meta.supportsWorkdirWalk === true;
|
|
240824
|
+
}
|
|
240647
240825
|
request(nodeId, buildFrame) {
|
|
240648
240826
|
const requestId = (0, import_node_crypto65.randomUUID)();
|
|
240649
240827
|
const sent = this.hub.dispatch(nodeId, buildFrame(requestId));
|
|
@@ -240658,7 +240836,7 @@ var init_daemon_adapter = __esm({
|
|
|
240658
240836
|
});
|
|
240659
240837
|
}
|
|
240660
240838
|
onDaemonMessage(msg) {
|
|
240661
|
-
if (msg.type !== "workdir_list_result" && msg.type !== "workdir_read_result") return;
|
|
240839
|
+
if (msg.type !== "workdir_list_result" && msg.type !== "workdir_read_result" && msg.type !== "workdir_walk_result") return;
|
|
240662
240840
|
const entry = this.pending.get(msg.requestId);
|
|
240663
240841
|
if (!entry) return;
|
|
240664
240842
|
this.pending.delete(msg.requestId);
|
|
@@ -250669,6 +250847,13 @@ var init_postgres_nodes = __esm({
|
|
|
250669
250847
|
async setNodeOnline(id, online) {
|
|
250670
250848
|
await this.pool.query(`UPDATE ${this.s}.nodes SET status=$2,last_seen_at=$3 WHERE id=$1 AND status <> 'deleted'`, [id, online ? "online" : "offline", (/* @__PURE__ */ new Date()).toISOString()]);
|
|
250671
250849
|
}
|
|
250850
|
+
/** 只推进 last_seen_at,不动 status(心跳落库;节流由调用方做,见 nodes/routes.ts)。 */
|
|
250851
|
+
async touchNodeSeen(id, atIso) {
|
|
250852
|
+
await this.pool.query(
|
|
250853
|
+
`UPDATE ${this.s}.nodes SET last_seen_at=$2 WHERE id=$1 AND status <> 'deleted' AND (last_seen_at IS NULL OR last_seen_at < $2)`,
|
|
250854
|
+
[id, atIso]
|
|
250855
|
+
);
|
|
250856
|
+
}
|
|
250672
250857
|
async setNodeRuntimesOnline(nodeId, online) {
|
|
250673
250858
|
await this.pool.query(`UPDATE ${this.s}.node_runtimes SET status=$2,last_seen_at=$3 WHERE node_id=$1`, [nodeId, online ? "online" : "offline", (/* @__PURE__ */ new Date()).toISOString()]);
|
|
250674
250859
|
}
|
|
@@ -262119,7 +262304,13 @@ async function startServe(opts) {
|
|
|
262119
262304
|
activeRunsOf,
|
|
262120
262305
|
nodeReportedModels,
|
|
262121
262306
|
refreshNodeModels,
|
|
262122
|
-
fetchLatestNpmDaemonVersion
|
|
262307
|
+
fetchLatestNpmDaemonVersion,
|
|
262308
|
+
// 「删除运行时 / 改团队默认模型仅组织管理者」的判据(Runtime PRD「权限设计」)。
|
|
262309
|
+
// 与 projectsDomain 那条 resolveMemberRole 同一实现:闭包在请求时才读成员表。
|
|
262310
|
+
resolveMemberRole: async (companyId, actorId) => {
|
|
262311
|
+
const members = await listCompanyMembers(companyId);
|
|
262312
|
+
return members.find((m2) => m2.actorId === actorId)?.role;
|
|
262313
|
+
}
|
|
262123
262314
|
}),
|
|
262124
262315
|
collabDomain({
|
|
262125
262316
|
kernel: defaultCompanyKernel,
|
|
@@ -266455,6 +266646,8 @@ init_src6();
|
|
|
266455
266646
|
init_src();
|
|
266456
266647
|
var WORKDIR_READ_MAX_BYTES2 = 2 * 1024 * 1024;
|
|
266457
266648
|
var WORKDIR_LIST_MAX_ENTRIES = 500;
|
|
266649
|
+
var WORKDIR_WALK_MAX_ENTRIES2 = 1e4;
|
|
266650
|
+
var WORKDIR_WALK_BUDGET_MS2 = 1e4;
|
|
266458
266651
|
var CREDENTIAL_WORDS = ["token", "secret", "credential", "password", "passwd", "apikey"];
|
|
266459
266652
|
var SSH_KEY_PREFIX = /^id_(rsa|dsa|ecdsa|ed25519)/;
|
|
266460
266653
|
function isSensitiveSegment(segment) {
|
|
@@ -266578,6 +266771,41 @@ async function handleWorkdirList(req) {
|
|
|
266578
266771
|
return { ok: false, code: "INTERNAL", message: String(e?.message ?? e) };
|
|
266579
266772
|
}
|
|
266580
266773
|
}
|
|
266774
|
+
async function handleWorkdirWalk(req, now = Date.now) {
|
|
266775
|
+
const cap = Math.max(1, Math.min(req.maxEntries ?? WORKDIR_WALK_MAX_ENTRIES2, WORKDIR_WALK_MAX_ENTRIES2));
|
|
266776
|
+
const deadline = now() + WORKDIR_WALK_BUDGET_MS2;
|
|
266777
|
+
const start = normalizeRel(req.path).replace(/[/\\]+$/, "");
|
|
266778
|
+
const root = await handleWorkdirList({ ...req, path: start });
|
|
266779
|
+
if (!root.ok) return root;
|
|
266780
|
+
const entries = [];
|
|
266781
|
+
let truncated = root.truncated === true;
|
|
266782
|
+
let level = [{ dir: start, listed: root }];
|
|
266783
|
+
while (level.length > 0) {
|
|
266784
|
+
const pending = [];
|
|
266785
|
+
for (const { dir, listed } of level) {
|
|
266786
|
+
if (!listed.ok) {
|
|
266787
|
+
if (listed.code !== "NOT_FOUND") truncated = true;
|
|
266788
|
+
continue;
|
|
266789
|
+
}
|
|
266790
|
+
if (listed.truncated) truncated = true;
|
|
266791
|
+
for (const entry of listed.entries) {
|
|
266792
|
+
if (entries.length >= cap) return { ok: true, entries, truncated: true, absPath: root.absPath };
|
|
266793
|
+
const rel = dir ? `${dir}/${entry.name}` : entry.name;
|
|
266794
|
+
entries.push({ path: rel, type: entry.type, size: entry.size, mtime: entry.mtime });
|
|
266795
|
+
if (entry.type === "dir") pending.push(rel);
|
|
266796
|
+
}
|
|
266797
|
+
}
|
|
266798
|
+
level = [];
|
|
266799
|
+
for (const dir of pending) {
|
|
266800
|
+
if (entries.length >= cap || now() >= deadline) {
|
|
266801
|
+
truncated = true;
|
|
266802
|
+
break;
|
|
266803
|
+
}
|
|
266804
|
+
level.push({ dir, listed: await handleWorkdirList({ ...req, path: dir }) });
|
|
266805
|
+
}
|
|
266806
|
+
}
|
|
266807
|
+
return { ok: true, entries, absPath: root.absPath, ...truncated ? { truncated: true } : {} };
|
|
266808
|
+
}
|
|
266581
266809
|
async function fdCanonicalPath(fh) {
|
|
266582
266810
|
try {
|
|
266583
266811
|
return await import_node_fs19.default.promises.readlink(`/proc/self/fd/${fh.fd}`);
|
|
@@ -267068,6 +267296,18 @@ var DaemonWsClient = class {
|
|
|
267068
267296
|
this.send({ type: "workdir_read_result", requestId: msg.requestId, result });
|
|
267069
267297
|
break;
|
|
267070
267298
|
}
|
|
267299
|
+
case "workdir_walk": {
|
|
267300
|
+
const result = await handleWorkdirWalk({
|
|
267301
|
+
workRoot: this.workRoot,
|
|
267302
|
+
runtimeKind: msg.runtimeKind,
|
|
267303
|
+
workdirKey: msg.workdirKey,
|
|
267304
|
+
rtId: msg.rtId,
|
|
267305
|
+
path: msg.path,
|
|
267306
|
+
maxEntries: msg.maxEntries
|
|
267307
|
+
});
|
|
267308
|
+
this.send({ type: "workdir_walk_result", requestId: msg.requestId, result });
|
|
267309
|
+
break;
|
|
267310
|
+
}
|
|
267071
267311
|
case "list_models": {
|
|
267072
267312
|
void this.reportRuntimeModels({ force: true, requestId: msg.requestId });
|
|
267073
267313
|
break;
|
|
@@ -267247,6 +267487,8 @@ var DaemonWsClient = class {
|
|
|
267247
267487
|
// 服务端据此决定是用节点归一还是自己就地归一;判据取能力位而不是「见过帧没有」,
|
|
267248
267488
|
// 否则首个 chunk 之前那段窗口两边都不归一,正文会静默丢一截。
|
|
267249
267489
|
supportsNormalizedEvents: true,
|
|
267490
|
+
// ADR 0554:本 daemon 认 `workdir_walk`。恒 true——handler 随包发,装了这版就一定有。
|
|
267491
|
+
supportsWorkdirWalk: true,
|
|
267250
267492
|
...activeSessions.length > 0 ? { activeSessions } : {},
|
|
267251
267493
|
// 始终带字段(空数组也带)作为新协议能力标志;服务端仅对支持该字段的节点做缺席确认。
|
|
267252
267494
|
pendingDispatchIds: [...this.pendingDispatchIds]
|
|
@@ -273151,7 +273393,7 @@ function shimScript() {
|
|
|
273151
273393
|
}
|
|
273152
273394
|
|
|
273153
273395
|
// src/index.ts
|
|
273154
|
-
var PKG_VERSION = true ? "2.2.
|
|
273396
|
+
var PKG_VERSION = true ? "2.2.27" : "dev";
|
|
273155
273397
|
var LOCAL_BIN = localBin();
|
|
273156
273398
|
var NPM_PREFIX = npmPrefix();
|
|
273157
273399
|
var INSTANCE = DEFAULT_INSTANCE;
|