oasis_test_v2 2.2.26 → 2.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1068 -464
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1607,7 +1607,7 @@ var init_delegation_prompts = __esm({
|
|
|
1607
1607
|
"### \u8981\u5E26\u6587\u4EF6\u5C31\u660E\u5199",
|
|
1608
1608
|
"",
|
|
1609
1609
|
"`--file <\u5DE5\u4F5C\u533A\u91CC\u7684\u8DEF\u5F84>`\uFF0C\u53EF\u4EE5\u7ED9\u591A\u6B21\u3002**\u4E0D\u5199\u5C31\u4E00\u4E2A\u6587\u4EF6\u90FD\u4E0D\u5E26**\u2014\u2014\u4E24\u8FB9\u5DE5\u4F5C\u533A\u662F\u5206\u5F00\u7684\u3002",
|
|
1610
|
-
"\u5BF9\u65B9\
|
|
1610
|
+
"\u5BF9\u65B9\u4EA4\u56DE\u7684\u6587\u4EF6\u81EA\u52A8\u843D\u8FDB `delegations/<\u77ED\u540D>/`\uFF08\u7528\u6237\u770B\u4E0D\u5230\uFF09\uFF1B\u8981\u7ED9\u7528\u6237\u770B\u7684\uFF0C\u590D\u5236\u6216\u52A0\u5DE5\u540E\u653E\u8FDB `outputs/`\uFF0C\u539F\u4EF6\u522B\u52A8\u3002"
|
|
1611
1611
|
].join("\n");
|
|
1612
1612
|
}
|
|
1613
1613
|
});
|
|
@@ -196435,6 +196435,18 @@ function digestOf(text5) {
|
|
|
196435
196435
|
const line = text5.trim().replace(/\s+/g, " ");
|
|
196436
196436
|
return line.length > DIGEST_CHARS ? `${line.slice(0, DIGEST_CHARS)}\u2026` : line;
|
|
196437
196437
|
}
|
|
196438
|
+
function delegationArtifactsView(record8) {
|
|
196439
|
+
const dirName = delegationDirName(record8.label, record8.expertActorId, record8.id);
|
|
196440
|
+
const prefixes = [`${DELEGATION_OUTBOUND_DIR}/${dirName}/`, `${SESSION_OUTPUTS_DIR}/${DELEGATION_OUTBOUND_DIR}/${dirName}/`];
|
|
196441
|
+
return collectDelegationArtifacts(record8.roundSummaries).map((a) => {
|
|
196442
|
+
const prefix = prefixes.find((p2) => a.name.startsWith(p2));
|
|
196443
|
+
return {
|
|
196444
|
+
...a,
|
|
196445
|
+
childMessageId: childRoundMessageIds(record8.childSessionId, a.round).placeholderId,
|
|
196446
|
+
...prefix ? { childPath: `${SESSION_OUTPUTS_DIR}/${a.name.slice(prefix.length)}` } : {}
|
|
196447
|
+
};
|
|
196448
|
+
});
|
|
196449
|
+
}
|
|
196438
196450
|
function childRoundMessageIds(childSessionId, round) {
|
|
196439
196451
|
const suffix = round <= 1 ? "" : `:${round}`;
|
|
196440
196452
|
return {
|
|
@@ -197560,7 +197572,7 @@ ${inboundBundle.paths.map((path41) => `- ${path41}`).join("\n")}` : text5,
|
|
|
197560
197572
|
...record8.label ? { label: record8.label } : {},
|
|
197561
197573
|
...record8.taskDigest ? { taskDigest: record8.taskDigest } : {},
|
|
197562
197574
|
roundSummaries: record8.roundSummaries,
|
|
197563
|
-
artifacts:
|
|
197575
|
+
artifacts: delegationArtifactsView(record8),
|
|
197564
197576
|
consecutiveFails: record8.consecutiveFails,
|
|
197565
197577
|
settledSeq: record8.settledSeq,
|
|
197566
197578
|
pendingInputs: await this.pendingInputs(store, record8.id),
|
|
@@ -198137,7 +198149,12 @@ function delegationReturnText(terminal, opts) {
|
|
|
198137
198149
|
}
|
|
198138
198150
|
}
|
|
198139
198151
|
if (opts.movedPaths.length > 0) {
|
|
198140
|
-
|
|
198152
|
+
const updated = new Set(opts.updatedPaths ?? []);
|
|
198153
|
+
lines.push(
|
|
198154
|
+
"",
|
|
198155
|
+
"\u4E13\u5BB6\u4EA4\u56DE\u7684\u6587\u4EF6\uFF08\u5DF2\u5728\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0B\uFF0C\u53EF\u76F4\u63A5\u8BFB\u53D6\uFF09\uFF1A",
|
|
198156
|
+
...opts.movedPaths.map((p2) => `- ${p2}\uFF08${updated.has(p2) ? "\u66F4\u65B0\uFF1A\u4E4B\u524D\u4EA4\u56DE\u8FC7\uFF0C\u8FD9\u4E00\u7248\u6709\u6539\u52A8" : "\u65B0\u4EA4"}\uFF09`)
|
|
198157
|
+
);
|
|
198141
198158
|
}
|
|
198142
198159
|
if ((opts.unchangedPaths?.length ?? 0) > 0) {
|
|
198143
198160
|
lines.push("", `\u53E6\u6709 ${opts.unchangedPaths.length} \u4E2A\u6587\u4EF6\u4E0E\u4E0A\u6B21\u4EA4\u56DE\u65F6\u9010\u5B57\u4E00\u81F4\uFF0C\u672C\u8F6E\u6CA1\u6709\u91CD\u4F20\uFF08\u4ECD\u5728\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0B\uFF0C\u8DEF\u5F84\u4E0D\u53D8\uFF09\u3002`);
|
|
@@ -198160,6 +198177,11 @@ function delegationReturnText(terminal, opts) {
|
|
|
198160
198177
|
"",
|
|
198161
198178
|
record8.state === "done" ? "\u8FD9\u4E9B\u662F\u6750\u6599\uFF0C\u4E0D\u662F\u7ED9\u7528\u6237\u7684\u6210\u54C1\uFF1A\u4F60\u6709\u8FD9\u6761\u5BF9\u8BDD\u7684\u5B8C\u6574\u4E0A\u4E0B\u6587\uFF0C\u81EA\u5DF1\u5224\u65AD\u8BE5\u8BB2\u54EA\u4E00\u53E5\u3001\u8981\u4E0D\u8981\u63A5\u7740\u5F80\u4E0B\u505A\u3002" : "\u5148\u81EA\u5DF1\u5224\u65AD\u80FD\u4E0D\u80FD\u63A5\u4F4F\uFF1A\u63A5\u7BA1\u3001\u6362\u4E2A\u505A\u6CD5\u3001\u8FD8\u662F\u91CD\u6D3E\u3002**\u522B\u628A\u4E13\u4E1A\u5931\u8D25\u539F\u6837\u4E22\u56DE\u7ED9\u7528\u6237**\u3002"
|
|
198162
198179
|
);
|
|
198180
|
+
if (opts.movedPaths.length > 0 || (opts.unchangedPaths?.length ?? 0) > 0) {
|
|
198181
|
+
lines.push(
|
|
198182
|
+
"\u4EA4\u56DE\u7684\u6587\u4EF6\u653E\u5728 `delegations/` \u4E0B\uFF0C**\u7528\u6237\u770B\u4E0D\u5230**\uFF1B\u8981\u7ED9\u7528\u6237\u770B\u7684\uFF0C\u590D\u5236\u6216\u52A0\u5DE5\u540E\u653E\u8FDB `outputs/`\uFF08\u539F\u4EF6\u522B\u79FB\u52A8\u3001\u522B\u5220\u9664\uFF09\u3002" + ((opts.updatedPaths?.length ?? 0) > 0 ? "\u6807\u4E86\u300C\u66F4\u65B0\u300D\u7684\u6587\u4EF6\u5982\u679C\u4F60\u4E4B\u524D\u653E\u8FDB\u8FC7 `outputs/`\uFF0C\u8981\u4E0D\u8981\u6362\u6210\u65B0\u7248\u7531\u4F60\u5224\u65AD\u3002" : "")
|
|
198183
|
+
);
|
|
198184
|
+
}
|
|
198163
198185
|
return lines.join("\n");
|
|
198164
198186
|
}
|
|
198165
198187
|
async function deliverDelegationReturn(deps, terminal) {
|
|
@@ -198212,11 +198234,14 @@ async function deliverDelegationReturn(deps, terminal) {
|
|
|
198212
198234
|
);
|
|
198213
198235
|
};
|
|
198214
198236
|
const bundleFiles = toBundleFiles(outbound.prepared);
|
|
198237
|
+
const previouslyDelivered = new Set(record8.roundSummaries.flatMap((round) => (round.files ?? []).map((file) => file.name)));
|
|
198215
198238
|
const text5 = delegationReturnText(terminal, {
|
|
198216
198239
|
movedPaths: bundleFiles.paths,
|
|
198217
198240
|
skipped: outbound.skipped,
|
|
198218
198241
|
recentRounds: deps.recentRounds ?? DELEGATION_RETURN_RECENT_ROUNDS,
|
|
198219
|
-
unchangedPaths: outbound.unchanged
|
|
198242
|
+
unchangedPaths: outbound.unchanged,
|
|
198243
|
+
// 「之前交回过」按台账里已经登记的文件名判——这一轮的文件要等投递之后才落账,此刻还不在里面。
|
|
198244
|
+
updatedPaths: bundleFiles.paths.filter((p2) => previouslyDelivered.has(p2))
|
|
198220
198245
|
});
|
|
198221
198246
|
const movedFileRefs = outbound.prepared.map((f2) => ({
|
|
198222
198247
|
name: f2.rel,
|
|
@@ -209352,8 +209377,12 @@ var init_memory_registry_store = __esm({
|
|
|
209352
209377
|
const keptName = v2.name ?? ex?.name;
|
|
209353
209378
|
const keptCreatedBy = ex?.createdBy ?? v2.createdBy;
|
|
209354
209379
|
const keptCreatedAt = ex?.createdAt ?? v2.createdAt;
|
|
209380
|
+
const keptAudience = v2.audience ?? ex?.audience;
|
|
209381
|
+
const keptEnabled = v2.enabledActorIds ?? ex?.enabledActorIds;
|
|
209355
209382
|
this.variables.set(mk, {
|
|
209356
209383
|
...v2,
|
|
209384
|
+
...keptAudience !== void 0 ? { audience: keptAudience } : {},
|
|
209385
|
+
...keptEnabled !== void 0 ? { enabledActorIds: [...keptEnabled] } : {},
|
|
209357
209386
|
...ex?.lastUsedAt !== void 0 ? { lastUsedAt: ex.lastUsedAt } : {},
|
|
209358
209387
|
...keptName ? { name: keptName } : {},
|
|
209359
209388
|
...keptCreatedBy ? { createdBy: keptCreatedBy } : {},
|
|
@@ -209371,12 +209400,30 @@ var init_memory_registry_store = __esm({
|
|
|
209371
209400
|
const ex = this.variables.get(mk);
|
|
209372
209401
|
if (ex) this.variables.set(mk, { ...ex, valueEncrypted, updatedAt });
|
|
209373
209402
|
}
|
|
209403
|
+
async patchVariableMeta(key, actorId, patch) {
|
|
209404
|
+
const mk = this._varKey(key, actorId);
|
|
209405
|
+
const ex = this.variables.get(mk);
|
|
209406
|
+
if (!ex) return false;
|
|
209407
|
+
const next = { ...ex, updatedAt: patch.updatedAt };
|
|
209408
|
+
if (patch.audience !== void 0) next.audience = patch.audience;
|
|
209409
|
+
if (patch.enabledActorIds !== void 0) next.enabledActorIds = [...patch.enabledActorIds];
|
|
209410
|
+
if (patch.deliveryMode !== void 0) next.deliveryMode = patch.deliveryMode;
|
|
209411
|
+
if (patch.expiresAt !== void 0) {
|
|
209412
|
+
if (patch.expiresAt === null) delete next.expiresAt;
|
|
209413
|
+
else next.expiresAt = patch.expiresAt;
|
|
209414
|
+
}
|
|
209415
|
+
if (patch.updatedBy !== void 0) next.updatedBy = patch.updatedBy;
|
|
209416
|
+
this.variables.set(mk, next);
|
|
209417
|
+
return true;
|
|
209418
|
+
}
|
|
209374
209419
|
async getVariableCiphertext(key, actorId) {
|
|
209375
209420
|
return this.variables.get(this._varKey(key, actorId))?.valueEncrypted ?? null;
|
|
209376
209421
|
}
|
|
209377
209422
|
async listVariables(actorId) {
|
|
209378
209423
|
const all2 = [...this.variables.values()];
|
|
209379
|
-
if (actorId !== void 0)
|
|
209424
|
+
if (actorId !== void 0) {
|
|
209425
|
+
return all2.filter((v2) => v2.actorId === actorId || v2.scope === "global" && (v2.audience !== "selected" || (v2.enabledActorIds ?? []).includes(actorId))).map((v2) => ({ ...v2 }));
|
|
209426
|
+
}
|
|
209380
209427
|
return all2.map((v2) => ({ ...v2 }));
|
|
209381
209428
|
}
|
|
209382
209429
|
async resolveProjectVariables(projectId2) {
|
|
@@ -209815,6 +209862,27 @@ var init_memory_trace_store = __esm({
|
|
|
209815
209862
|
usage: structuredClone(run.usage)
|
|
209816
209863
|
}));
|
|
209817
209864
|
}
|
|
209865
|
+
/** 与 PostgresTraceStore.runtimeRunActivity 同语义:按实例数全部 run(不看状态 / usage),取最近开跑。 */
|
|
209866
|
+
async runtimeRunActivity(runtimeInstanceId) {
|
|
209867
|
+
let runCount = 0;
|
|
209868
|
+
let lastRunStartedAt = null;
|
|
209869
|
+
for (const run of this.runs.values()) {
|
|
209870
|
+
if (run.runtimeInstanceId !== runtimeInstanceId) continue;
|
|
209871
|
+
runCount += 1;
|
|
209872
|
+
if (lastRunStartedAt === null || run.startedAt > lastRunStartedAt) lastRunStartedAt = run.startedAt;
|
|
209873
|
+
}
|
|
209874
|
+
return { runCount, lastRunStartedAt };
|
|
209875
|
+
}
|
|
209876
|
+
/** 与 PostgresTraceStore.listRuntimeRuns 同语义:时间窗内该实例的全部 run,按开跑正序。 */
|
|
209877
|
+
async listRuntimeRuns(runtimeInstanceId, range) {
|
|
209878
|
+
return [...this.runs.values()].filter((run) => run.runtimeInstanceId === runtimeInstanceId && run.startedAt >= range.from && run.startedAt <= range.to).sort((a, b2) => a.startedAt < b2.startedAt ? -1 : a.startedAt > b2.startedAt ? 1 : 0).map((run) => ({
|
|
209879
|
+
actorId: run.actorId,
|
|
209880
|
+
runtimeKind: run.runtimeKind,
|
|
209881
|
+
effectiveModel: run.effectiveModel,
|
|
209882
|
+
startedAt: run.startedAt,
|
|
209883
|
+
usage: run.usage ? structuredClone(run.usage) : null
|
|
209884
|
+
}));
|
|
209885
|
+
}
|
|
209818
209886
|
/* ---------- execution continuity ---------- */
|
|
209819
209887
|
async getAttempt(attemptId) {
|
|
209820
209888
|
return this.getRun(attemptId);
|
|
@@ -211852,6 +211920,415 @@ var init_uncategorized_project = __esm({
|
|
|
211852
211920
|
}
|
|
211853
211921
|
});
|
|
211854
211922
|
|
|
211923
|
+
// ../server/src/domains/actors/model-pricing.ts
|
|
211924
|
+
function lookupInMap(map, prefixKeys, candidates) {
|
|
211925
|
+
for (const candidate of candidates) {
|
|
211926
|
+
const exact = map instanceof Map ? map.get(candidate) : map[candidate];
|
|
211927
|
+
if (exact) return exact;
|
|
211928
|
+
}
|
|
211929
|
+
const prefix = prefixKeys.find((p2) => candidates.some((candidate) => candidate.startsWith(`${p2}-`) || candidate.startsWith(`${p2}:`)));
|
|
211930
|
+
if (!prefix) return null;
|
|
211931
|
+
return map instanceof Map ? map.get(prefix) ?? null : map[prefix] ?? null;
|
|
211932
|
+
}
|
|
211933
|
+
function stripBracketTier(model) {
|
|
211934
|
+
return model.replace(/\[[^\]]*\]/g, "").trim();
|
|
211935
|
+
}
|
|
211936
|
+
function modelCandidates(model) {
|
|
211937
|
+
const normalized4 = model.trim().toLowerCase();
|
|
211938
|
+
if (!normalized4) return [];
|
|
211939
|
+
const base = [
|
|
211940
|
+
normalized4,
|
|
211941
|
+
normalized4.includes(":") ? normalized4.slice(normalized4.lastIndexOf(":") + 1) : "",
|
|
211942
|
+
normalized4.includes("/") ? normalized4.slice(normalized4.lastIndexOf("/") + 1) : ""
|
|
211943
|
+
].filter(Boolean);
|
|
211944
|
+
const stripped = base.map(stripBracketTier).filter((m2) => m2 && !base.includes(m2));
|
|
211945
|
+
return [...base, ...new Set(stripped)];
|
|
211946
|
+
}
|
|
211947
|
+
function setModelPriceCacheLoader(loader) {
|
|
211948
|
+
cacheLoader = loader;
|
|
211949
|
+
}
|
|
211950
|
+
async function warmModelPriceCache() {
|
|
211951
|
+
if (!cacheLoader) return 0;
|
|
211952
|
+
try {
|
|
211953
|
+
const next = await cacheLoader();
|
|
211954
|
+
dbPrices = next;
|
|
211955
|
+
dbPrefixKeys = [...next.keys()].sort((a, b2) => b2.length - a.length);
|
|
211956
|
+
cacheLoadedAt = Date.now();
|
|
211957
|
+
return next.size;
|
|
211958
|
+
} catch (err) {
|
|
211959
|
+
console.warn(`[model-pricing] \u9884\u70ED\u4EF7\u683C\u7F13\u5B58\u5931\u8D25\uFF0C\u6CBF\u7528\u9759\u6001\u5FEB\u7167\uFF1A${String(err)}`);
|
|
211960
|
+
return 0;
|
|
211961
|
+
}
|
|
211962
|
+
}
|
|
211963
|
+
function maybeRefreshCache() {
|
|
211964
|
+
if (!cacheLoader) return;
|
|
211965
|
+
if (dbPrices && Date.now() - cacheLoadedAt < CACHE_TTL_MS2) return;
|
|
211966
|
+
if (cacheRefreshInFlight) return;
|
|
211967
|
+
cacheRefreshInFlight = warmModelPriceCache().catch(() => 0).then(() => {
|
|
211968
|
+
cacheRefreshInFlight = null;
|
|
211969
|
+
});
|
|
211970
|
+
}
|
|
211971
|
+
function priceForModel(model) {
|
|
211972
|
+
if (!model) return null;
|
|
211973
|
+
const candidates = modelCandidates(model);
|
|
211974
|
+
if (candidates.length === 0) return null;
|
|
211975
|
+
maybeRefreshCache();
|
|
211976
|
+
if (dbPrices && dbPrices.size > 0) {
|
|
211977
|
+
const fromDb = lookupInMap(dbPrices, dbPrefixKeys, candidates);
|
|
211978
|
+
if (fromDb) return fromDb;
|
|
211979
|
+
}
|
|
211980
|
+
return lookupInMap(MODEL_PRICES, STATIC_PREFIX_PRICES, candidates);
|
|
211981
|
+
}
|
|
211982
|
+
function estimateUsageCostBreakdown(model, usage) {
|
|
211983
|
+
const price = priceForModel(model);
|
|
211984
|
+
if (!price) return null;
|
|
211985
|
+
const inputTokens = Math.max(usage.inputTokens ?? 0, 0);
|
|
211986
|
+
const outputTokens = Math.max(usage.outputTokens ?? 0, 0);
|
|
211987
|
+
const cacheReadTokens = Math.max(usage.cacheReadTokens ?? 0, 0);
|
|
211988
|
+
const cacheCreationTokens = Math.max(usage.cacheCreationTokens ?? 0, 0);
|
|
211989
|
+
const cacheReadPrice = price.cachedInputPerMillion ?? price.inputPerMillion * 0.1;
|
|
211990
|
+
return {
|
|
211991
|
+
currency: price.currency,
|
|
211992
|
+
inputMicros: Math.round(inputTokens * price.inputPerMillion / 1e6 * 1e6),
|
|
211993
|
+
cacheMicros: Math.round(
|
|
211994
|
+
(cacheCreationTokens * price.inputPerMillion + cacheReadTokens * cacheReadPrice) / 1e6 * 1e6
|
|
211995
|
+
),
|
|
211996
|
+
outputMicros: Math.round(outputTokens * price.outputPerMillion / 1e6 * 1e6)
|
|
211997
|
+
};
|
|
211998
|
+
}
|
|
211999
|
+
function estimateUsageCost(model, usage) {
|
|
212000
|
+
const breakdown = estimateUsageCostBreakdown(model, usage);
|
|
212001
|
+
if (!breakdown) return null;
|
|
212002
|
+
return {
|
|
212003
|
+
currency: breakdown.currency,
|
|
212004
|
+
micros: breakdown.inputMicros + breakdown.cacheMicros + breakdown.outputMicros
|
|
212005
|
+
};
|
|
212006
|
+
}
|
|
212007
|
+
function displayCostUsdMicros(model, usage) {
|
|
212008
|
+
const stored = Math.max(usage.costUsdMicros ?? 0, 0);
|
|
212009
|
+
if (stored > 0) return stored;
|
|
212010
|
+
const estimated = estimateUsageCost(model, usage);
|
|
212011
|
+
if (!estimated || estimated.micros <= 0) return 0;
|
|
212012
|
+
if (estimated.currency === "USD") return estimated.micros;
|
|
212013
|
+
return Math.round(estimated.micros / DISPLAY_CNY_PER_USD);
|
|
212014
|
+
}
|
|
212015
|
+
var FOREIGN_MODEL_MARKUP, usd, cny, MODEL_PRICES, STATIC_PREFIX_PRICES, CACHE_TTL_MS2, dbPrices, dbPrefixKeys, cacheLoadedAt, cacheLoader, cacheRefreshInFlight;
|
|
212016
|
+
var init_model_pricing = __esm({
|
|
212017
|
+
"../server/src/domains/actors/model-pricing.ts"() {
|
|
212018
|
+
"use strict";
|
|
212019
|
+
init_src();
|
|
212020
|
+
FOREIGN_MODEL_MARKUP = 1.08;
|
|
212021
|
+
usd = (inputPerMillion, outputPerMillion, cachedInputPerMillion) => ({
|
|
212022
|
+
currency: "USD",
|
|
212023
|
+
inputPerMillion: inputPerMillion * FOREIGN_MODEL_MARKUP,
|
|
212024
|
+
outputPerMillion: outputPerMillion * FOREIGN_MODEL_MARKUP,
|
|
212025
|
+
...cachedInputPerMillion !== void 0 ? { cachedInputPerMillion: cachedInputPerMillion * FOREIGN_MODEL_MARKUP } : {}
|
|
212026
|
+
});
|
|
212027
|
+
cny = (inputPerMillion, outputPerMillion, cachedInputPerMillion) => ({
|
|
212028
|
+
currency: "CNY",
|
|
212029
|
+
inputPerMillion,
|
|
212030
|
+
outputPerMillion,
|
|
212031
|
+
...cachedInputPerMillion !== void 0 ? { cachedInputPerMillion } : {}
|
|
212032
|
+
});
|
|
212033
|
+
MODEL_PRICES = {
|
|
212034
|
+
"gpt-5.5": usd(5, 30, 0.5),
|
|
212035
|
+
"gpt-5.5-pro": usd(30, 180),
|
|
212036
|
+
"chat-latest": usd(5, 30, 0.5),
|
|
212037
|
+
"gpt-5.4-mini": usd(0.75, 4.5, 0.075),
|
|
212038
|
+
"gpt-5.4-nano": usd(0.2, 1.25, 0.02),
|
|
212039
|
+
"gpt-5.4": usd(2.5, 15, 0.25),
|
|
212040
|
+
"gpt-5.4-pro": usd(30, 180),
|
|
212041
|
+
"gpt-5.3-codex": usd(1.75, 14, 0.175),
|
|
212042
|
+
"gpt-5.2": usd(1.75, 14, 0.175),
|
|
212043
|
+
"gpt-5.2-chat-latest": usd(1.75, 14, 0.175),
|
|
212044
|
+
"gpt-5.2-pro": usd(21, 168),
|
|
212045
|
+
"gpt-5.2-codex": usd(1.75, 14, 0.175),
|
|
212046
|
+
"gpt-5.1": usd(1.25, 10, 0.125),
|
|
212047
|
+
"gpt-5.1-chat-latest": usd(1.25, 10, 0.125),
|
|
212048
|
+
"gpt-5.1-codex": usd(1.25, 10, 0.125),
|
|
212049
|
+
"gpt-5.1-codex-max": usd(1.25, 10, 0.125),
|
|
212050
|
+
"gpt-5.1-codex-mini": usd(0.25, 2, 0.025),
|
|
212051
|
+
"gpt-5-pro": usd(15, 120),
|
|
212052
|
+
"gpt-5-codex": usd(1.25, 10, 0.125),
|
|
212053
|
+
"gpt-5-search-api": usd(1.25, 10, 0.125),
|
|
212054
|
+
"gpt-5": usd(1.25, 10, 0.125),
|
|
212055
|
+
"gpt-5-mini": usd(0.25, 2, 0.025),
|
|
212056
|
+
"gpt-5-nano": usd(0.05, 0.4, 5e-3),
|
|
212057
|
+
"gpt-5-chat-latest": usd(1.25, 10, 0.125),
|
|
212058
|
+
"gpt-4.1": usd(2, 8, 0.5),
|
|
212059
|
+
"gpt-4.1-2025-04-14": usd(2, 8, 0.5),
|
|
212060
|
+
"gpt-4.1-mini": usd(0.4, 1.6, 0.1),
|
|
212061
|
+
"gpt-4.1-mini-2025-04-14": usd(0.4, 1.6, 0.1),
|
|
212062
|
+
"gpt-4.1-nano": usd(0.1, 0.4, 0.025),
|
|
212063
|
+
"gpt-4.1-nano-2025-04-14": usd(0.1, 0.4, 0.025),
|
|
212064
|
+
"o4-mini": usd(1.1, 4.4, 0.275),
|
|
212065
|
+
// o3 / o3-mini 在 codex 静态目录里可选,却一直不在价目快照里(费用记 0)。
|
|
212066
|
+
// 这两行按 OpenAI 公开价填,不是智增增快照里的行;供应商价目表同步到时以 DB 为准。
|
|
212067
|
+
"o3": usd(2, 8),
|
|
212068
|
+
"o3-mini": usd(1.1, 4.4),
|
|
212069
|
+
"claude-fable-5-1": usd(10, 50),
|
|
212070
|
+
"claude-fable-5": usd(10, 50),
|
|
212071
|
+
"claude-mythos-5-1": usd(10, 50),
|
|
212072
|
+
"claude-mythos-5": usd(10, 50),
|
|
212073
|
+
// claude-opus-5 是 Claude Code 当代默认款,却一直不在这张快照里:查不到价 → 费用记 0
|
|
212074
|
+
// (与 2026-09-04 那次 `claude-opus-5[1m]` 记 0 同一类坑)。按 Opus 档 $5/$25 补上。
|
|
212075
|
+
"claude-opus-5": usd(5, 25),
|
|
212076
|
+
"claude-opus-4-8": usd(5, 25),
|
|
212077
|
+
"claude-opus-4-7": usd(5, 25),
|
|
212078
|
+
"claude-sonnet-5": usd(2, 10),
|
|
212079
|
+
"claude-sonnet-4-6": usd(3, 15),
|
|
212080
|
+
"claude-opus-4-6": usd(5, 25),
|
|
212081
|
+
"claude-opus-4-5-20251101": usd(5, 25),
|
|
212082
|
+
"claude-opus-4-5": usd(5, 25),
|
|
212083
|
+
"claude-sonnet-4-5-20250929": usd(3, 15),
|
|
212084
|
+
// 短名同样要有:前缀比对是「表里的键是模型名的前缀」,带日期的那行匹配不上不带日期的短名。
|
|
212085
|
+
// claude-sonnet-4-5 从 2026-07-04 起就在目录里可选,却一直查不到价(费用记 0),本次一并补。
|
|
212086
|
+
"claude-sonnet-4-5": usd(3, 15),
|
|
212087
|
+
"claude-opus-4-1-20250805": usd(15, 75),
|
|
212088
|
+
"claude-opus-4-20250514": usd(15, 75),
|
|
212089
|
+
"claude-sonnet-4-20250514": usd(3, 15),
|
|
212090
|
+
"claude-3-7-sonnet-20250219": usd(3, 15),
|
|
212091
|
+
"claude-3-5-sonnet-20241022": usd(3, 15),
|
|
212092
|
+
"claude-3-5-sonnet-20240620": usd(3, 15),
|
|
212093
|
+
"claude-3-sonnet-20240229": usd(3, 15),
|
|
212094
|
+
"claude-haiku-4-5-20251001": usd(1, 5),
|
|
212095
|
+
// codebuddy 目录用点号写法(claude-sonnet-4.6),与上面的连字符写法是同一批模型,
|
|
212096
|
+
// 但字符串对不上就查不到价 → 记 0。同价另起别名行。
|
|
212097
|
+
"claude-sonnet-4.6": usd(3, 15),
|
|
212098
|
+
"claude-opus-4.7": usd(5, 25),
|
|
212099
|
+
"gemini-3.1-pro": usd(2, 12),
|
|
212100
|
+
"gemini-3-pro-preview": usd(2, 12),
|
|
212101
|
+
// Claude Code 回报的可能是不带日期的短名,短名匹配不上带日期的那行(前缀比对是「表里的键是模型名的前缀」)。
|
|
212102
|
+
"claude-haiku-4-5": usd(1, 5),
|
|
212103
|
+
"gemini-3.5-flash": usd(1.5, 9),
|
|
212104
|
+
"gemini-3.1-pro-preview": usd(2, 12),
|
|
212105
|
+
"gemini-3.1-pro-preview-customtools": usd(2, 12),
|
|
212106
|
+
"gemini-3.1-flash-lite-preview": usd(0.25, 1.5),
|
|
212107
|
+
"gemini-3-flash-preview": usd(0.5, 3),
|
|
212108
|
+
"gemini-2.5-pro": usd(1.25, 10),
|
|
212109
|
+
"gemini-2.5-flash": usd(0.3, 2.5),
|
|
212110
|
+
"gemini-2.5-flash-lite": usd(0.1, 0.4),
|
|
212111
|
+
"gemini-2.5-flash-preview-09-2025": usd(0.3, 2.5),
|
|
212112
|
+
"gemini-2.0-flash": usd(0.1, 0.4),
|
|
212113
|
+
"deepseek-v4-flash": cny(1, 2, 0.02),
|
|
212114
|
+
"deepseek-v4-pro": cny(3, 6, 0.025),
|
|
212115
|
+
"deepseek-chat": cny(2, 3),
|
|
212116
|
+
"deepseek-reasoner": cny(2, 3),
|
|
212117
|
+
"deepseek-v3.2": cny(2, 3),
|
|
212118
|
+
"deepseek-coder": cny(2, 3),
|
|
212119
|
+
"deepseek-r1": cny(4, 16),
|
|
212120
|
+
"deepseek-r1-0528": cny(4, 16),
|
|
212121
|
+
"deepseek-v3": cny(2, 8),
|
|
212122
|
+
"deepseek-v3-0324": cny(2, 8),
|
|
212123
|
+
"deepseek-v3.1": cny(4, 12),
|
|
212124
|
+
"qwen3-max": cny(6, 24),
|
|
212125
|
+
"qwen3-max-preview": cny(6, 24),
|
|
212126
|
+
"qwen3-coder-plus": cny(4, 16),
|
|
212127
|
+
"qwen3-235b-a22b": cny(4, 40),
|
|
212128
|
+
"qwen3-32b": cny(2, 20),
|
|
212129
|
+
"qwen3-30b-a3b": cny(1.5, 15),
|
|
212130
|
+
"qwen3-14b": cny(1, 10),
|
|
212131
|
+
"qwen3-8b": cny(0.5, 5),
|
|
212132
|
+
"qwen3-4b": cny(0.3, 3),
|
|
212133
|
+
"qwen3-1.7b": cny(0.3, 3),
|
|
212134
|
+
"qwen3-0.6b": cny(0.3, 3),
|
|
212135
|
+
"qwq-plus": cny(1.6, 4),
|
|
212136
|
+
"qwq-plus-latest": cny(1.6, 4),
|
|
212137
|
+
"qwq-plus-2025-03-05": cny(1.6, 4),
|
|
212138
|
+
"kimi-k2.7-code": cny(6.5, 27),
|
|
212139
|
+
"kimi-k2.6": cny(6.5, 27),
|
|
212140
|
+
"kimi-k2.5": cny(4, 21),
|
|
212141
|
+
"kimi-k2-0905-preview": cny(4, 16),
|
|
212142
|
+
"kimi-k2-0711-preview": cny(4, 16),
|
|
212143
|
+
"kimi-k2-turbo-preview": cny(8, 58)
|
|
212144
|
+
};
|
|
212145
|
+
STATIC_PREFIX_PRICES = Object.keys(MODEL_PRICES).sort((a, b2) => b2.length - a.length);
|
|
212146
|
+
CACHE_TTL_MS2 = 5 * 6e4;
|
|
212147
|
+
dbPrices = null;
|
|
212148
|
+
dbPrefixKeys = [];
|
|
212149
|
+
cacheLoadedAt = 0;
|
|
212150
|
+
cacheLoader = null;
|
|
212151
|
+
cacheRefreshInFlight = null;
|
|
212152
|
+
}
|
|
212153
|
+
});
|
|
212154
|
+
|
|
212155
|
+
// ../server/src/domains/usage/accounting.ts
|
|
212156
|
+
function nonNegative(v2) {
|
|
212157
|
+
return Math.max(v2 ?? 0, 0);
|
|
212158
|
+
}
|
|
212159
|
+
function resolvedModel(model) {
|
|
212160
|
+
const trimmed = model?.trim();
|
|
212161
|
+
return trimmed ? trimmed : null;
|
|
212162
|
+
}
|
|
212163
|
+
function splitByWeight(total, w2) {
|
|
212164
|
+
const sum = w2.input + w2.cache + w2.output;
|
|
212165
|
+
if (total === 0) return { input: 0, cache: 0, output: 0 };
|
|
212166
|
+
if (sum <= 0) return { input: 0, cache: 0, output: total };
|
|
212167
|
+
const input = Math.round(total * w2.input / sum);
|
|
212168
|
+
const cache = Math.round(total * w2.cache / sum);
|
|
212169
|
+
return { input, cache, output: total - input - cache };
|
|
212170
|
+
}
|
|
212171
|
+
function toDisplayUsd(currency, micros) {
|
|
212172
|
+
return currency === "USD" ? micros : Math.round(micros / DISPLAY_CNY_PER_USD);
|
|
212173
|
+
}
|
|
212174
|
+
function accountUsage(model, usage) {
|
|
212175
|
+
const resolved = resolvedModel(model);
|
|
212176
|
+
if (!resolved || !usage) return null;
|
|
212177
|
+
const input = nonNegative(usage.inputTokens);
|
|
212178
|
+
const output = nonNegative(usage.outputTokens);
|
|
212179
|
+
const cache = nonNegative(usage.cacheReadTokens) + nonNegative(usage.cacheCreationTokens);
|
|
212180
|
+
const tokens = { input, cache, output, total: input + cache + output };
|
|
212181
|
+
const estimated = estimateUsageCostBreakdown(resolved, usage);
|
|
212182
|
+
const weights = estimated ? { input: estimated.inputMicros, cache: estimated.cacheMicros, output: estimated.outputMicros } : null;
|
|
212183
|
+
const reported = Math.max(usage.costUsdMicros ?? 0, 0);
|
|
212184
|
+
if (reported > 0) {
|
|
212185
|
+
const parts = weights && estimated?.currency === "USD" ? splitByWeight(reported, weights) : { input: 0, cache: 0, output: reported };
|
|
212186
|
+
return {
|
|
212187
|
+
tokens,
|
|
212188
|
+
currency: "USD",
|
|
212189
|
+
amountMicros: reported,
|
|
212190
|
+
parts,
|
|
212191
|
+
displayUsdMicros: reported,
|
|
212192
|
+
displayParts: parts,
|
|
212193
|
+
source: "reported"
|
|
212194
|
+
};
|
|
212195
|
+
}
|
|
212196
|
+
if (estimated && weights) {
|
|
212197
|
+
const amount = weights.input + weights.cache + weights.output;
|
|
212198
|
+
const displayUsdMicros = toDisplayUsd(estimated.currency, amount);
|
|
212199
|
+
return {
|
|
212200
|
+
tokens,
|
|
212201
|
+
currency: estimated.currency,
|
|
212202
|
+
amountMicros: amount,
|
|
212203
|
+
parts: weights,
|
|
212204
|
+
displayUsdMicros,
|
|
212205
|
+
displayParts: estimated.currency === "USD" ? weights : splitByWeight(displayUsdMicros, weights),
|
|
212206
|
+
source: "estimated"
|
|
212207
|
+
};
|
|
212208
|
+
}
|
|
212209
|
+
return {
|
|
212210
|
+
tokens,
|
|
212211
|
+
currency: "USD",
|
|
212212
|
+
amountMicros: 0,
|
|
212213
|
+
parts: { input: 0, cache: 0, output: 0 },
|
|
212214
|
+
displayUsdMicros: 0,
|
|
212215
|
+
displayParts: { input: 0, cache: 0, output: 0 },
|
|
212216
|
+
source: "unpriced"
|
|
212217
|
+
};
|
|
212218
|
+
}
|
|
212219
|
+
function isUnpriced(account) {
|
|
212220
|
+
return account.source === "unpriced" && account.tokens.total > 0;
|
|
212221
|
+
}
|
|
212222
|
+
var init_accounting = __esm({
|
|
212223
|
+
"../server/src/domains/usage/accounting.ts"() {
|
|
212224
|
+
"use strict";
|
|
212225
|
+
init_model_pricing();
|
|
212226
|
+
}
|
|
212227
|
+
});
|
|
212228
|
+
|
|
212229
|
+
// ../server/src/domains/actors/stats.ts
|
|
212230
|
+
function emptyCostBreakdown() {
|
|
212231
|
+
return {
|
|
212232
|
+
inputUsdMicros: 0,
|
|
212233
|
+
cacheUsdMicros: 0,
|
|
212234
|
+
outputUsdMicros: 0,
|
|
212235
|
+
inputCnyMicros: 0,
|
|
212236
|
+
cacheCnyMicros: 0,
|
|
212237
|
+
outputCnyMicros: 0
|
|
212238
|
+
};
|
|
212239
|
+
}
|
|
212240
|
+
function emptySummary() {
|
|
212241
|
+
return {
|
|
212242
|
+
tokenWeek: 0,
|
|
212243
|
+
tokenWeekBreakdown: { input: 0, cache: 0, output: 0 },
|
|
212244
|
+
costWeekUsdMicros: 0,
|
|
212245
|
+
costWeekCnyMicros: 0,
|
|
212246
|
+
costWeekBreakdown: emptyCostBreakdown(),
|
|
212247
|
+
unpricedModels: []
|
|
212248
|
+
};
|
|
212249
|
+
}
|
|
212250
|
+
function addAccount(target, account) {
|
|
212251
|
+
target.tokenWeekBreakdown.input += account.tokens.input;
|
|
212252
|
+
target.tokenWeekBreakdown.cache += account.tokens.cache;
|
|
212253
|
+
target.tokenWeekBreakdown.output += account.tokens.output;
|
|
212254
|
+
target.tokenWeek += account.tokens.total;
|
|
212255
|
+
if (account.currency === "USD") {
|
|
212256
|
+
target.costWeekUsdMicros += account.amountMicros;
|
|
212257
|
+
target.costWeekBreakdown.inputUsdMicros += account.parts.input;
|
|
212258
|
+
target.costWeekBreakdown.cacheUsdMicros += account.parts.cache;
|
|
212259
|
+
target.costWeekBreakdown.outputUsdMicros += account.parts.output;
|
|
212260
|
+
} else {
|
|
212261
|
+
target.costWeekCnyMicros += account.amountMicros;
|
|
212262
|
+
target.costWeekBreakdown.inputCnyMicros += account.parts.input;
|
|
212263
|
+
target.costWeekBreakdown.cacheCnyMicros += account.parts.cache;
|
|
212264
|
+
target.costWeekBreakdown.outputCnyMicros += account.parts.output;
|
|
212265
|
+
}
|
|
212266
|
+
}
|
|
212267
|
+
function summarize2(rows) {
|
|
212268
|
+
const out = emptySummary();
|
|
212269
|
+
const unpriced = /* @__PURE__ */ new Set();
|
|
212270
|
+
for (const row of rows) {
|
|
212271
|
+
const account = accountUsage(row.model, row.usage);
|
|
212272
|
+
if (!account) continue;
|
|
212273
|
+
addAccount(out, account);
|
|
212274
|
+
if (isUnpriced(account)) unpriced.add(resolvedModel(row.model));
|
|
212275
|
+
}
|
|
212276
|
+
out.unpricedModels = [...unpriced].sort();
|
|
212277
|
+
return out;
|
|
212278
|
+
}
|
|
212279
|
+
async function actorStats(actorId, opts = {}) {
|
|
212280
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
212281
|
+
const from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3).toISOString();
|
|
212282
|
+
const to = now.toISOString();
|
|
212283
|
+
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({
|
|
212284
|
+
actorIds: [actorId],
|
|
212285
|
+
from,
|
|
212286
|
+
to,
|
|
212287
|
+
granularity: "hour"
|
|
212288
|
+
}) : []);
|
|
212289
|
+
return {
|
|
212290
|
+
actorId,
|
|
212291
|
+
usageAvailable: !!(opts.usage?.listRuns || !opts.artifactIds && opts.usage?.queryUsageStats),
|
|
212292
|
+
...opts.model ? { participatingTaskCount: participatingTaskCount(opts.model, actorId) } : {},
|
|
212293
|
+
tokenWeek: weeklyUsage.tokenWeek,
|
|
212294
|
+
tokenWeekBreakdown: weeklyUsage.tokenWeekBreakdown,
|
|
212295
|
+
costWeekUsdMicros: weeklyUsage.costWeekUsdMicros,
|
|
212296
|
+
costWeekCnyMicros: weeklyUsage.costWeekCnyMicros,
|
|
212297
|
+
costWeekBreakdown: weeklyUsage.costWeekBreakdown,
|
|
212298
|
+
usageWindow: { from, to },
|
|
212299
|
+
unpricedModels: weeklyUsage.unpricedModels
|
|
212300
|
+
};
|
|
212301
|
+
}
|
|
212302
|
+
async function listWeeklyRuns(trace, actorId, from, to, artifactIds) {
|
|
212303
|
+
const out = [];
|
|
212304
|
+
let cursor;
|
|
212305
|
+
do {
|
|
212306
|
+
const page = await trace.listRuns({ actorId, from, to, limit: 500, ...cursor ? { cursor } : {} });
|
|
212307
|
+
out.push(...page.items.filter((run) => !artifactIds || run.artifactId != null && artifactIds.has(run.artifactId)));
|
|
212308
|
+
cursor = page.nextCursor ?? void 0;
|
|
212309
|
+
} while (cursor);
|
|
212310
|
+
return out;
|
|
212311
|
+
}
|
|
212312
|
+
function summarizeWeeklyRuns(runs) {
|
|
212313
|
+
return summarize2(runs.map((run) => ({ model: run.effectiveModel, usage: run.usage })));
|
|
212314
|
+
}
|
|
212315
|
+
function summarizeWeeklyUsage(rows) {
|
|
212316
|
+
return summarize2(rows.map((row) => ({ model: row.effectiveModel, usage: row })));
|
|
212317
|
+
}
|
|
212318
|
+
function participatingTaskCount(model, actorId) {
|
|
212319
|
+
const workorders = /* @__PURE__ */ new Set();
|
|
212320
|
+
for (const artifact of model.artifacts.values()) {
|
|
212321
|
+
if (artifact.owner === actorId) workorders.add(artifact.workspace);
|
|
212322
|
+
}
|
|
212323
|
+
return workorders.size;
|
|
212324
|
+
}
|
|
212325
|
+
var init_stats = __esm({
|
|
212326
|
+
"../server/src/domains/actors/stats.ts"() {
|
|
212327
|
+
"use strict";
|
|
212328
|
+
init_accounting();
|
|
212329
|
+
}
|
|
212330
|
+
});
|
|
212331
|
+
|
|
211855
212332
|
// ../server/src/domains/projects/service.ts
|
|
211856
212333
|
function sanitizeCreatorActorId(raw) {
|
|
211857
212334
|
const id = raw?.trim();
|
|
@@ -212132,6 +212609,7 @@ var init_service4 = __esm({
|
|
|
212132
212609
|
init_projection();
|
|
212133
212610
|
init_uncategorized_project();
|
|
212134
212611
|
init_workorders();
|
|
212612
|
+
init_stats();
|
|
212135
212613
|
init_node_state();
|
|
212136
212614
|
DEFAULT_AGENT_ACTOR = "actor:agent:system";
|
|
212137
212615
|
MemoryProjectStateStore = class {
|
|
@@ -212491,6 +212969,17 @@ var init_service4 = __esm({
|
|
|
212491
212969
|
artifacts.sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt));
|
|
212492
212970
|
return { artifacts, summary: summarizeArtifacts(artifacts) };
|
|
212493
212971
|
}
|
|
212972
|
+
/** 成员概览只读本公司的项目记录与当前任务模型,不加载项目文件/用量旁账。 */
|
|
212973
|
+
async getActorParticipationStats(actorId) {
|
|
212974
|
+
if (!this.actorDirectory) throw new Error("participation source unavailable");
|
|
212975
|
+
if (!await this.actorDirectory.getActor(actorId)) return null;
|
|
212976
|
+
const projects = await this.projects.listProjects();
|
|
212977
|
+
return {
|
|
212978
|
+
actorId,
|
|
212979
|
+
createdProjectCount: projects.filter((project) => !isUncategorizedProjectId(project.id) && project.createdBy?.id === actorId).length,
|
|
212980
|
+
participatingTaskCount: participatingTaskCount(this.kernel.model, actorId)
|
|
212981
|
+
};
|
|
212982
|
+
}
|
|
212494
212983
|
/**
|
|
212495
212984
|
* 项目列表(带派生态)。
|
|
212496
212985
|
*
|
|
@@ -215467,6 +215956,9 @@ var init_company_scoped_registry = __esm({
|
|
|
215467
215956
|
async patchVariableValue(...args) {
|
|
215468
215957
|
return this.base.patchVariableValue(...args);
|
|
215469
215958
|
}
|
|
215959
|
+
async patchVariableMeta(...args) {
|
|
215960
|
+
return this.base.patchVariableMeta(...args);
|
|
215961
|
+
}
|
|
215470
215962
|
async getVariableCiphertext(...args) {
|
|
215471
215963
|
return this.base.getVariableCiphertext(...args);
|
|
215472
215964
|
}
|
|
@@ -215491,17 +215983,30 @@ function joinRel(dir, name) {
|
|
|
215491
215983
|
return dir ? `${dir}/${name}` : name;
|
|
215492
215984
|
}
|
|
215493
215985
|
async function scanSessionOutputs(bridge, loc, now = Date.now) {
|
|
215986
|
+
const locator = { workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId };
|
|
215987
|
+
if (typeof bridge.walk === "function") {
|
|
215988
|
+
const walked = await bridge.walk(loc.nodeId, { ...locator, path: SESSION_OUTPUTS_DIR }, WALK_MAX_ENTRIES).catch(() => null);
|
|
215989
|
+
if (!walked) return null;
|
|
215990
|
+
if (walked.ok) {
|
|
215991
|
+
const files2 = walked.entries.filter((entry) => entry.type === "file").map((entry) => ({ path: entry.path, size: entry.size, mtime: entry.mtime }));
|
|
215992
|
+
return { files: files2, via: "walk", tree: walked.entries };
|
|
215993
|
+
}
|
|
215994
|
+
if (walked.code === "NOT_FOUND") return { files: [], via: "walk", tree: [] };
|
|
215995
|
+
if (walked.code !== "UNSUPPORTED") return null;
|
|
215996
|
+
}
|
|
215997
|
+
const files = await scanLayered(
|
|
215998
|
+
(path41) => bridge.list(loc.nodeId, { ...locator, path: path41 }),
|
|
215999
|
+
now
|
|
216000
|
+
);
|
|
216001
|
+
return files === null ? null : { files, via: "layered" };
|
|
216002
|
+
}
|
|
216003
|
+
async function scanLayered(list2, now) {
|
|
215494
216004
|
const deadline = now() + BUDGET_MS;
|
|
215495
216005
|
const out = [];
|
|
215496
216006
|
let listCalls = 0;
|
|
215497
216007
|
const listDir = (path41) => {
|
|
215498
216008
|
listCalls += 1;
|
|
215499
|
-
return
|
|
215500
|
-
workdirKey: loc.workdirKey,
|
|
215501
|
-
runtimeKind: loc.runtimeKind,
|
|
215502
|
-
rtId: loc.rtId,
|
|
215503
|
-
path: path41
|
|
215504
|
-
});
|
|
216009
|
+
return list2(path41);
|
|
215505
216010
|
};
|
|
215506
216011
|
const root = await listDir(SESSION_OUTPUTS_DIR).catch(() => null);
|
|
215507
216012
|
if (!root) return null;
|
|
@@ -215534,23 +216039,44 @@ async function scanSessionOutputs(bridge, loc, now = Date.now) {
|
|
|
215534
216039
|
}
|
|
215535
216040
|
return out;
|
|
215536
216041
|
}
|
|
216042
|
+
async function legacyReach(tree) {
|
|
216043
|
+
const children = /* @__PURE__ */ new Map();
|
|
216044
|
+
for (const entry of tree) {
|
|
216045
|
+
const slash = entry.path.lastIndexOf("/");
|
|
216046
|
+
const parent = slash >= 0 ? entry.path.slice(0, slash) : "";
|
|
216047
|
+
const siblings = children.get(parent) ?? [];
|
|
216048
|
+
siblings.push({ name: entry.path.slice(slash + 1), type: entry.type, size: entry.size, mtime: entry.mtime });
|
|
216049
|
+
children.set(parent, siblings);
|
|
216050
|
+
}
|
|
216051
|
+
const files = await scanLayered(async (path41) => ({ ok: true, entries: children.get(path41) ?? [], absPath: "" }), () => 0);
|
|
216052
|
+
return new Set((files ?? []).map((file) => file.path));
|
|
216053
|
+
}
|
|
215537
216054
|
async function settleSessionOutputs(deps) {
|
|
215538
|
-
const
|
|
215539
|
-
if (
|
|
216055
|
+
const scan2 = await scanSessionOutputs(deps.bridge, deps.loc, deps.now);
|
|
216056
|
+
if (scan2 === null) return null;
|
|
215540
216057
|
const previous3 = await deps.readFingerprints(deps.sessionId).catch(() => null);
|
|
215541
|
-
const diff = diffSessionOutputs(
|
|
216058
|
+
const diff = diffSessionOutputs(scan2.files, previous3 ?? void 0);
|
|
216059
|
+
if (scan2.via === "walk") {
|
|
216060
|
+
if (previous3 && previous3[WALK_TABLE_MARK] === void 0) {
|
|
216061
|
+
const reach = await legacyReach(scan2.tree ?? []);
|
|
216062
|
+
diff.changed = diff.changed.filter((file) => previous3[file.path] !== void 0 || reach.has(file.path));
|
|
216063
|
+
}
|
|
216064
|
+
diff.fingerprints[WALK_TABLE_MARK] = { size: 0, mtime: "" };
|
|
216065
|
+
}
|
|
215542
216066
|
await deps.writeFingerprints(deps.sessionId, diff.fingerprints).catch(() => void 0);
|
|
215543
216067
|
return diff;
|
|
215544
216068
|
}
|
|
215545
|
-
var MAX_DEPTH, MAX_ENTRIES, MAX_LIST_CALLS, BUDGET_MS;
|
|
216069
|
+
var WALK_MAX_ENTRIES, MAX_DEPTH, MAX_ENTRIES, MAX_LIST_CALLS, BUDGET_MS, WALK_TABLE_MARK;
|
|
215546
216070
|
var init_outputs_scan = __esm({
|
|
215547
216071
|
"../server/src/domains/chat-sessions/outputs-scan.ts"() {
|
|
215548
216072
|
"use strict";
|
|
215549
216073
|
init_src();
|
|
216074
|
+
WALK_MAX_ENTRIES = 2e3;
|
|
215550
216075
|
MAX_DEPTH = 3;
|
|
215551
216076
|
MAX_ENTRIES = 100;
|
|
215552
216077
|
MAX_LIST_CALLS = 12;
|
|
215553
216078
|
BUDGET_MS = 5e3;
|
|
216079
|
+
WALK_TABLE_MARK = "#walk";
|
|
215554
216080
|
}
|
|
215555
216081
|
});
|
|
215556
216082
|
|
|
@@ -220014,6 +220540,9 @@ function variableKeyFromEnv(env = process.env) {
|
|
|
220014
220540
|
if (env.NODE_ENV === "production") throw new Error("OASIS_VAR_KEY is required in production");
|
|
220015
220541
|
return (0, import_node_crypto47.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
|
|
220016
220542
|
}
|
|
220543
|
+
function dedupe(ids2) {
|
|
220544
|
+
return [...new Set(ids2.filter((x2) => typeof x2 === "string" && x2.length > 0))];
|
|
220545
|
+
}
|
|
220017
220546
|
var import_node_crypto47, REDACTED_MARKER, ActorsService, mask, changedKeys;
|
|
220018
220547
|
var init_service5 = __esm({
|
|
220019
220548
|
"../server/src/domains/actors/service.ts"() {
|
|
@@ -220997,7 +221526,31 @@ ${input.description}
|
|
|
220997
221526
|
...v2.by ? { createdBy: v2.by, createdAt: this.now(), updatedBy: v2.by } : {},
|
|
220998
221527
|
// ADR 凭据保险库补章 §D2.2:expiresAt 写侧穿透——**显式带 key**(含 undefined),使清空(传空→undefined)
|
|
220999
221528
|
// 也落到底(store 两实现都按此覆盖)。lastUsedAt 从不由写侧传,取用时另刷(§D2.3)。
|
|
221000
|
-
expiresAt: v2.expiresAt
|
|
221529
|
+
expiresAt: v2.expiresAt,
|
|
221530
|
+
// 决策 0556:发给谁只对全局行有意义;个人档 / 项目档一律不带(存储层 COALESCE:不带 = 不动)。
|
|
221531
|
+
...v2.scope === "global" && v2.audience !== void 0 ? { audience: v2.audience } : {},
|
|
221532
|
+
...v2.scope === "global" && v2.enabledActorIds !== void 0 ? { enabledActorIds: dedupe(v2.enabledActorIds) } : {}
|
|
221533
|
+
});
|
|
221534
|
+
}
|
|
221535
|
+
/**
|
|
221536
|
+
* 只改一行变量的元数据、不碰值(决策 0556)。
|
|
221537
|
+
*
|
|
221538
|
+
* 两个用处:① 管理页「配置Agent」改发给谁(audience / enabledActorIds)——不用为了改名单去取明文重写;
|
|
221539
|
+
* ② 改到期时间 / 注入方式不用连值一起重填(此前 `putVariable` 空值即静默不写,界面只能逼人重输一遍值)。
|
|
221540
|
+
*
|
|
221541
|
+
* 行不存在返回 false,**不会凭空造一条没有值的行**。audience / enabledActorIds 只允许写在全局行上。
|
|
221542
|
+
*/
|
|
221543
|
+
async patchVariableMeta(key, selector, patch) {
|
|
221544
|
+
if (selector.actorId !== void 0 && (patch.audience !== void 0 || patch.enabledActorIds !== void 0)) {
|
|
221545
|
+
throw new ApiError(400, "BAD_REQUEST", "audience / enabledActorIds \u53EA\u80FD\u5199\u5728\u7EC4\u7EC7\u9ED8\u8BA4\u503C\uFF08\u5168\u5C40\u884C\uFF09\u4E0A\uFF0C\u4E2A\u4EBA\u6863\u6CA1\u6709\u300C\u53D1\u7ED9\u8C01\u300D");
|
|
221546
|
+
}
|
|
221547
|
+
return this.opts.store.patchVariableMeta(key, selector.actorId, {
|
|
221548
|
+
...patch.audience !== void 0 ? { audience: patch.audience } : {},
|
|
221549
|
+
...patch.enabledActorIds !== void 0 ? { enabledActorIds: dedupe(patch.enabledActorIds) } : {},
|
|
221550
|
+
...patch.deliveryMode !== void 0 ? { deliveryMode: patch.deliveryMode } : {},
|
|
221551
|
+
...patch.expiresAt !== void 0 ? { expiresAt: patch.expiresAt } : {},
|
|
221552
|
+
...patch.by ? { updatedBy: patch.by } : {},
|
|
221553
|
+
updatedAt: this.now()
|
|
221001
221554
|
});
|
|
221002
221555
|
}
|
|
221003
221556
|
/** ADR 凭据保险库补章 §D2.3:某一行变量「明文真被取用」→ 刷 lastUsedAt。行按 (key, actorId?, projectId?)
|
|
@@ -221072,7 +221625,10 @@ ${input.description}
|
|
|
221072
221625
|
// 存量行没有这三格,原样不带出去——展示侧出「—」,不拿 updatedAt 冒充 createdAt。
|
|
221073
221626
|
...r.createdBy !== void 0 ? { createdBy: r.createdBy } : {},
|
|
221074
221627
|
...r.createdAt !== void 0 ? { createdAt: r.createdAt } : {},
|
|
221075
|
-
...r.updatedBy !== void 0 ? { updatedBy: r.updatedBy } : {}
|
|
221628
|
+
...r.updatedBy !== void 0 ? { updatedBy: r.updatedBy } : {},
|
|
221629
|
+
// 决策 0556:发给谁。存量行没有 → 不带出去,读侧按「全员生效」理解。
|
|
221630
|
+
...r.audience !== void 0 ? { audience: r.audience } : {},
|
|
221631
|
+
...r.enabledActorIds !== void 0 ? { enabledActorIds: r.enabledActorIds } : {}
|
|
221076
221632
|
};
|
|
221077
221633
|
});
|
|
221078
221634
|
}
|
|
@@ -221536,415 +222092,6 @@ var init_skill_bundle = __esm({
|
|
|
221536
222092
|
}
|
|
221537
222093
|
});
|
|
221538
222094
|
|
|
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
222095
|
// ../server/src/domains/actors/list-usage.ts
|
|
221949
222096
|
function aggregateUsageByActor(runs) {
|
|
221950
222097
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -222929,6 +223076,10 @@ function actorsDomain(opts) {
|
|
|
222929
223076
|
const b2 = req.body;
|
|
222930
223077
|
if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
|
|
222931
223078
|
const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
|
|
223079
|
+
const audienceMeta = parseAudienceMeta(b2);
|
|
223080
|
+
if ((audienceMeta.audience !== void 0 || audienceMeta.enabledActorIds !== void 0) && b2.scope !== "global") {
|
|
223081
|
+
throw new ApiError(400, "BAD_REQUEST", "audience / enabledActorIds \u53EA\u80FD\u5199\u5728 scope=global \u7684\u7EC4\u7EC7\u9ED8\u8BA4\u503C\u4E0A");
|
|
223082
|
+
}
|
|
222932
223083
|
if (b2.scope !== "global" && b2.scope !== "personal" && b2.scope !== "project") {
|
|
222933
223084
|
throw new ApiError(
|
|
222934
223085
|
400,
|
|
@@ -222973,6 +223124,7 @@ function actorsDomain(opts) {
|
|
|
222973
223124
|
...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
|
|
222974
223125
|
...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
|
|
222975
223126
|
...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
|
|
223127
|
+
...audienceMeta,
|
|
222976
223128
|
// 创建者/更新者取**当前登录人**,不接受 body 传入——自报的「谁建的」等于没有这一格。
|
|
222977
223129
|
by: req.auth.actor,
|
|
222978
223130
|
expiresAt: expiresAt2
|
|
@@ -222985,6 +223137,18 @@ function actorsDomain(opts) {
|
|
|
222985
223137
|
);
|
|
222986
223138
|
return { status: 201, body: { ok: true } };
|
|
222987
223139
|
});
|
|
223140
|
+
router.patch("/api/variables/:key", async (req) => {
|
|
223141
|
+
requireVariableManager(req);
|
|
223142
|
+
const { service } = await resolveCtx(req.auth.companyId);
|
|
223143
|
+
const key = req.params.key;
|
|
223144
|
+
const b2 = req.body ?? {};
|
|
223145
|
+
const patch = parseMetaPatch(b2, { allowAudience: true });
|
|
223146
|
+
await requireSecretOwner(req, service, key, {});
|
|
223147
|
+
const ok3 = await service.patchVariableMeta(key, {}, { ...patch, by: req.auth.actor });
|
|
223148
|
+
if (!ok3) throw new ApiError(404, "VARIABLE_NOT_FOUND", `\u6CA1\u6709\u53EB ${key} \u7684\u7EC4\u7EC7\u9ED8\u8BA4\u503C`);
|
|
223149
|
+
await auditVariableChange(req, "credential_write", key, "global");
|
|
223150
|
+
return { status: 200, body: { ok: true } };
|
|
223151
|
+
});
|
|
222988
223152
|
router.delete("/api/variables/:key", async (req) => {
|
|
222989
223153
|
requireVariableManager(req);
|
|
222990
223154
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
@@ -223055,6 +223219,19 @@ function actorsDomain(opts) {
|
|
|
223055
223219
|
await auditVariableChange(req, "credential_write", b2.key, `personal:${actorId}`);
|
|
223056
223220
|
return { status: 201, body: { ok: true } };
|
|
223057
223221
|
});
|
|
223222
|
+
router.patch("/api/actors/:id/variables/:key", async (req) => {
|
|
223223
|
+
requireVariableManager(req);
|
|
223224
|
+
const { service } = await resolveCtx(req.auth.companyId);
|
|
223225
|
+
const actorId = req.params.id;
|
|
223226
|
+
const key = req.params.key;
|
|
223227
|
+
const b2 = req.body ?? {};
|
|
223228
|
+
const patch = parseMetaPatch(b2, { allowAudience: false });
|
|
223229
|
+
await requireSecretOwner(req, service, key, { actorId });
|
|
223230
|
+
const ok3 = await service.patchVariableMeta(key, { actorId }, { ...patch, by: req.auth.actor });
|
|
223231
|
+
if (!ok3) throw new ApiError(404, "VARIABLE_NOT_FOUND", `\u8FD9\u4F4D Agent \u6CA1\u6709\u5355\u72EC\u8BBE\u7F6E ${key}`);
|
|
223232
|
+
await auditVariableChange(req, "credential_write", key, `personal:${actorId}`);
|
|
223233
|
+
return { status: 200, body: { ok: true } };
|
|
223234
|
+
});
|
|
223058
223235
|
router.delete("/api/actors/:id/variables/:key", async (req) => {
|
|
223059
223236
|
requireVariableManager(req);
|
|
223060
223237
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
@@ -223657,6 +223834,43 @@ function actorsDomain(opts) {
|
|
|
223657
223834
|
});
|
|
223658
223835
|
};
|
|
223659
223836
|
}
|
|
223837
|
+
function parseAudienceMeta(b2) {
|
|
223838
|
+
const out = {};
|
|
223839
|
+
if (b2.audience !== void 0) {
|
|
223840
|
+
if (b2.audience !== "all" && b2.audience !== "selected") {
|
|
223841
|
+
throw new ApiError(400, "BAD_REQUEST", "audience \u53EA\u80FD\u662F all \u6216 selected");
|
|
223842
|
+
}
|
|
223843
|
+
out.audience = b2.audience;
|
|
223844
|
+
}
|
|
223845
|
+
if (b2.enabledActorIds !== void 0) {
|
|
223846
|
+
if (!Array.isArray(b2.enabledActorIds) || b2.enabledActorIds.some((x2) => typeof x2 !== "string" || !x2)) {
|
|
223847
|
+
throw new ApiError(400, "BAD_REQUEST", "enabledActorIds \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4");
|
|
223848
|
+
}
|
|
223849
|
+
out.enabledActorIds = b2.enabledActorIds;
|
|
223850
|
+
}
|
|
223851
|
+
return out;
|
|
223852
|
+
}
|
|
223853
|
+
function parseMetaPatch(b2, opts) {
|
|
223854
|
+
const out = {};
|
|
223855
|
+
const aud = parseAudienceMeta(b2);
|
|
223856
|
+
if ((aud.audience !== void 0 || aud.enabledActorIds !== void 0) && !opts.allowAudience) {
|
|
223857
|
+
throw new ApiError(400, "BAD_REQUEST", "\u4E2A\u4EBA\u6863\u6CA1\u6709\u300C\u53D1\u7ED9\u8C01\u300D\u2014\u2014audience / enabledActorIds \u8BF7\u5199\u5728\u7EC4\u7EC7\u9ED8\u8BA4\u503C\u4E0A");
|
|
223858
|
+
}
|
|
223859
|
+
Object.assign(out, aud);
|
|
223860
|
+
if (b2.deliveryMode !== void 0) {
|
|
223861
|
+
if (b2.deliveryMode !== "env" && b2.deliveryMode !== "ref" && b2.deliveryMode !== "broker") {
|
|
223862
|
+
throw new ApiError(400, "BAD_REQUEST", "deliveryMode \u53EA\u80FD\u662F env\u3001ref \u6216 broker");
|
|
223863
|
+
}
|
|
223864
|
+
out.deliveryMode = b2.deliveryMode;
|
|
223865
|
+
}
|
|
223866
|
+
if (b2.expiresAt !== void 0) {
|
|
223867
|
+
if (b2.expiresAt === null || b2.expiresAt === "") out.expiresAt = null;
|
|
223868
|
+
else if (typeof b2.expiresAt === "string") out.expiresAt = normalizeExpiresAt(b2.expiresAt) ?? null;
|
|
223869
|
+
else throw new ApiError(400, "BAD_REQUEST", "expiresAt \u5FC5\u987B\u662F ISO \u65F6\u95F4\u5B57\u7B26\u4E32\u6216 null");
|
|
223870
|
+
}
|
|
223871
|
+
if (Object.keys(out).length === 0) throw new ApiError(400, "BAD_REQUEST", "\u81F3\u5C11\u8981\u6539\u4E00\u6837\uFF1Aaudience / enabledActorIds / deliveryMode / expiresAt");
|
|
223872
|
+
return out;
|
|
223873
|
+
}
|
|
223660
223874
|
var CREDENTIAL_REVEAL_SOURCES, RevealRateLimiter;
|
|
223661
223875
|
var init_routes3 = __esm({
|
|
223662
223876
|
"../server/src/domains/actors/routes.ts"() {
|
|
@@ -224235,6 +224449,20 @@ ${errs.join("\n")}`);
|
|
|
224235
224449
|
});
|
|
224236
224450
|
return { status: 200, body: toApiProjectArtifacts(list2, req.query) };
|
|
224237
224451
|
});
|
|
224452
|
+
router.get("/api/actors/:id/participation-stats", async (req) => {
|
|
224453
|
+
const service = await svc(req);
|
|
224454
|
+
let stats;
|
|
224455
|
+
try {
|
|
224456
|
+
stats = await service.getActorParticipationStats(req.params.id);
|
|
224457
|
+
} catch (error2) {
|
|
224458
|
+
if (error2 instanceof Error && error2.message === "participation source unavailable") {
|
|
224459
|
+
throw new ApiError(503, "UNAVAILABLE", "\u6210\u5458\u7EDF\u8BA1\u6570\u636E\u6E90\u672A\u63A5\u5165");
|
|
224460
|
+
}
|
|
224461
|
+
throw error2;
|
|
224462
|
+
}
|
|
224463
|
+
if (!stats) throw new ApiError(404, "NOT_FOUND", "\u8FD9\u4E2A\u7EC4\u7EC7\u91CC\u6CA1\u6709\u8FD9\u4F4D\u6210\u5458");
|
|
224464
|
+
return { status: 200, body: stats };
|
|
224465
|
+
});
|
|
224238
224466
|
router.get("/api/actors/:id/files-view", async (req) => {
|
|
224239
224467
|
const items = await (await svc(req)).listActorFiles(req.params.id);
|
|
224240
224468
|
return {
|
|
@@ -229122,9 +229350,50 @@ function reasonFromWorkdirCode(code2) {
|
|
|
229122
229350
|
function joinRel2(dir, name) {
|
|
229123
229351
|
return dir ? `${dir}/${name}` : name;
|
|
229124
229352
|
}
|
|
229353
|
+
function workdirFileItem(rel, name, size, mtime) {
|
|
229354
|
+
return {
|
|
229355
|
+
id: `workdir:${rel}`,
|
|
229356
|
+
name,
|
|
229357
|
+
kind: "file",
|
|
229358
|
+
// 节点侧列目录只报 name/type/size/mtime,没有 MIME——诚实留 null,
|
|
229359
|
+
// 不拿后缀猜(红线:取不到就是取不到,别拿别的字段顶)。
|
|
229360
|
+
content_type: null,
|
|
229361
|
+
size,
|
|
229362
|
+
updated_at: mtime,
|
|
229363
|
+
source: "workdir",
|
|
229364
|
+
blob_ref: null,
|
|
229365
|
+
path: rel,
|
|
229366
|
+
message_id: null,
|
|
229367
|
+
// 工作区文件本期不开写回(brief §4①:要动 node↔server 协议帧,超预算)。
|
|
229368
|
+
// 这里是**按能力不渲染**的判据,不是置灰假钮。
|
|
229369
|
+
editable: false
|
|
229370
|
+
};
|
|
229371
|
+
}
|
|
229125
229372
|
async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
229126
|
-
const deadline = now() + WORKDIR_WALK_BUDGET_MS;
|
|
229127
229373
|
const items = [];
|
|
229374
|
+
if (typeof bridge.walk === "function") {
|
|
229375
|
+
const walked = await bridge.walk(
|
|
229376
|
+
loc.nodeId,
|
|
229377
|
+
{ workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: "" },
|
|
229378
|
+
WORKDIR_TREE_MAX_ENTRIES
|
|
229379
|
+
);
|
|
229380
|
+
if (walked.ok) {
|
|
229381
|
+
let truncated2 = walked.truncated === true;
|
|
229382
|
+
for (const entry of walked.entries) {
|
|
229383
|
+
if (entry.type !== "file") continue;
|
|
229384
|
+
if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
|
|
229385
|
+
truncated2 = true;
|
|
229386
|
+
break;
|
|
229387
|
+
}
|
|
229388
|
+
const slash = entry.path.lastIndexOf("/");
|
|
229389
|
+
items.push(workdirFileItem(entry.path, entry.path.slice(slash + 1), entry.size, entry.mtime));
|
|
229390
|
+
}
|
|
229391
|
+
return { ok: true, items, truncated: truncated2 };
|
|
229392
|
+
}
|
|
229393
|
+
if (walked.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
|
|
229394
|
+
if (walked.code !== "UNSUPPORTED") return { ok: false, reason: reasonFromWorkdirCode(walked.code) };
|
|
229395
|
+
}
|
|
229396
|
+
const deadline = now() + WORKDIR_WALK_BUDGET_MS;
|
|
229128
229397
|
let listCalls = 0;
|
|
229129
229398
|
let truncated = false;
|
|
229130
229399
|
const listDir = (path41) => {
|
|
@@ -229159,24 +229428,7 @@ async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
|
229159
229428
|
truncated = true;
|
|
229160
229429
|
continue;
|
|
229161
229430
|
}
|
|
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
|
-
});
|
|
229431
|
+
items.push(workdirFileItem(joinRel2(dir, entry.name), entry.name, entry.size, entry.mtime));
|
|
229180
229432
|
}
|
|
229181
229433
|
}
|
|
229182
229434
|
if (depth === WORKDIR_WALK_MAX_DEPTH || nextDirs.length === 0) {
|
|
@@ -229298,7 +229550,7 @@ async function findMaterializedAttachmentCopies(messageItems, workdirItems, read
|
|
|
229298
229550
|
}
|
|
229299
229551
|
return copies;
|
|
229300
229552
|
}
|
|
229301
|
-
var import_node_crypto51, WORKDIR_WALK_MAX_DEPTH, WORKDIR_WALK_MAX_ENTRIES, WORKDIR_WALK_MAX_LIST_CALLS, WORKDIR_WALK_BUDGET_MS;
|
|
229553
|
+
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
229554
|
var init_files_view = __esm({
|
|
229303
229555
|
"../server/src/domains/chat-sessions/files-view.ts"() {
|
|
229304
229556
|
"use strict";
|
|
@@ -229309,6 +229561,7 @@ var init_files_view = __esm({
|
|
|
229309
229561
|
WORKDIR_WALK_MAX_ENTRIES = 200;
|
|
229310
229562
|
WORKDIR_WALK_MAX_LIST_CALLS = 20;
|
|
229311
229563
|
WORKDIR_WALK_BUDGET_MS = 1e4;
|
|
229564
|
+
WORKDIR_TREE_MAX_ENTRIES = 2e3;
|
|
229312
229565
|
}
|
|
229313
229566
|
});
|
|
229314
229567
|
|
|
@@ -231543,6 +231796,84 @@ var init_connect_script = __esm({
|
|
|
231543
231796
|
}
|
|
231544
231797
|
});
|
|
231545
231798
|
|
|
231799
|
+
// ../server/src/domains/nodes/runtime-activity.ts
|
|
231800
|
+
function isRuntimeRunReader(store) {
|
|
231801
|
+
const s2 = store;
|
|
231802
|
+
return !!s2 && typeof s2.runtimeRunActivity === "function" && typeof s2.listRuntimeRuns === "function";
|
|
231803
|
+
}
|
|
231804
|
+
function parseRuntimeUsageWindow(query, granularity, nowMs) {
|
|
231805
|
+
const toRaw = query.get("to");
|
|
231806
|
+
const fromRaw = query.get("from");
|
|
231807
|
+
const toMs = toRaw ? Date.parse(toRaw) : nowMs;
|
|
231808
|
+
if (!Number.isFinite(toMs)) return "to \u4E0D\u662F\u5408\u6CD5\u7684 ISO \u65F6\u95F4";
|
|
231809
|
+
const fromMs = fromRaw ? Date.parse(fromRaw) : toMs - DEFAULT_WINDOW_MS[granularity];
|
|
231810
|
+
if (!Number.isFinite(fromMs)) return "from \u4E0D\u662F\u5408\u6CD5\u7684 ISO \u65F6\u95F4";
|
|
231811
|
+
if (fromMs > toMs) return "from \u4E0D\u80FD\u665A\u4E8E to";
|
|
231812
|
+
if (toMs - fromMs > MAX_WINDOW_MS) return "\u65F6\u95F4\u7A97\u6700\u957F 92 \u5929";
|
|
231813
|
+
return { from: new Date(fromMs).toISOString(), to: new Date(toMs).toISOString() };
|
|
231814
|
+
}
|
|
231815
|
+
function runtimeUsageBucket(startedAt, granularity) {
|
|
231816
|
+
const ms = Date.parse(startedAt);
|
|
231817
|
+
if (!Number.isFinite(ms)) return null;
|
|
231818
|
+
const d = new Date(ms);
|
|
231819
|
+
if (granularity === "day") return d.toISOString().slice(0, 10);
|
|
231820
|
+
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours())).toISOString();
|
|
231821
|
+
}
|
|
231822
|
+
function bucketRuntimeRuns(runtimeInstanceId, runs, granularity) {
|
|
231823
|
+
const rows = /* @__PURE__ */ new Map();
|
|
231824
|
+
for (const run of runs) {
|
|
231825
|
+
const bucket = runtimeUsageBucket(run.startedAt, granularity);
|
|
231826
|
+
if (!bucket) continue;
|
|
231827
|
+
const model = resolvedModel(run.effectiveModel);
|
|
231828
|
+
const key = `${bucket}\0${run.actorId}\0${model ?? ""}`;
|
|
231829
|
+
let row = rows.get(key);
|
|
231830
|
+
if (!row) {
|
|
231831
|
+
row = {
|
|
231832
|
+
actorId: run.actorId,
|
|
231833
|
+
runtimeInstanceId,
|
|
231834
|
+
runtimeKind: run.runtimeKind,
|
|
231835
|
+
effectiveModel: model,
|
|
231836
|
+
bucket,
|
|
231837
|
+
runCount: 0,
|
|
231838
|
+
inputTokens: 0,
|
|
231839
|
+
outputTokens: 0,
|
|
231840
|
+
totalTokens: 0,
|
|
231841
|
+
cacheReadTokens: 0,
|
|
231842
|
+
cacheCreationTokens: 0,
|
|
231843
|
+
costUsdMicros: 0
|
|
231844
|
+
};
|
|
231845
|
+
rows.set(key, row);
|
|
231846
|
+
}
|
|
231847
|
+
row.runCount += 1;
|
|
231848
|
+
const usage = run.usage;
|
|
231849
|
+
if (!usage) continue;
|
|
231850
|
+
const input = nonNegative2(usage.inputTokens);
|
|
231851
|
+
const output = nonNegative2(usage.outputTokens);
|
|
231852
|
+
row.inputTokens += input;
|
|
231853
|
+
row.outputTokens += output;
|
|
231854
|
+
row.totalTokens += input + output;
|
|
231855
|
+
row.cacheReadTokens += nonNegative2(usage.cacheReadTokens);
|
|
231856
|
+
row.cacheCreationTokens += nonNegative2(usage.cacheCreationTokens);
|
|
231857
|
+
const account = accountUsage(model, usage);
|
|
231858
|
+
row.costUsdMicros += account ? account.displayUsdMicros : nonNegative2(usage.costUsdMicros);
|
|
231859
|
+
}
|
|
231860
|
+
return [...rows.values()].sort((a, b2) => {
|
|
231861
|
+
if (a.bucket !== b2.bucket) return a.bucket < b2.bucket ? 1 : -1;
|
|
231862
|
+
if (a.actorId !== b2.actorId) return a.actorId < b2.actorId ? -1 : 1;
|
|
231863
|
+
return (a.effectiveModel ?? "").localeCompare(b2.effectiveModel ?? "");
|
|
231864
|
+
});
|
|
231865
|
+
}
|
|
231866
|
+
var DEFAULT_WINDOW_MS, MAX_WINDOW_MS, nonNegative2;
|
|
231867
|
+
var init_runtime_activity = __esm({
|
|
231868
|
+
"../server/src/domains/nodes/runtime-activity.ts"() {
|
|
231869
|
+
"use strict";
|
|
231870
|
+
init_accounting();
|
|
231871
|
+
DEFAULT_WINDOW_MS = { hour: 24 * 36e5, day: 30 * 864e5 };
|
|
231872
|
+
MAX_WINDOW_MS = 92 * 864e5;
|
|
231873
|
+
nonNegative2 = (v2) => Math.max(v2 ?? 0, 0);
|
|
231874
|
+
}
|
|
231875
|
+
});
|
|
231876
|
+
|
|
231546
231877
|
// ../server/src/node-health.ts
|
|
231547
231878
|
function deriveNodeHealth(input) {
|
|
231548
231879
|
const staleMs = input.staleMs ?? 9e4;
|
|
@@ -231752,6 +232083,19 @@ function nodesDomain(deps) {
|
|
|
231752
232083
|
hubWired = true;
|
|
231753
232084
|
const { nodeStore } = deps;
|
|
231754
232085
|
const now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
232086
|
+
const HEARTBEAT_PERSIST_MS = 6e4;
|
|
232087
|
+
const heartbeatPersistedAt = /* @__PURE__ */ new Map();
|
|
232088
|
+
hub.addMessageListener((daemonId) => {
|
|
232089
|
+
if (!nodeStore.touchNodeSeen) return;
|
|
232090
|
+
const ms = Date.now();
|
|
232091
|
+
const prev = heartbeatPersistedAt.get(daemonId) ?? 0;
|
|
232092
|
+
if (ms - prev < HEARTBEAT_PERSIST_MS) return;
|
|
232093
|
+
heartbeatPersistedAt.set(daemonId, ms);
|
|
232094
|
+
void nodeStore.touchNodeSeen(daemonId, new Date(ms).toISOString()).catch((err) => {
|
|
232095
|
+
heartbeatPersistedAt.delete(daemonId);
|
|
232096
|
+
console.warn(`[nodes] \u5FC3\u8DF3\u843D\u5E93\u5931\u8D25(node=${daemonId})\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
232097
|
+
});
|
|
232098
|
+
});
|
|
231755
232099
|
hub.addMessageListener(async (daemonId, msg) => {
|
|
231756
232100
|
if (msg.type !== "hello") return;
|
|
231757
232101
|
const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
|
|
@@ -231820,6 +232164,23 @@ function nodesDomain(deps) {
|
|
|
231820
232164
|
};
|
|
231821
232165
|
tryWire();
|
|
231822
232166
|
if (!hubWired) wirePoll = setInterval(tryWire, 100);
|
|
232167
|
+
async function forbidUnlessOrgManager(req, verb) {
|
|
232168
|
+
if (!deps.resolveMemberRole) return null;
|
|
232169
|
+
const role = await deps.resolveMemberRole(req.auth.companyId, req.auth.actor);
|
|
232170
|
+
if (role === "owner" || role === "admin") return null;
|
|
232171
|
+
return {
|
|
232172
|
+
status: 403,
|
|
232173
|
+
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` }
|
|
232174
|
+
};
|
|
232175
|
+
}
|
|
232176
|
+
async function runtimeInScope(req) {
|
|
232177
|
+
const runtimeId = req.params["runtimeId"];
|
|
232178
|
+
if (!runtimeId) return { denied: { status: 400, body: { error: "missing runtimeId" } } };
|
|
232179
|
+
const scope = req.auth.companyId !== void 0 ? { companyId: req.auth.companyId } : void 0;
|
|
232180
|
+
const rt = (await deps.nodeStore.listRuntimes(void 0, scope)).find((r) => r.id === runtimeId);
|
|
232181
|
+
if (!rt) return { denied: { status: 404, body: { error: "runtime not found" } } };
|
|
232182
|
+
return { runtimeId };
|
|
232183
|
+
}
|
|
231823
232184
|
async function syncConnectedDaemon(daemonId, hub) {
|
|
231824
232185
|
const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
|
|
231825
232186
|
if (!d) return;
|
|
@@ -231848,6 +232209,13 @@ function nodesDomain(deps) {
|
|
|
231848
232209
|
const outOfSync = nodes.filter((n) => onlineIds.has(n.id) && (n.status !== "online" || (runtimesByNode.get(n.id) ?? []).some((r) => r.status !== "online")));
|
|
231849
232210
|
await Promise.all(outOfSync.map((n) => syncConnectedDaemon(n.id, hub)));
|
|
231850
232211
|
}
|
|
232212
|
+
function heartbeatMsOf(n, offline) {
|
|
232213
|
+
const live = hub?.lastSeenAt(n.id);
|
|
232214
|
+
if (live !== void 0) return live;
|
|
232215
|
+
if (!offline) return void 0;
|
|
232216
|
+
const ms = Date.parse(n.lastSeenAt);
|
|
232217
|
+
return Number.isNaN(ms) ? void 0 : ms;
|
|
232218
|
+
}
|
|
231851
232219
|
const items = await Promise.all(nodes.map(async (n) => {
|
|
231852
232220
|
const rts = runtimesByNode.get(n.id) ?? [];
|
|
231853
232221
|
const daemonVersion = daemonVersionById.get(n.id);
|
|
@@ -231877,7 +232245,16 @@ function nodesDomain(deps) {
|
|
|
231877
232245
|
...deps.nodeHealth ? {
|
|
231878
232246
|
health: deriveNodeHealth({
|
|
231879
232247
|
online: onlineIds.has(n.id),
|
|
231880
|
-
|
|
232248
|
+
/*
|
|
232249
|
+
* 心跳时钟:在线节点读 hub 内存(每帧刷新,最准);**离线节点回落到落库的
|
|
232250
|
+
* `lastSeenAt`**——那是这台机器最后一次跟服务端说上话的时刻(心跳每分钟落一次库,
|
|
232251
|
+
* 见上面的 `HEARTBEAT_PERSIST_MS`;断开那一刻 `setNodeOnline(false)` 也会写一次)。
|
|
232252
|
+
* 运行时页「工作情况」列的离线行显示的「断连时间 X」读的就是它。
|
|
232253
|
+
*
|
|
232254
|
+
* 不影响健康判定:`healthy = online && 心跳新鲜 && 连败未超阈`,离线节点 `online=false`
|
|
232255
|
+
* 已经把结论钉死成不健康,这里补进来的只是**解释**用的时刻(设计稿 4.5 ⑤ 的口径)。
|
|
232256
|
+
*/
|
|
232257
|
+
...heartbeatMsOf(n, nodeOffline) !== void 0 ? { lastHeartbeatMs: heartbeatMsOf(n, nodeOffline) } : {},
|
|
231881
232258
|
activeRunCount,
|
|
231882
232259
|
...deps.nodeHealth.snapshot(n.id) ? { snapshot: deps.nodeHealth.snapshot(n.id) } : {},
|
|
231883
232260
|
now: healthNow
|
|
@@ -232047,6 +232424,8 @@ function nodesDomain(deps) {
|
|
|
232047
232424
|
router.delete("/api/runtimes/:runtimeId", async (req) => {
|
|
232048
232425
|
const runtimeId = req.params["runtimeId"];
|
|
232049
232426
|
if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
|
|
232427
|
+
const forbidden = await forbidUnlessOrgManager(req, "\u5220\u9664\u8FD0\u884C\u65F6");
|
|
232428
|
+
if (forbidden) return forbidden;
|
|
232050
232429
|
const runtime = (await deps.nodeStore.listRuntimes()).find((r) => r.id === runtimeId);
|
|
232051
232430
|
const { nodeDeleted } = await deps.nodeStore.deleteRuntime(runtimeId);
|
|
232052
232431
|
if (nodeDeleted && runtime) revokeNodeAccess(runtime.nodeId);
|
|
@@ -232105,6 +232484,23 @@ function nodesDomain(deps) {
|
|
|
232105
232484
|
await deps.registry.upsertRuntimeModelOverride(override);
|
|
232106
232485
|
return { status: 200, body: { override } };
|
|
232107
232486
|
});
|
|
232487
|
+
router.get("/api/runtimes/:runtimeId/activity", async (req) => {
|
|
232488
|
+
const found = await runtimeInScope(req);
|
|
232489
|
+
if (found.denied) return found.denied;
|
|
232490
|
+
const reader = await deps.runtimeRuns?.(req.auth.companyId);
|
|
232491
|
+
const activity = reader ? await reader.runtimeRunActivity(found.runtimeId) : null;
|
|
232492
|
+
return { status: 200, body: { activity } };
|
|
232493
|
+
});
|
|
232494
|
+
router.get("/api/runtimes/:runtimeId/usage", async (req) => {
|
|
232495
|
+
const found = await runtimeInScope(req);
|
|
232496
|
+
if (found.denied) return found.denied;
|
|
232497
|
+
const granularity = req.query.get("granularity") === "day" ? "day" : "hour";
|
|
232498
|
+
const window2 = parseRuntimeUsageWindow(req.query, granularity, Date.now());
|
|
232499
|
+
if (typeof window2 === "string") return { status: 400, body: { error: window2 } };
|
|
232500
|
+
const reader = await deps.runtimeRuns?.(req.auth.companyId);
|
|
232501
|
+
const runs = reader ? await reader.listRuntimeRuns(found.runtimeId, window2) : null;
|
|
232502
|
+
return { status: 200, body: { items: bucketRuntimeRuns(found.runtimeId, runs ?? [], granularity) } };
|
|
232503
|
+
});
|
|
232108
232504
|
router.get("/api/runtimes/:runtimeId/config", async (req) => {
|
|
232109
232505
|
const runtimeId = req.params["runtimeId"];
|
|
232110
232506
|
if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
|
|
@@ -232114,6 +232510,8 @@ function nodesDomain(deps) {
|
|
|
232114
232510
|
router.put("/api/runtimes/:runtimeId/config", async (req) => {
|
|
232115
232511
|
const runtimeId = req.params["runtimeId"];
|
|
232116
232512
|
if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
|
|
232513
|
+
const forbidden = await forbidUnlessOrgManager(req, "\u4FEE\u6539\u56E2\u961F\u9ED8\u8BA4\u6A21\u578B");
|
|
232514
|
+
if (forbidden) return forbidden;
|
|
232117
232515
|
const body2 = req.body ?? {};
|
|
232118
232516
|
const cfg = {
|
|
232119
232517
|
runtimeId,
|
|
@@ -232151,6 +232549,7 @@ var init_routes7 = __esm({
|
|
|
232151
232549
|
init_src();
|
|
232152
232550
|
init_src6();
|
|
232153
232551
|
init_connect_script();
|
|
232552
|
+
init_runtime_activity();
|
|
232154
232553
|
init_node_health();
|
|
232155
232554
|
}
|
|
232156
232555
|
});
|
|
@@ -232325,6 +232724,10 @@ var init_node_store = __esm({
|
|
|
232325
232724
|
n.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
232326
232725
|
}
|
|
232327
232726
|
}
|
|
232727
|
+
async touchNodeSeen(id, atIso) {
|
|
232728
|
+
const n = this.nodes.get(id);
|
|
232729
|
+
if (n && n.status !== "deleted") n.lastSeenAt = atIso;
|
|
232730
|
+
}
|
|
232328
232731
|
async setNodeRuntimesOnline(nodeId, online) {
|
|
232329
232732
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
232330
232733
|
for (const rt of this.runtimes.values()) {
|
|
@@ -232444,6 +232847,10 @@ var init_node_store = __esm({
|
|
|
232444
232847
|
await super.setNodeOnline(id, online);
|
|
232445
232848
|
this.flush();
|
|
232446
232849
|
}
|
|
232850
|
+
async touchNodeSeen(id, atIso) {
|
|
232851
|
+
await super.touchNodeSeen(id, atIso);
|
|
232852
|
+
this.flush();
|
|
232853
|
+
}
|
|
232447
232854
|
async setNodeRuntimesOnline(nodeId, online) {
|
|
232448
232855
|
await super.setNodeRuntimesOnline(nodeId, online);
|
|
232449
232856
|
this.flush();
|
|
@@ -235958,7 +236365,9 @@ function createChatSessionsDomain(opts) {
|
|
|
235958
236365
|
的去重装置,直接发给前端等于给了一条 `messages[]` 里不存在的 id ——追加转达卡全部掉进
|
|
235959
236366
|
floating 堆在流末尾,同一条消息上的两条委派还会因后缀不同被拆成两张单专家卡。 */
|
|
235960
236367
|
touches: touches.map((t) => ({ messageId: touchAnchorMessageId(t.messageId), kind: t.kind, at: t.at })),
|
|
235961
|
-
artifacts
|
|
236368
|
+
/* 与协调者读口**同一个函数**——两侧 artifacts 有「逐字同源」的既有断言。它顺带给每份产物
|
|
236369
|
+
填上 `childMessageId`(子会话面板把产出卡挂到产出它的那一轮下面要用)。 */
|
|
236370
|
+
artifacts: delegationArtifactsView(record8),
|
|
235962
236371
|
createdAt: record8.createdAt,
|
|
235963
236372
|
...record8.settledAt ? { settledAt: record8.settledAt } : {}
|
|
235964
236373
|
});
|
|
@@ -236246,6 +236655,7 @@ var init_chat_sessions = __esm({
|
|
|
236246
236655
|
init_escalation_opening();
|
|
236247
236656
|
init_workorders();
|
|
236248
236657
|
init_chat_parts();
|
|
236658
|
+
init_service3();
|
|
236249
236659
|
init_injected_context();
|
|
236250
236660
|
init_deletion();
|
|
236251
236661
|
init_chat_turn_gate();
|
|
@@ -240644,6 +241054,29 @@ var init_daemon_adapter = __esm({
|
|
|
240644
241054
|
maxBytes
|
|
240645
241055
|
}));
|
|
240646
241056
|
}
|
|
241057
|
+
/**
|
|
241058
|
+
* 一次列全 `loc.path` 下的整棵子树(ADR 0554)。节点没自报能力位 → 不发帧、直接回 `UNSUPPORTED`,
|
|
241059
|
+
* 调用方退回逐层 `list`。判据取 hello 里的能力位,不靠「发了看回不回」:老节点收到不认识的帧
|
|
241060
|
+
* 只会静默丢掉,那样要白等满 30 秒超时。
|
|
241061
|
+
*/
|
|
241062
|
+
walk(nodeId, loc, maxEntries) {
|
|
241063
|
+
if (!this.nodeSupportsWalk(nodeId)) return Promise.resolve({ ok: false, code: "UNSUPPORTED" });
|
|
241064
|
+
return this.request(nodeId, (requestId) => ({
|
|
241065
|
+
type: "workdir_walk",
|
|
241066
|
+
requestId,
|
|
241067
|
+
workdirKey: loc.workdirKey,
|
|
241068
|
+
runtimeKind: loc.runtimeKind,
|
|
241069
|
+
rtId: loc.rtId ?? null,
|
|
241070
|
+
path: loc.path,
|
|
241071
|
+
maxEntries
|
|
241072
|
+
}));
|
|
241073
|
+
}
|
|
241074
|
+
nodeSupportsWalk(nodeId) {
|
|
241075
|
+
const connectedDaemons = this.hub.connectedDaemons;
|
|
241076
|
+
if (typeof connectedDaemons !== "function") return false;
|
|
241077
|
+
const daemon = connectedDaemons.call(this.hub).find((candidate) => candidate.daemonId === nodeId);
|
|
241078
|
+
return daemon?.meta.supportsWorkdirWalk === true;
|
|
241079
|
+
}
|
|
240647
241080
|
request(nodeId, buildFrame) {
|
|
240648
241081
|
const requestId = (0, import_node_crypto65.randomUUID)();
|
|
240649
241082
|
const sent = this.hub.dispatch(nodeId, buildFrame(requestId));
|
|
@@ -240658,7 +241091,7 @@ var init_daemon_adapter = __esm({
|
|
|
240658
241091
|
});
|
|
240659
241092
|
}
|
|
240660
241093
|
onDaemonMessage(msg) {
|
|
240661
|
-
if (msg.type !== "workdir_list_result" && msg.type !== "workdir_read_result") return;
|
|
241094
|
+
if (msg.type !== "workdir_list_result" && msg.type !== "workdir_read_result" && msg.type !== "workdir_walk_result") return;
|
|
240662
241095
|
const entry = this.pending.get(msg.requestId);
|
|
240663
241096
|
if (!entry) return;
|
|
240664
241097
|
this.pending.delete(msg.requestId);
|
|
@@ -242145,7 +242578,10 @@ function rowToVariable(row) {
|
|
|
242145
242578
|
// 创建者 / 创建时间 / 更新者:2026-09-10 之前的行没有 → 不带这一格,展示侧出「—」。
|
|
242146
242579
|
...row.created_by ? { createdBy: row.created_by } : {},
|
|
242147
242580
|
...row.created_at ? { createdAt: row.created_at } : {},
|
|
242148
|
-
...row.updated_by ? { updatedBy: row.updated_by } : {}
|
|
242581
|
+
...row.updated_by ? { updatedBy: row.updated_by } : {},
|
|
242582
|
+
// 决策 0556:NULL(存量行)不带出去——读侧按「全员生效」理解,与决策之前一致。
|
|
242583
|
+
...row.audience === "all" || row.audience === "selected" ? { audience: row.audience } : {},
|
|
242584
|
+
...Array.isArray(row.enabled_actor_ids) ? { enabledActorIds: row.enabled_actor_ids } : {}
|
|
242149
242585
|
};
|
|
242150
242586
|
}
|
|
242151
242587
|
var ident4, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
|
|
@@ -242320,6 +242756,8 @@ var init_postgres_registry = __esm({
|
|
|
242320
242756
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS created_by text`);
|
|
242321
242757
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS created_at text`);
|
|
242322
242758
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS updated_by text`);
|
|
242759
|
+
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS audience text`);
|
|
242760
|
+
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS enabled_actor_ids text[]`);
|
|
242323
242761
|
await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_key_actor_uq"`);
|
|
242324
242762
|
await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_scope_uq"`);
|
|
242325
242763
|
await pool.query(`
|
|
@@ -242825,14 +243263,18 @@ var init_postgres_registry = __esm({
|
|
|
242825
243263
|
/* created_by / created_at 走 COALESCE(**旧值**, 新值):**第一次落行才写**,之后每次 upsert
|
|
242826
243264
|
都保留原值——否则「谁建的」会被最后一次改的人顶掉,这一格就没有意义了。
|
|
242827
243265
|
updated_by 反过来:每次都覆盖。三者与 name 的 COALESCE 方向相反,别照抄。 */
|
|
242828
|
-
|
|
242829
|
-
|
|
243266
|
+
/* audience / enabled_actor_ids(决策 0556)走 COALESCE(**新值**, 旧值):不传 = 不动——
|
|
243267
|
+
改默认值时手上不带「发给谁」,普通赋值会把「仅指定 Agent」悄悄打回全员生效。 */
|
|
243268
|
+
`INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at, expires_at, name, created_by, created_at, updated_by, audience, enabled_actor_ids)
|
|
243269
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)
|
|
242830
243270
|
ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
|
|
242831
243271
|
SET scope=$2, actor_id=$3, project_id=$4, connector_id=$5, overrides=$6, delivery_mode=$7, value_encrypted=$8, updated_at=$9, expires_at=$10,
|
|
242832
243272
|
name=COALESCE($11, ${this.s}.variables.name),
|
|
242833
243273
|
created_by=COALESCE(${this.s}.variables.created_by, $12),
|
|
242834
243274
|
created_at=COALESCE(${this.s}.variables.created_at, $13),
|
|
242835
|
-
updated_by=$14
|
|
243275
|
+
updated_by=$14,
|
|
243276
|
+
audience=COALESCE($15, ${this.s}.variables.audience),
|
|
243277
|
+
enabled_actor_ids=COALESCE($16, ${this.s}.variables.enabled_actor_ids)`,
|
|
242836
243278
|
[
|
|
242837
243279
|
v2.key,
|
|
242838
243280
|
v2.scope,
|
|
@@ -242847,7 +243289,9 @@ var init_postgres_registry = __esm({
|
|
|
242847
243289
|
v2.name ?? null,
|
|
242848
243290
|
v2.createdBy ?? null,
|
|
242849
243291
|
v2.createdAt ?? null,
|
|
242850
|
-
v2.updatedBy ?? null
|
|
243292
|
+
v2.updatedBy ?? null,
|
|
243293
|
+
v2.audience ?? null,
|
|
243294
|
+
v2.enabledActorIds ?? null
|
|
242851
243295
|
]
|
|
242852
243296
|
);
|
|
242853
243297
|
}
|
|
@@ -242866,6 +243310,34 @@ var init_postgres_registry = __esm({
|
|
|
242866
243310
|
[key, aid, valueEncrypted, updatedAt]
|
|
242867
243311
|
);
|
|
242868
243312
|
}
|
|
243313
|
+
async patchVariableMeta(key, actorId, patch) {
|
|
243314
|
+
const r = await this.pool.query(
|
|
243315
|
+
`UPDATE ${this.s}.variables SET
|
|
243316
|
+
audience = CASE WHEN $3::boolean THEN $4::text ELSE audience END,
|
|
243317
|
+
enabled_actor_ids = CASE WHEN $5::boolean THEN $6::text[] ELSE enabled_actor_ids END,
|
|
243318
|
+
delivery_mode = CASE WHEN $7::boolean THEN $8::text ELSE delivery_mode END,
|
|
243319
|
+
expires_at = CASE WHEN $9::boolean THEN $10::text ELSE expires_at END,
|
|
243320
|
+
updated_by = CASE WHEN $11::boolean THEN $12::text ELSE updated_by END,
|
|
243321
|
+
updated_at = $13
|
|
243322
|
+
WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'') AND COALESCE(project_id,'')=''`,
|
|
243323
|
+
[
|
|
243324
|
+
key,
|
|
243325
|
+
actorId ?? null,
|
|
243326
|
+
patch.audience !== void 0,
|
|
243327
|
+
patch.audience ?? null,
|
|
243328
|
+
patch.enabledActorIds !== void 0,
|
|
243329
|
+
patch.enabledActorIds ?? null,
|
|
243330
|
+
patch.deliveryMode !== void 0,
|
|
243331
|
+
patch.deliveryMode ?? null,
|
|
243332
|
+
patch.expiresAt !== void 0,
|
|
243333
|
+
patch.expiresAt ?? null,
|
|
243334
|
+
patch.updatedBy !== void 0,
|
|
243335
|
+
patch.updatedBy ?? null,
|
|
243336
|
+
patch.updatedAt
|
|
243337
|
+
]
|
|
243338
|
+
);
|
|
243339
|
+
return (r.rowCount ?? 0) > 0;
|
|
243340
|
+
}
|
|
242869
243341
|
async getVariableCiphertext(key, actorId) {
|
|
242870
243342
|
const aid = actorId ?? null;
|
|
242871
243343
|
const r = await this.pool.query(
|
|
@@ -242878,7 +243350,14 @@ var init_postgres_registry = __esm({
|
|
|
242878
243350
|
let r;
|
|
242879
243351
|
if (actorId !== void 0) {
|
|
242880
243352
|
r = await this.pool.query(
|
|
242881
|
-
|
|
243353
|
+
/* 决策 0556:`audience='selected'` 的全局行**只给 enabled_actor_ids 里那几位**。
|
|
243354
|
+
注入(resolveActorEnv / resolveActorProvisionEntries)、agent 侧 reveal、组织页 Agent 的密钥列表
|
|
243355
|
+
全都读这一条查询——语义只改在这一处,别在上层各自再滤一遍(滤漏一处就是泄漏)。
|
|
243356
|
+
audience 为 NULL(存量行)或 'all' 时行为与决策之前逐字一致。 */
|
|
243357
|
+
`SELECT * FROM ${this.s}.variables
|
|
243358
|
+
WHERE actor_id=$1
|
|
243359
|
+
OR (scope='global' AND (audience IS DISTINCT FROM 'selected' OR $1 = ANY(COALESCE(enabled_actor_ids, '{}'::text[]))))
|
|
243360
|
+
ORDER BY key`,
|
|
242882
243361
|
[actorId]
|
|
242883
243362
|
);
|
|
242884
243363
|
} else {
|
|
@@ -245355,6 +245834,7 @@ var init_postgres_trace = __esm({
|
|
|
245355
245834
|
AND left(job_key, char_length('produce::' || node_id || '::')) = 'produce::' || node_id || '::'`);
|
|
245356
245835
|
await pool.query(`CREATE INDEX IF NOT EXISTS agent_runs_effective_model_idx ON "${s2}".agent_runs (effective_model)`);
|
|
245357
245836
|
await pool.query(`CREATE INDEX IF NOT EXISTS agent_runs_actor_started_idx ON "${s2}".agent_runs (actor_id, started_at DESC)`);
|
|
245837
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS agent_runs_runtime_started_idx ON "${s2}".agent_runs (runtime_instance_id, started_at DESC)`);
|
|
245358
245838
|
await pool.query(`CREATE INDEX IF NOT EXISTS agent_runs_workorder_idx ON "${s2}".agent_runs (work_order_id, node_id, attempt_no DESC)`);
|
|
245359
245839
|
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS agent_runs_node_attempt_uq ON "${s2}".agent_runs (node_id, attempt_no) WHERE node_id IS NOT NULL AND attempt_no IS NOT NULL`);
|
|
245360
245840
|
await pool.query(`
|
|
@@ -246041,6 +246521,41 @@ var init_postgres_trace = __esm({
|
|
|
246041
246521
|
usage: row.usage
|
|
246042
246522
|
}));
|
|
246043
246523
|
}
|
|
246524
|
+
/* ---------- 运行时详情页:按实例直接读 run 行 ---------- */
|
|
246525
|
+
/**
|
|
246526
|
+
* 这个运行时实例上一共跑过几轮、最近一轮什么时候开跑。
|
|
246527
|
+
*
|
|
246528
|
+
* 读 `agent_runs` 而不是 `agent_usage_*` 聚合表:chat、工单节点(produce / integrate / review)、
|
|
246529
|
+
* 协调者每一轮**开跑时**就先落一行 run,所以开跑即计入;聚合表只在 run 结束且带 usage 时才 +1,
|
|
246530
|
+
* 失败 / 取消没 usage 的轮次永远不算。状态不过滤——每一轮都是一次运行。
|
|
246531
|
+
*/
|
|
246532
|
+
async runtimeRunActivity(runtimeInstanceId) {
|
|
246533
|
+
const r = await this.pool.query(
|
|
246534
|
+
`SELECT count(*)::int AS run_count, max(started_at) AS last_started_at
|
|
246535
|
+
FROM ${this.s}.agent_runs WHERE runtime_instance_id = $1`,
|
|
246536
|
+
[runtimeInstanceId]
|
|
246537
|
+
);
|
|
246538
|
+
const row = r.rows[0] ?? {};
|
|
246539
|
+
return { runCount: Number(row.run_count ?? 0), lastRunStartedAt: row.last_started_at ?? null };
|
|
246540
|
+
}
|
|
246541
|
+
/** 该实例在时间窗内开跑的全部 run(含还没有 usage 的在途 / 失败轮次),按开跑时刻正序。 */
|
|
246542
|
+
async listRuntimeRuns(runtimeInstanceId, range) {
|
|
246543
|
+
const r = await this.pool.query(
|
|
246544
|
+
`SELECT actor_id, runtime_kind, effective_model, started_at, usage
|
|
246545
|
+
FROM ${this.s}.agent_runs
|
|
246546
|
+
WHERE runtime_instance_id = $1 AND started_at >= $2 AND started_at <= $3
|
|
246547
|
+
ORDER BY started_at`,
|
|
246548
|
+
[runtimeInstanceId, range.from, range.to]
|
|
246549
|
+
);
|
|
246550
|
+
return r.rows.map((row) => ({
|
|
246551
|
+
actorId: row.actor_id,
|
|
246552
|
+
runtimeKind: row.runtime_kind ?? "shell",
|
|
246553
|
+
effectiveModel: row.effective_model ?? null,
|
|
246554
|
+
startedAt: row.started_at,
|
|
246555
|
+
// 历史行里有 JSONB 字面量 null,与 SQL NULL 一样当「还没有 usage」。
|
|
246556
|
+
usage: row.usage && typeof row.usage === "object" ? row.usage : null
|
|
246557
|
+
}));
|
|
246558
|
+
}
|
|
246044
246559
|
/* ---------- execution continuity ---------- */
|
|
246045
246560
|
async getAttempt(attemptId) {
|
|
246046
246561
|
return this.getRun(attemptId);
|
|
@@ -250669,6 +251184,13 @@ var init_postgres_nodes = __esm({
|
|
|
250669
251184
|
async setNodeOnline(id, online) {
|
|
250670
251185
|
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
251186
|
}
|
|
251187
|
+
/** 只推进 last_seen_at,不动 status(心跳落库;节流由调用方做,见 nodes/routes.ts)。 */
|
|
251188
|
+
async touchNodeSeen(id, atIso) {
|
|
251189
|
+
await this.pool.query(
|
|
251190
|
+
`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)`,
|
|
251191
|
+
[id, atIso]
|
|
251192
|
+
);
|
|
251193
|
+
}
|
|
250672
251194
|
async setNodeRuntimesOnline(nodeId, online) {
|
|
250673
251195
|
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
251196
|
}
|
|
@@ -254049,6 +254571,9 @@ var init_cutover_aware_registry = __esm({
|
|
|
254049
254571
|
async patchVariableValue(...args) {
|
|
254050
254572
|
return (await this.orgStoreForWrite()).patchVariableValue(...args);
|
|
254051
254573
|
}
|
|
254574
|
+
async patchVariableMeta(...args) {
|
|
254575
|
+
return (await this.orgStoreForWrite()).patchVariableMeta(...args);
|
|
254576
|
+
}
|
|
254052
254577
|
async getVariableCiphertext(...args) {
|
|
254053
254578
|
return (await this.orgStore()).getVariableCiphertext(...args);
|
|
254054
254579
|
}
|
|
@@ -254430,6 +254955,15 @@ var init_cutover_aware_trace = __esm({
|
|
|
254430
254955
|
async listUsageRuns(filter) {
|
|
254431
254956
|
return (await this.pick()).listUsageRuns(filter);
|
|
254432
254957
|
}
|
|
254958
|
+
// ── 运行时详情页的按实例读(不在 TraceStore 契约上:落点 store 没实现就当没有数据源)──────────
|
|
254959
|
+
async runtimeRunActivity(runtimeInstanceId) {
|
|
254960
|
+
const s2 = await this.pick();
|
|
254961
|
+
return typeof s2.runtimeRunActivity === "function" ? s2.runtimeRunActivity(runtimeInstanceId) : null;
|
|
254962
|
+
}
|
|
254963
|
+
async listRuntimeRuns(runtimeInstanceId, range) {
|
|
254964
|
+
const s2 = await this.pick();
|
|
254965
|
+
return typeof s2.listRuntimeRuns === "function" ? s2.listRuntimeRuns(runtimeInstanceId, range) : null;
|
|
254966
|
+
}
|
|
254433
254967
|
// ── 事件 / 工具调用 / 链接 ────────────────────────────────────────────────
|
|
254434
254968
|
async appendEvent(runId, event) {
|
|
254435
254969
|
return (await this.pickWritable()).appendEvent(runId, event);
|
|
@@ -258519,7 +259053,7 @@ var init_run_settlement_wiring = __esm({
|
|
|
258519
259053
|
});
|
|
258520
259054
|
|
|
258521
259055
|
// ../server/src/deadline-clock.ts
|
|
258522
|
-
async function deadlineClockTick(deps,
|
|
259056
|
+
async function deadlineClockTick(deps, dedupe2) {
|
|
258523
259057
|
const limit = deps.batchLimit ?? DEADLINE_CLOCK_BATCH_LIMIT;
|
|
258524
259058
|
const lookahead = deps.lookaheadMs ?? DEADLINE_CLOCK_LOOKAHEAD_MS;
|
|
258525
259059
|
const policy = deps.policy();
|
|
@@ -258548,14 +259082,14 @@ async function deadlineClockTick(deps, dedupe) {
|
|
|
258548
259082
|
if (overdue > result.oldestDueDelayMs) result.oldestDueDelayMs = overdue;
|
|
258549
259083
|
const phase = overdue >= 0 ? "due" : "early";
|
|
258550
259084
|
const bucket = `${c.workId}@${deadline}@${phase}`;
|
|
258551
|
-
if (
|
|
259085
|
+
if (dedupe2.has(c.workorderId, bucket)) {
|
|
258552
259086
|
result.deduped++;
|
|
258553
259087
|
continue;
|
|
258554
259088
|
}
|
|
258555
259089
|
const buckets = toPing.get(c.workorderId) ?? [];
|
|
258556
259090
|
buckets.push(bucket);
|
|
258557
259091
|
toPing.set(c.workorderId, buckets);
|
|
258558
|
-
|
|
259092
|
+
dedupe2.mark(c.workorderId, bucket);
|
|
258559
259093
|
}
|
|
258560
259094
|
if (page.nextCursor) result.backlog = toPing.size + result.deduped;
|
|
258561
259095
|
for (const [workorderId, buckets] of toPing) {
|
|
@@ -258564,7 +259098,7 @@ async function deadlineClockTick(deps, dedupe) {
|
|
|
258564
259098
|
result.pinged++;
|
|
258565
259099
|
} catch (err) {
|
|
258566
259100
|
result.failed++;
|
|
258567
|
-
for (const b2 of buckets)
|
|
259101
|
+
for (const b2 of buckets) dedupe2.unmark(workorderId, b2);
|
|
258568
259102
|
deps.log?.(`[deadline-clock] ping ${workorderId} \u5931\u8D25: ${String(err)}`);
|
|
258569
259103
|
}
|
|
258570
259104
|
}
|
|
@@ -258728,6 +259262,7 @@ var init_src11 = __esm({
|
|
|
258728
259262
|
init_auth();
|
|
258729
259263
|
init_sender();
|
|
258730
259264
|
init_routes7();
|
|
259265
|
+
init_runtime_activity();
|
|
258731
259266
|
init_runtime_drift();
|
|
258732
259267
|
init_runtime_tenancy();
|
|
258733
259268
|
init_types4();
|
|
@@ -261543,6 +262078,11 @@ async function startServe(opts) {
|
|
|
261543
262078
|
}) : dispatchLedger;
|
|
261544
262079
|
const companyRegistryFor = async (companyId) => pgPool ? (await openCompanyPlane(companyId)).overlay : scopedRegistryFor(companyId);
|
|
261545
262080
|
const defaultRegistry = await companyRegistryFor(defaultCompanyId);
|
|
262081
|
+
const companyLiveTypeDefs = async (companyId) => {
|
|
262082
|
+
if (!pgPool || !cutoverStore) return null;
|
|
262083
|
+
if (!cutoverStore.readsCompanySchema(await cutoverStore.get(companyId, "types"))) return null;
|
|
262084
|
+
return buildLiveSchema(await (await openCompanyPlane(companyId)).types.list());
|
|
262085
|
+
};
|
|
261546
262086
|
const engineRouter = new CompanyEngineRouter({
|
|
261547
262087
|
// ★ 字节柜按公司取:`gc` 用的就是 `engine.blobs`,共享柜 + 单公司可达集 = 删光别家。
|
|
261548
262088
|
defaultEngine: { kernel: defaultCompanyKernel, oplog, blobs: await blobStoreFor(defaultCompanyId), registry: defaultRegistry, assets },
|
|
@@ -261558,6 +262098,7 @@ async function startServe(opts) {
|
|
|
261558
262098
|
// ★ 同默认公司:漏了这里,非默认公司的引擎照旧按代码默认 30min 判超时(策略文件形同虚设)。
|
|
261559
262099
|
policy: () => activeTimeoutPolicy
|
|
261560
262100
|
});
|
|
262101
|
+
for (const d of await companyLiveTypeDefs(companyId) ?? []) engine2.kernel.setType(d);
|
|
261561
262102
|
const registry2 = await companyRegistryFor(companyId);
|
|
261562
262103
|
await ensureCompanyActors(controlPlaneStore, companyId, registry2);
|
|
261563
262104
|
await ensureDefaultExpertTeam(registry2);
|
|
@@ -262094,7 +262635,7 @@ async function startServe(opts) {
|
|
|
262094
262635
|
() => [engine2.kernel],
|
|
262095
262636
|
async () => {
|
|
262096
262637
|
if (primary) {
|
|
262097
|
-
for (const d of await
|
|
262638
|
+
for (const d of await companyLiveTypeDefs(cid) ?? []) engine2.kernel.setType(d);
|
|
262098
262639
|
return;
|
|
262099
262640
|
}
|
|
262100
262641
|
await rebuildLiveSchema();
|
|
@@ -262119,7 +262660,19 @@ async function startServe(opts) {
|
|
|
262119
262660
|
activeRunsOf,
|
|
262120
262661
|
nodeReportedModels,
|
|
262121
262662
|
refreshNodeModels,
|
|
262122
|
-
fetchLatestNpmDaemonVersion
|
|
262663
|
+
fetchLatestNpmDaemonVersion,
|
|
262664
|
+
// 「删除运行时 / 改团队默认模型仅组织管理者」的判据(Runtime PRD「权限设计」)。
|
|
262665
|
+
// 与 projectsDomain 那条 resolveMemberRole 同一实现:闭包在请求时才读成员表。
|
|
262666
|
+
resolveMemberRole: async (companyId, actorId) => {
|
|
262667
|
+
const members = await listCompanyMembers(companyId);
|
|
262668
|
+
return members.find((m2) => m2.actorId === actorId)?.role;
|
|
262669
|
+
},
|
|
262670
|
+
// 详情页「运行次数 / 最近工作时间」与用量 tab 按实例读 run 行:落到这家公司 trace 当前的读落点
|
|
262671
|
+
//(切走了读公司 schema,没切走读 legacy)。dev 文件态的 FileTraceStore 同样实现了这两个读。
|
|
262672
|
+
runtimeRuns: async (companyId) => {
|
|
262673
|
+
const store = await traceStoreFor(companyId);
|
|
262674
|
+
return isRuntimeRunReader(store) ? store : null;
|
|
262675
|
+
}
|
|
262123
262676
|
}),
|
|
262124
262677
|
collabDomain({
|
|
262125
262678
|
kernel: defaultCompanyKernel,
|
|
@@ -264734,7 +265287,7 @@ async function startServe(opts) {
|
|
|
264734
265287
|
} else if (!deadlineStoreOf(newEngine.kernel)?.listOpenWorkDeadlines) {
|
|
264735
265288
|
console.warn("[deadline-clock] engine store \u672A\u5B9E\u73B0 listOpenWorkDeadlines\uFF0C\u65F6\u949F\u672A\u542F\u52A8\u2014\u2014deadline \u65E0\u6700\u8FDF\u89E6\u53D1\u4FDD\u8BC1");
|
|
264736
265289
|
} else {
|
|
264737
|
-
const
|
|
265290
|
+
const dedupe2 = new DeadlineDedupe();
|
|
264738
265291
|
const clockCursors = /* @__PURE__ */ new Map();
|
|
264739
265292
|
let clockTicking = false;
|
|
264740
265293
|
void (async () => {
|
|
@@ -264784,7 +265337,7 @@ async function startServe(opts) {
|
|
|
264784
265337
|
batchLimit: DEADLINE_CLOCK_BATCH_LIMIT,
|
|
264785
265338
|
...cursor ? { cursor } : {},
|
|
264786
265339
|
log: (m2) => console.warn(m2)
|
|
264787
|
-
},
|
|
265340
|
+
}, dedupe2);
|
|
264788
265341
|
clockCursors.set(companyId, r.nextCursor);
|
|
264789
265342
|
if (r.pinged > 0) {
|
|
264790
265343
|
console.log(`[deadline-clock] [${companyId}] \u6295\u51FA ${r.pinged} \u4E2A workorder.ping\uFF08\u53BB\u91CD ${r.deduped}\u3001\u8C41\u514D ${r.exempt}\uFF09`);
|
|
@@ -266455,6 +267008,8 @@ init_src6();
|
|
|
266455
267008
|
init_src();
|
|
266456
267009
|
var WORKDIR_READ_MAX_BYTES2 = 2 * 1024 * 1024;
|
|
266457
267010
|
var WORKDIR_LIST_MAX_ENTRIES = 500;
|
|
267011
|
+
var WORKDIR_WALK_MAX_ENTRIES2 = 1e4;
|
|
267012
|
+
var WORKDIR_WALK_BUDGET_MS2 = 1e4;
|
|
266458
267013
|
var CREDENTIAL_WORDS = ["token", "secret", "credential", "password", "passwd", "apikey"];
|
|
266459
267014
|
var SSH_KEY_PREFIX = /^id_(rsa|dsa|ecdsa|ed25519)/;
|
|
266460
267015
|
function isSensitiveSegment(segment) {
|
|
@@ -266578,6 +267133,41 @@ async function handleWorkdirList(req) {
|
|
|
266578
267133
|
return { ok: false, code: "INTERNAL", message: String(e?.message ?? e) };
|
|
266579
267134
|
}
|
|
266580
267135
|
}
|
|
267136
|
+
async function handleWorkdirWalk(req, now = Date.now) {
|
|
267137
|
+
const cap = Math.max(1, Math.min(req.maxEntries ?? WORKDIR_WALK_MAX_ENTRIES2, WORKDIR_WALK_MAX_ENTRIES2));
|
|
267138
|
+
const deadline = now() + WORKDIR_WALK_BUDGET_MS2;
|
|
267139
|
+
const start = normalizeRel(req.path).replace(/[/\\]+$/, "");
|
|
267140
|
+
const root = await handleWorkdirList({ ...req, path: start });
|
|
267141
|
+
if (!root.ok) return root;
|
|
267142
|
+
const entries = [];
|
|
267143
|
+
let truncated = root.truncated === true;
|
|
267144
|
+
let level = [{ dir: start, listed: root }];
|
|
267145
|
+
while (level.length > 0) {
|
|
267146
|
+
const pending = [];
|
|
267147
|
+
for (const { dir, listed } of level) {
|
|
267148
|
+
if (!listed.ok) {
|
|
267149
|
+
if (listed.code !== "NOT_FOUND") truncated = true;
|
|
267150
|
+
continue;
|
|
267151
|
+
}
|
|
267152
|
+
if (listed.truncated) truncated = true;
|
|
267153
|
+
for (const entry of listed.entries) {
|
|
267154
|
+
if (entries.length >= cap) return { ok: true, entries, truncated: true, absPath: root.absPath };
|
|
267155
|
+
const rel = dir ? `${dir}/${entry.name}` : entry.name;
|
|
267156
|
+
entries.push({ path: rel, type: entry.type, size: entry.size, mtime: entry.mtime });
|
|
267157
|
+
if (entry.type === "dir") pending.push(rel);
|
|
267158
|
+
}
|
|
267159
|
+
}
|
|
267160
|
+
level = [];
|
|
267161
|
+
for (const dir of pending) {
|
|
267162
|
+
if (entries.length >= cap || now() >= deadline) {
|
|
267163
|
+
truncated = true;
|
|
267164
|
+
break;
|
|
267165
|
+
}
|
|
267166
|
+
level.push({ dir, listed: await handleWorkdirList({ ...req, path: dir }) });
|
|
267167
|
+
}
|
|
267168
|
+
}
|
|
267169
|
+
return { ok: true, entries, absPath: root.absPath, ...truncated ? { truncated: true } : {} };
|
|
267170
|
+
}
|
|
266581
267171
|
async function fdCanonicalPath(fh) {
|
|
266582
267172
|
try {
|
|
266583
267173
|
return await import_node_fs19.default.promises.readlink(`/proc/self/fd/${fh.fd}`);
|
|
@@ -267068,6 +267658,18 @@ var DaemonWsClient = class {
|
|
|
267068
267658
|
this.send({ type: "workdir_read_result", requestId: msg.requestId, result });
|
|
267069
267659
|
break;
|
|
267070
267660
|
}
|
|
267661
|
+
case "workdir_walk": {
|
|
267662
|
+
const result = await handleWorkdirWalk({
|
|
267663
|
+
workRoot: this.workRoot,
|
|
267664
|
+
runtimeKind: msg.runtimeKind,
|
|
267665
|
+
workdirKey: msg.workdirKey,
|
|
267666
|
+
rtId: msg.rtId,
|
|
267667
|
+
path: msg.path,
|
|
267668
|
+
maxEntries: msg.maxEntries
|
|
267669
|
+
});
|
|
267670
|
+
this.send({ type: "workdir_walk_result", requestId: msg.requestId, result });
|
|
267671
|
+
break;
|
|
267672
|
+
}
|
|
267071
267673
|
case "list_models": {
|
|
267072
267674
|
void this.reportRuntimeModels({ force: true, requestId: msg.requestId });
|
|
267073
267675
|
break;
|
|
@@ -267247,6 +267849,8 @@ var DaemonWsClient = class {
|
|
|
267247
267849
|
// 服务端据此决定是用节点归一还是自己就地归一;判据取能力位而不是「见过帧没有」,
|
|
267248
267850
|
// 否则首个 chunk 之前那段窗口两边都不归一,正文会静默丢一截。
|
|
267249
267851
|
supportsNormalizedEvents: true,
|
|
267852
|
+
// ADR 0554:本 daemon 认 `workdir_walk`。恒 true——handler 随包发,装了这版就一定有。
|
|
267853
|
+
supportsWorkdirWalk: true,
|
|
267250
267854
|
...activeSessions.length > 0 ? { activeSessions } : {},
|
|
267251
267855
|
// 始终带字段(空数组也带)作为新协议能力标志;服务端仅对支持该字段的节点做缺席确认。
|
|
267252
267856
|
pendingDispatchIds: [...this.pendingDispatchIds]
|
|
@@ -273151,7 +273755,7 @@ function shimScript() {
|
|
|
273151
273755
|
}
|
|
273152
273756
|
|
|
273153
273757
|
// src/index.ts
|
|
273154
|
-
var PKG_VERSION = true ? "2.2.
|
|
273758
|
+
var PKG_VERSION = true ? "2.2.28" : "dev";
|
|
273155
273759
|
var LOCAL_BIN = localBin();
|
|
273156
273760
|
var NPM_PREFIX = npmPrefix();
|
|
273157
273761
|
var INSTANCE = DEFAULT_INSTANCE;
|