@zqagent/core 0.2.16 → 0.2.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.oss.en.md +13 -0
- package/README.oss.md +11 -0
- package/README.oss.zh-TW.md +11 -0
- package/dist/cli/index.cjs +764 -53
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +764 -53
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +16 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/server/index.cjs +733 -47
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +739 -53
- package/dist/server/index.js.map +1 -1
- package/dist/tools/explore.cjs.map +1 -1
- package/dist/tools/explore.js.map +1 -1
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -583,6 +583,15 @@ var path4 = __toESM(require("path"), 1);
|
|
|
583
583
|
var readline = __toESM(require("readline"), 1);
|
|
584
584
|
var import_crypto = require("crypto");
|
|
585
585
|
|
|
586
|
+
// src/server/llmIds.ts
|
|
587
|
+
init_cjs_shims();
|
|
588
|
+
var GLOBAL_PROVIDER_ID = "__env__";
|
|
589
|
+
var BUILTIN_PROVIDER_ID = "__zqai__";
|
|
590
|
+
function isBuiltinProvider(id) {
|
|
591
|
+
return id === BUILTIN_PROVIDER_ID;
|
|
592
|
+
}
|
|
593
|
+
var FALLBACK_MODEL = "gpt-4o";
|
|
594
|
+
|
|
586
595
|
// src/server/dbgTrace.ts
|
|
587
596
|
init_cjs_shims();
|
|
588
597
|
var DBG_FLEET = (() => {
|
|
@@ -1720,7 +1729,23 @@ var Store = class {
|
|
|
1720
1729
|
legacyFiles: [path4.join(this.root, "node-llm.json")]
|
|
1721
1730
|
});
|
|
1722
1731
|
const old = new Map(cur.providers.map((p) => [p.id, p]));
|
|
1723
|
-
const
|
|
1732
|
+
const src = [...cfg.providers];
|
|
1733
|
+
const prevBuiltin = cur.providers.find((p) => p.id === BUILTIN_PROVIDER_ID);
|
|
1734
|
+
if (prevBuiltin && !src.some((p) => p.id === BUILTIN_PROVIDER_ID)) {
|
|
1735
|
+
src.unshift({
|
|
1736
|
+
id: prevBuiltin.id,
|
|
1737
|
+
name: prevBuiltin.name,
|
|
1738
|
+
type: prevBuiltin.type,
|
|
1739
|
+
baseURL: prevBuiltin.baseURL,
|
|
1740
|
+
model: prevBuiltin.model,
|
|
1741
|
+
apiKey: decryptKey(prevBuiltin.apiKeyEnc, file) ?? void 0,
|
|
1742
|
+
reasoningEffort: prevBuiltin.reasoningEffort ?? null,
|
|
1743
|
+
...prevBuiltin.models ? { models: prevBuiltin.models } : {},
|
|
1744
|
+
...prevBuiltin.nonChat ? { nonChat: prevBuiltin.nonChat } : {},
|
|
1745
|
+
...prevBuiltin.modelMeta ? { modelMeta: prevBuiltin.modelMeta } : {}
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
const providers = src.map((p) => {
|
|
1724
1749
|
const prev = old.get(p.id);
|
|
1725
1750
|
const apiKeyEnc = p.apiKey ? encryptKey(p.apiKey, file) : prev?.apiKeyEnc ?? "";
|
|
1726
1751
|
return {
|
|
@@ -9248,7 +9273,6 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
9248
9273
|
|
|
9249
9274
|
// src/server/nodeLlm.ts
|
|
9250
9275
|
init_cjs_shims();
|
|
9251
|
-
var GLOBAL_PROVIDER_ID = "__env__";
|
|
9252
9276
|
function envProvider() {
|
|
9253
9277
|
const apiKey = process.env.OPENAI_API_KEY;
|
|
9254
9278
|
if (!apiKey) return null;
|
|
@@ -9291,19 +9315,26 @@ function nodeLlmView(cfg) {
|
|
|
9291
9315
|
global: true
|
|
9292
9316
|
}
|
|
9293
9317
|
] : [],
|
|
9294
|
-
...cfg.providers.map((p) =>
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9318
|
+
...cfg.providers.map((p) => {
|
|
9319
|
+
const builtin = isBuiltinProvider(p.id);
|
|
9320
|
+
return {
|
|
9321
|
+
id: p.id,
|
|
9322
|
+
name: p.name,
|
|
9323
|
+
type: p.type ?? "",
|
|
9324
|
+
baseURL: p.baseURL,
|
|
9325
|
+
model: p.model,
|
|
9326
|
+
hasApiKey: Boolean(p.apiKey),
|
|
9327
|
+
models: p.models ?? [],
|
|
9328
|
+
nonChat: p.nonChat ?? [],
|
|
9329
|
+
modelMeta: p.modelMeta ?? [],
|
|
9330
|
+
reasoningEffort: p.reasoningEffort ?? null,
|
|
9331
|
+
global: false,
|
|
9332
|
+
/** 平台内置(接入 Hub 后自动下发,用户不用关心 Key) */
|
|
9333
|
+
builtin,
|
|
9334
|
+
/** 不可编辑的字段:控制台据此把 baseURL / key 显示为只读 */
|
|
9335
|
+
...builtin ? { locked: ["baseURL", "apiKey"] } : {}
|
|
9336
|
+
};
|
|
9337
|
+
})
|
|
9307
9338
|
];
|
|
9308
9339
|
return {
|
|
9309
9340
|
defaultProviderId: cfg.defaultProviderId ?? null,
|
|
@@ -9316,27 +9347,42 @@ function applyNodeLlmPatch(cfg, raw) {
|
|
|
9316
9347
|
const next = { providers: cfg.providers, defaultProviderId: cfg.defaultProviderId };
|
|
9317
9348
|
if (Array.isArray(raw?.providers)) {
|
|
9318
9349
|
const old = new Map(cfg.providers.map((p) => [p.id, p]));
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9350
|
+
const incoming = raw.providers.filter((x) => Boolean(x) && typeof x === "object").filter((x) => x.global !== true && String(x.id ?? "") !== GLOBAL_PROVIDER_ID);
|
|
9351
|
+
const prevBuiltin = cfg.providers.find((p) => isBuiltinProvider(p.id));
|
|
9352
|
+
const builtinPatch = incoming.find((x) => isBuiltinProvider(String(x.id ?? "")));
|
|
9353
|
+
const keptBuiltin = prevBuiltin ? [
|
|
9354
|
+
{
|
|
9355
|
+
...prevBuiltin,
|
|
9356
|
+
...builtinPatch?.model != null ? { model: String(builtinPatch.model).trim() || prevBuiltin.model } : {},
|
|
9357
|
+
...normalizeModelList(builtinPatch?.models) ? { models: normalizeModelList(builtinPatch?.models) } : {},
|
|
9358
|
+
...builtinPatch?.reasoningEffort !== void 0 ? { reasoningEffort: normReasoningEffort(builtinPatch.reasoningEffort) } : {},
|
|
9359
|
+
...typeof builtinPatch?.name === "string" && builtinPatch.name.trim() ? { name: builtinPatch.name.trim() } : {}
|
|
9360
|
+
}
|
|
9361
|
+
] : [];
|
|
9362
|
+
next.providers = [
|
|
9363
|
+
...keptBuiltin,
|
|
9364
|
+
...incoming.filter((x) => !isBuiltinProvider(String(x.id ?? ""))).map((x) => {
|
|
9365
|
+
const id = String(x.id ?? "").trim();
|
|
9366
|
+
if (!id) throw new ApiError(400, "bad_request", "\u4F9B\u5E94\u5546 id \u4E0D\u80FD\u4E3A\u7A7A");
|
|
9367
|
+
const prev = old.get(id);
|
|
9368
|
+
const apiKey = String(x.apiKey ?? "").trim();
|
|
9369
|
+
const models = normalizeModelList(x.models) ?? prev?.models;
|
|
9370
|
+
const nonChat = normalizeModelList(x.nonChat) ?? prev?.nonChat;
|
|
9371
|
+
const modelMeta = normalizeModelMeta(x.modelMeta) ?? prev?.modelMeta;
|
|
9372
|
+
return {
|
|
9373
|
+
id,
|
|
9374
|
+
name: String(x.name ?? prev?.name ?? id).trim(),
|
|
9375
|
+
type: x.type == null ? prev?.type : String(x.type),
|
|
9376
|
+
baseURL: String(x.baseURL ?? prev?.baseURL ?? "").trim(),
|
|
9377
|
+
model: String(x.model ?? prev?.model ?? "").trim(),
|
|
9378
|
+
apiKey: apiKey || prev?.apiKey,
|
|
9379
|
+
reasoningEffort: x.reasoningEffort === void 0 ? prev?.reasoningEffort ?? null : normReasoningEffort(x.reasoningEffort),
|
|
9380
|
+
...models ? { models } : {},
|
|
9381
|
+
...nonChat ? { nonChat } : {},
|
|
9382
|
+
...modelMeta ? { modelMeta } : {}
|
|
9383
|
+
};
|
|
9384
|
+
})
|
|
9385
|
+
];
|
|
9340
9386
|
}
|
|
9341
9387
|
if (typeof raw?.defaultProviderId === "string") {
|
|
9342
9388
|
const id = raw.defaultProviderId.trim();
|
|
@@ -9356,6 +9402,9 @@ function nodeDefaultProvider(store) {
|
|
|
9356
9402
|
if (cfg.providers.length === 0) return null;
|
|
9357
9403
|
return cfg.providers.find((p) => p.id === cfg.defaultProviderId) ?? cfg.providers[0] ?? null;
|
|
9358
9404
|
}
|
|
9405
|
+
function defaultTenantModel(store) {
|
|
9406
|
+
return process.env.OPENAI_MODEL || process.env.MODEL || nodeDefaultProvider(store)?.model || FALLBACK_MODEL;
|
|
9407
|
+
}
|
|
9359
9408
|
function resolveSessionProvider(store, tenant, providerId) {
|
|
9360
9409
|
const own = tenant.provider ?? {};
|
|
9361
9410
|
if (providerId) {
|
|
@@ -9385,6 +9434,208 @@ function resolveSessionProvider(store, tenant, providerId) {
|
|
|
9385
9434
|
return { ...own, model: tenant.model };
|
|
9386
9435
|
}
|
|
9387
9436
|
|
|
9437
|
+
// src/server/builtinLlm.ts
|
|
9438
|
+
init_cjs_shims();
|
|
9439
|
+
var FETCH_TIMEOUT_MS = 8e3;
|
|
9440
|
+
var RETRY_AFTER_MS = 5 * 6e4;
|
|
9441
|
+
function hubBase(cred) {
|
|
9442
|
+
return (cred.hubUrl ?? "").replace(/\/+$/, "");
|
|
9443
|
+
}
|
|
9444
|
+
function asProvider(grant) {
|
|
9445
|
+
const baseURL = String(grant.baseURL ?? "").trim();
|
|
9446
|
+
const apiKey = String(grant.apiKey ?? "").trim();
|
|
9447
|
+
const model = String(grant.model ?? "").trim();
|
|
9448
|
+
if (!baseURL || !apiKey) return null;
|
|
9449
|
+
return {
|
|
9450
|
+
id: BUILTIN_PROVIDER_ID,
|
|
9451
|
+
name: String(grant.name ?? "").trim() || "\u5FD7\u524D\u5927\u6A21\u578B\uFF08\u5185\u7F6E\uFF09",
|
|
9452
|
+
type: "builtin",
|
|
9453
|
+
baseURL,
|
|
9454
|
+
model,
|
|
9455
|
+
apiKey
|
|
9456
|
+
};
|
|
9457
|
+
}
|
|
9458
|
+
async function enrichModels(p) {
|
|
9459
|
+
try {
|
|
9460
|
+
const r = await Promise.race([
|
|
9461
|
+
fetchModels(p.baseURL, p.apiKey ?? "", p.model),
|
|
9462
|
+
new Promise((resolve20) => {
|
|
9463
|
+
const t = setTimeout(() => resolve20(null), FETCH_TIMEOUT_MS);
|
|
9464
|
+
t.unref?.();
|
|
9465
|
+
})
|
|
9466
|
+
]);
|
|
9467
|
+
if (!r?.models?.length) return p;
|
|
9468
|
+
return {
|
|
9469
|
+
...p,
|
|
9470
|
+
// 控制面没给默认型号时,取供应商返回的第一个,避免模型为空让用户卡住
|
|
9471
|
+
model: p.model || r.models[0],
|
|
9472
|
+
models: r.models,
|
|
9473
|
+
...r.nonChat?.length ? { nonChat: r.nonChat } : {},
|
|
9474
|
+
...r.modelMeta?.length ? { modelMeta: r.modelMeta } : {}
|
|
9475
|
+
};
|
|
9476
|
+
} catch {
|
|
9477
|
+
return p;
|
|
9478
|
+
}
|
|
9479
|
+
}
|
|
9480
|
+
function saveBuiltin(store, provider) {
|
|
9481
|
+
const cfg = store.loadNodeLlm();
|
|
9482
|
+
const others = cfg.providers.filter((p) => p.id !== BUILTIN_PROVIDER_ID);
|
|
9483
|
+
const prev = cfg.providers.find((p) => p.id === BUILTIN_PROVIDER_ID);
|
|
9484
|
+
const merged = {
|
|
9485
|
+
...provider,
|
|
9486
|
+
// 用户选过的型号/候选列表/推理强度/显示名都保留;只有托管字段(baseURL/key)强制以控制面为准
|
|
9487
|
+
...prev?.model ? { model: prev.model } : {},
|
|
9488
|
+
...prev?.models?.length ? { models: prev.models } : {},
|
|
9489
|
+
...prev?.reasoningEffort != null ? { reasoningEffort: prev.reasoningEffort } : {},
|
|
9490
|
+
...prev?.name ? { name: prev.name } : {}
|
|
9491
|
+
};
|
|
9492
|
+
store.saveNodeLlm({
|
|
9493
|
+
providers: [merged, ...others],
|
|
9494
|
+
// 之前没有默认且用户也没指定时,让内置模型成为默认——「开箱即用」
|
|
9495
|
+
defaultProviderId: cfg.defaultProviderId ?? BUILTIN_PROVIDER_ID
|
|
9496
|
+
});
|
|
9497
|
+
adoptBuiltinModelForTenants(store, merged.model);
|
|
9498
|
+
}
|
|
9499
|
+
var LEGACY_DEFAULT_MODELS = /* @__PURE__ */ new Set([FALLBACK_MODEL, "deepseek-v4-flash"]);
|
|
9500
|
+
function adoptBuiltinModelForTenants(store, model) {
|
|
9501
|
+
if (!model) return;
|
|
9502
|
+
for (const id of store.listTenants()) {
|
|
9503
|
+
const tenant = store.loadTenant(id);
|
|
9504
|
+
if (!tenant || !LEGACY_DEFAULT_MODELS.has(tenant.model)) continue;
|
|
9505
|
+
if (tenant.provider?.apiKey) continue;
|
|
9506
|
+
tenant.model = model;
|
|
9507
|
+
store.saveTenant(tenant);
|
|
9508
|
+
}
|
|
9509
|
+
}
|
|
9510
|
+
async function ensureBuiltinProvider(store, opts = {}) {
|
|
9511
|
+
const cred = store.loadFleetCredential();
|
|
9512
|
+
if (!cred?.fleetToken || !cred.fleetSecret) {
|
|
9513
|
+
return { status: "disabled", reason: "\u8282\u70B9\u672A\u63A5\u5165 Hub" };
|
|
9514
|
+
}
|
|
9515
|
+
const hub = hubBase(cred);
|
|
9516
|
+
if (!hub) return { status: "disabled", reason: "\u7F3A\u5C11 Hub \u5730\u5740" };
|
|
9517
|
+
const local = store.loadNodeLlm().providers.find((p) => p.id === BUILTIN_PROVIDER_ID);
|
|
9518
|
+
if (local && !opts.force) {
|
|
9519
|
+
adoptBuiltinModelForTenants(store, local.model);
|
|
9520
|
+
return { status: "ready", provider: local, refreshed: false };
|
|
9521
|
+
}
|
|
9522
|
+
const cached3 = cred.builtinLlm ? asProvider(cred.builtinLlm) : null;
|
|
9523
|
+
let res;
|
|
9524
|
+
try {
|
|
9525
|
+
res = await fetch(`${hub}/api/hub/node/builtin-llm`, {
|
|
9526
|
+
headers: {
|
|
9527
|
+
Authorization: `Bearer ${cred.fleetToken}`,
|
|
9528
|
+
"X-Fleet-Secret": cred.fleetSecret,
|
|
9529
|
+
"X-Node-Id": cred.nodeId
|
|
9530
|
+
},
|
|
9531
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
9532
|
+
});
|
|
9533
|
+
} catch (e) {
|
|
9534
|
+
if (cached3) {
|
|
9535
|
+
const provider2 = await enrichModels(cached3);
|
|
9536
|
+
saveBuiltin(store, provider2);
|
|
9537
|
+
return { status: "ready", provider: provider2, refreshed: true };
|
|
9538
|
+
}
|
|
9539
|
+
return { status: "error", reason: `\u8FDE\u63A5 Hub \u5931\u8D25\uFF1A${e.message}` };
|
|
9540
|
+
}
|
|
9541
|
+
let body = null;
|
|
9542
|
+
try {
|
|
9543
|
+
body = await res.json();
|
|
9544
|
+
} catch {
|
|
9545
|
+
}
|
|
9546
|
+
if (!res.ok) {
|
|
9547
|
+
const msg = body?.error?.message ?? `HTTP ${res.status}`;
|
|
9548
|
+
if (res.status === 401 || res.status === 403) {
|
|
9549
|
+
return { status: "unavailable", reason: msg };
|
|
9550
|
+
}
|
|
9551
|
+
if (cached3) {
|
|
9552
|
+
const provider2 = await enrichModels(cached3);
|
|
9553
|
+
saveBuiltin(store, provider2);
|
|
9554
|
+
return { status: "ready", provider: provider2, refreshed: true };
|
|
9555
|
+
}
|
|
9556
|
+
return { status: "error", reason: msg };
|
|
9557
|
+
}
|
|
9558
|
+
const grant = body?.data?.builtinLlm;
|
|
9559
|
+
if (!grant) {
|
|
9560
|
+
const why = String(body?.data?.reason ?? "").trim();
|
|
9561
|
+
return { status: "unavailable", reason: why || "\u63A7\u5236\u9762\u672A\u4E0B\u53D1\u5185\u7F6E\u6A21\u578B" };
|
|
9562
|
+
}
|
|
9563
|
+
const parsed = asProvider(grant);
|
|
9564
|
+
if (!parsed) return { status: "unavailable", reason: "\u5185\u7F6E\u6A21\u578B\u914D\u7F6E\u4E0D\u5B8C\u6574" };
|
|
9565
|
+
const provider = await enrichModels(parsed);
|
|
9566
|
+
saveBuiltin(store, provider);
|
|
9567
|
+
return { status: "ready", provider, refreshed: true };
|
|
9568
|
+
}
|
|
9569
|
+
async function fetchLlmBalance(store) {
|
|
9570
|
+
const cred = store.loadFleetCredential();
|
|
9571
|
+
const hub = cred ? hubBase(cred) : "";
|
|
9572
|
+
if (!cred?.fleetToken || !cred.fleetSecret || !hub) {
|
|
9573
|
+
return { available: null, currency: "CNY", reason: "\u8282\u70B9\u672A\u63A5\u5165 Hub" };
|
|
9574
|
+
}
|
|
9575
|
+
try {
|
|
9576
|
+
const res = await fetch(`${hub}/api/hub/node/balance`, {
|
|
9577
|
+
headers: {
|
|
9578
|
+
Authorization: `Bearer ${cred.fleetToken}`,
|
|
9579
|
+
"X-Fleet-Secret": cred.fleetSecret,
|
|
9580
|
+
"X-Node-Id": cred.nodeId
|
|
9581
|
+
},
|
|
9582
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
9583
|
+
});
|
|
9584
|
+
const body = await res.json().catch(() => null);
|
|
9585
|
+
if (!res.ok) {
|
|
9586
|
+
return {
|
|
9587
|
+
available: null,
|
|
9588
|
+
currency: "CNY",
|
|
9589
|
+
reason: body?.data ? "" : `HTTP ${res.status}`
|
|
9590
|
+
};
|
|
9591
|
+
}
|
|
9592
|
+
const d = body?.data ?? {};
|
|
9593
|
+
const available = typeof d.available === "number" ? d.available : null;
|
|
9594
|
+
return {
|
|
9595
|
+
available,
|
|
9596
|
+
currency: typeof d.currency === "string" && d.currency ? d.currency : "CNY",
|
|
9597
|
+
...typeof d.reserved === "number" ? { reserved: d.reserved } : {},
|
|
9598
|
+
// Hub 没配 HUB_PUBLIC_URL 时退回用接入时的 hubUrl
|
|
9599
|
+
topupUrl: typeof d.topupUrl === "string" && d.topupUrl ? d.topupUrl : `${hub}/topup`,
|
|
9600
|
+
...available === null && typeof d.reason === "string" ? { reason: d.reason } : {}
|
|
9601
|
+
};
|
|
9602
|
+
} catch (e) {
|
|
9603
|
+
return {
|
|
9604
|
+
available: null,
|
|
9605
|
+
currency: "CNY",
|
|
9606
|
+
reason: `\u8FDE\u63A5 Hub \u5931\u8D25\uFF1A${e.message}`
|
|
9607
|
+
};
|
|
9608
|
+
}
|
|
9609
|
+
}
|
|
9610
|
+
var AUTH_RETRY_THROTTLE_MS = 6e4;
|
|
9611
|
+
var lastAuthRetryAt = 0;
|
|
9612
|
+
function healBuiltinOnAuthFailure(store, message) {
|
|
9613
|
+
if (!/401|unauthorized|invalid[_\s]*api[_\s]*key|api[\s_-]*key\s*(?:无效|invalid)/i.test(message)) return;
|
|
9614
|
+
if (!store.loadFleetCredential()) return;
|
|
9615
|
+
if (!store.loadNodeLlm().providers.some((p) => p.id === BUILTIN_PROVIDER_ID)) return;
|
|
9616
|
+
if (Date.now() - lastAuthRetryAt < AUTH_RETRY_THROTTLE_MS) return;
|
|
9617
|
+
lastAuthRetryAt = Date.now();
|
|
9618
|
+
void ensureBuiltinProvider(store, { force: true }).catch(() => {
|
|
9619
|
+
});
|
|
9620
|
+
}
|
|
9621
|
+
var lastFailureAt = 0;
|
|
9622
|
+
function scheduleBuiltinProvider(store, log = () => {
|
|
9623
|
+
}) {
|
|
9624
|
+
if (Date.now() - lastFailureAt < RETRY_AFTER_MS) return;
|
|
9625
|
+
void ensureBuiltinProvider(store).then((r) => {
|
|
9626
|
+
if (r.status === "ready") {
|
|
9627
|
+
if (r.refreshed) log(`[builtin-llm] \u5DF2\u4E0B\u53D1\u5185\u7F6E\u6A21\u578B\uFF1A${r.provider.name}\uFF08${r.provider.model}\uFF09`);
|
|
9628
|
+
return;
|
|
9629
|
+
}
|
|
9630
|
+
if (r.status === "disabled") return;
|
|
9631
|
+
lastFailureAt = Date.now();
|
|
9632
|
+
if (r.status === "unavailable") log(`[builtin-llm] \u6682\u65E0\u5185\u7F6E\u6A21\u578B\uFF1A${r.reason}`);
|
|
9633
|
+
else log(`[builtin-llm] \u62C9\u53D6\u5185\u7F6E\u6A21\u578B\u5931\u8D25\uFF1A${r.reason}`);
|
|
9634
|
+
}).catch(() => {
|
|
9635
|
+
lastFailureAt = Date.now();
|
|
9636
|
+
});
|
|
9637
|
+
}
|
|
9638
|
+
|
|
9388
9639
|
// src/server/usage.ts
|
|
9389
9640
|
init_cjs_shims();
|
|
9390
9641
|
var fs18 = __toESM(require("fs"), 1);
|
|
@@ -11446,6 +11697,7 @@ data: ${JSON.stringify(entry.data)}
|
|
|
11446
11697
|
this._setStatus(rt, "error");
|
|
11447
11698
|
this._persist(rt);
|
|
11448
11699
|
this._emitNotify(rt, "error", this._turnEndData(rt, event.data, {}));
|
|
11700
|
+
healBuiltinOnAuthFailure(this.store, String(event.data["error"] ?? ""));
|
|
11449
11701
|
break;
|
|
11450
11702
|
case "interrupted" /* INTERRUPTED */:
|
|
11451
11703
|
this._setStatus(rt, "interrupted");
|
|
@@ -11876,7 +12128,6 @@ var path25 = __toESM(require("path"), 1);
|
|
|
11876
12128
|
// src/server/projects.ts
|
|
11877
12129
|
init_cjs_shims();
|
|
11878
12130
|
var path21 = __toESM(require("path"), 1);
|
|
11879
|
-
var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
|
|
11880
12131
|
function toProjectSummary(store, tenant) {
|
|
11881
12132
|
return {
|
|
11882
12133
|
id: tenant.id,
|
|
@@ -12016,7 +12267,7 @@ function createProject(store, input) {
|
|
|
12016
12267
|
id,
|
|
12017
12268
|
name: explicitName || defaultName,
|
|
12018
12269
|
apiKey: generateApiKey(),
|
|
12019
|
-
model: typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() :
|
|
12270
|
+
model: typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : defaultTenantModel(store),
|
|
12020
12271
|
provider: {
|
|
12021
12272
|
apiKey: typeof raw.providerApiKey === "string" && raw.providerApiKey.trim() ? raw.providerApiKey.trim() : void 0,
|
|
12022
12273
|
baseURL: typeof raw.baseURL === "string" && raw.baseURL.trim() ? raw.baseURL.trim() : void 0
|
|
@@ -12041,7 +12292,7 @@ function buildNodeInfo(store, opts) {
|
|
|
12041
12292
|
nodeId: opts.nodeId ?? ensureNodeId(store),
|
|
12042
12293
|
platform: process.platform,
|
|
12043
12294
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
12044
|
-
version: "0.2.
|
|
12295
|
+
version: "0.2.20",
|
|
12045
12296
|
admin: opts.admin,
|
|
12046
12297
|
projects: opts.projects
|
|
12047
12298
|
};
|
|
@@ -12407,10 +12658,133 @@ function makeDirIn(parentInput, name) {
|
|
|
12407
12658
|
init_cjs_shims();
|
|
12408
12659
|
var fs20 = __toESM(require("fs"), 1);
|
|
12409
12660
|
var path23 = __toESM(require("path"), 1);
|
|
12661
|
+
|
|
12662
|
+
// src/server/zipArchive.ts
|
|
12663
|
+
init_cjs_shims();
|
|
12664
|
+
var zlib = __toESM(require("zlib"), 1);
|
|
12665
|
+
var MAX_ZIP_ENTRIES = 2e4;
|
|
12666
|
+
var LOCAL_SIG = 67324752;
|
|
12667
|
+
var CENTRAL_SIG = 33639248;
|
|
12668
|
+
var EOCD_SIG = 101010256;
|
|
12669
|
+
var FLAG_UTF8 = 2048;
|
|
12670
|
+
var METHOD_STORE = 0;
|
|
12671
|
+
var METHOD_DEFLATE = 8;
|
|
12672
|
+
var CRC_TABLE = (() => {
|
|
12673
|
+
const table = new Int32Array(256);
|
|
12674
|
+
for (let n = 0; n < 256; n++) {
|
|
12675
|
+
let c = n;
|
|
12676
|
+
for (let k = 0; k < 8; k++) {
|
|
12677
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
12678
|
+
}
|
|
12679
|
+
table[n] = c;
|
|
12680
|
+
}
|
|
12681
|
+
return table;
|
|
12682
|
+
})();
|
|
12683
|
+
function crc32(buf) {
|
|
12684
|
+
let c = -1;
|
|
12685
|
+
for (let i = 0; i < buf.length; i++) {
|
|
12686
|
+
c = CRC_TABLE[(c ^ buf[i]) & 255] ^ c >>> 8;
|
|
12687
|
+
}
|
|
12688
|
+
return (c ^ -1) >>> 0;
|
|
12689
|
+
}
|
|
12690
|
+
function dosDateTime(d) {
|
|
12691
|
+
const year = Math.max(1980, d.getFullYear());
|
|
12692
|
+
return {
|
|
12693
|
+
time: d.getHours() << 11 | d.getMinutes() << 5 | d.getSeconds() >> 1,
|
|
12694
|
+
date: year - 1980 << 9 | d.getMonth() + 1 << 5 | d.getDate()
|
|
12695
|
+
};
|
|
12696
|
+
}
|
|
12697
|
+
function buildZip(entries) {
|
|
12698
|
+
if (entries.length > MAX_ZIP_ENTRIES) {
|
|
12699
|
+
throw new Error(`\u5F52\u6863\u6761\u76EE\u8FC7\u591A\uFF08${entries.length} > ${MAX_ZIP_ENTRIES}\uFF09`);
|
|
12700
|
+
}
|
|
12701
|
+
const chunks = [];
|
|
12702
|
+
const central = [];
|
|
12703
|
+
let offset = 0;
|
|
12704
|
+
for (const e of entries) {
|
|
12705
|
+
const name = Buffer.from(e.name, "utf-8");
|
|
12706
|
+
const isDir = e.name.endsWith("/");
|
|
12707
|
+
const raw = isDir ? Buffer.alloc(0) : e.data ?? Buffer.alloc(0);
|
|
12708
|
+
let method = METHOD_STORE;
|
|
12709
|
+
let body = raw;
|
|
12710
|
+
if (!isDir && raw.length > 0) {
|
|
12711
|
+
const deflated = zlib.deflateRawSync(raw, { level: 6 });
|
|
12712
|
+
if (deflated.length < raw.length) {
|
|
12713
|
+
method = METHOD_DEFLATE;
|
|
12714
|
+
body = deflated;
|
|
12715
|
+
}
|
|
12716
|
+
}
|
|
12717
|
+
const crc = isDir ? 0 : crc32(raw);
|
|
12718
|
+
const { time, date } = dosDateTime(e.mtime ?? /* @__PURE__ */ new Date());
|
|
12719
|
+
const local = Buffer.alloc(30);
|
|
12720
|
+
local.writeUInt32LE(LOCAL_SIG, 0);
|
|
12721
|
+
local.writeUInt16LE(20, 4);
|
|
12722
|
+
local.writeUInt16LE(FLAG_UTF8, 6);
|
|
12723
|
+
local.writeUInt16LE(method, 8);
|
|
12724
|
+
local.writeUInt16LE(time, 10);
|
|
12725
|
+
local.writeUInt16LE(date, 12);
|
|
12726
|
+
local.writeUInt32LE(crc, 14);
|
|
12727
|
+
local.writeUInt32LE(body.length, 18);
|
|
12728
|
+
local.writeUInt32LE(raw.length, 22);
|
|
12729
|
+
local.writeUInt16LE(name.length, 26);
|
|
12730
|
+
local.writeUInt16LE(0, 28);
|
|
12731
|
+
chunks.push(local, name);
|
|
12732
|
+
if (body.length > 0) chunks.push(body);
|
|
12733
|
+
central.push({
|
|
12734
|
+
name,
|
|
12735
|
+
method,
|
|
12736
|
+
crc,
|
|
12737
|
+
csize: body.length,
|
|
12738
|
+
usize: raw.length,
|
|
12739
|
+
offset,
|
|
12740
|
+
time,
|
|
12741
|
+
date,
|
|
12742
|
+
isDir
|
|
12743
|
+
});
|
|
12744
|
+
offset += local.length + name.length + body.length;
|
|
12745
|
+
}
|
|
12746
|
+
const cdStart = offset;
|
|
12747
|
+
for (const c of central) {
|
|
12748
|
+
const head = Buffer.alloc(46);
|
|
12749
|
+
head.writeUInt32LE(CENTRAL_SIG, 0);
|
|
12750
|
+
head.writeUInt16LE(20, 4);
|
|
12751
|
+
head.writeUInt16LE(20, 6);
|
|
12752
|
+
head.writeUInt16LE(FLAG_UTF8, 8);
|
|
12753
|
+
head.writeUInt16LE(c.method, 10);
|
|
12754
|
+
head.writeUInt16LE(c.time, 12);
|
|
12755
|
+
head.writeUInt16LE(c.date, 14);
|
|
12756
|
+
head.writeUInt32LE(c.crc, 16);
|
|
12757
|
+
head.writeUInt32LE(c.csize, 20);
|
|
12758
|
+
head.writeUInt32LE(c.usize, 24);
|
|
12759
|
+
head.writeUInt16LE(c.name.length, 28);
|
|
12760
|
+
head.writeUInt16LE(0, 30);
|
|
12761
|
+
head.writeUInt16LE(0, 32);
|
|
12762
|
+
head.writeUInt16LE(0, 34);
|
|
12763
|
+
head.writeUInt16LE(0, 36);
|
|
12764
|
+
head.writeUInt32LE(c.isDir ? 16 : 0, 38);
|
|
12765
|
+
head.writeUInt32LE(c.offset, 42);
|
|
12766
|
+
chunks.push(head, c.name);
|
|
12767
|
+
offset += head.length + c.name.length;
|
|
12768
|
+
}
|
|
12769
|
+
const eocd = Buffer.alloc(22);
|
|
12770
|
+
eocd.writeUInt32LE(EOCD_SIG, 0);
|
|
12771
|
+
eocd.writeUInt16LE(0, 4);
|
|
12772
|
+
eocd.writeUInt16LE(0, 6);
|
|
12773
|
+
eocd.writeUInt16LE(central.length, 8);
|
|
12774
|
+
eocd.writeUInt16LE(central.length, 10);
|
|
12775
|
+
eocd.writeUInt32LE(offset - cdStart, 12);
|
|
12776
|
+
eocd.writeUInt32LE(cdStart, 16);
|
|
12777
|
+
eocd.writeUInt16LE(0, 20);
|
|
12778
|
+
chunks.push(eocd);
|
|
12779
|
+
return Buffer.concat(chunks);
|
|
12780
|
+
}
|
|
12781
|
+
|
|
12782
|
+
// src/server/projectFiles.ts
|
|
12410
12783
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
12411
12784
|
var MAX_DOWNLOAD_BYTES = 16 * 1024 * 1024;
|
|
12412
12785
|
var MAX_BINARY_BYTES = 2.5 * 1024 * 1024;
|
|
12413
12786
|
var MAX_ENTRIES2 = 2e3;
|
|
12787
|
+
var MAX_ZIP_BYTES = 100 * 1024 * 1024;
|
|
12414
12788
|
function toPosix(p) {
|
|
12415
12789
|
return p.split(path23.sep).join("/");
|
|
12416
12790
|
}
|
|
@@ -12686,6 +13060,134 @@ function deleteProjectPaths(workspaceRoot, relPaths) {
|
|
|
12686
13060
|
}
|
|
12687
13061
|
return out;
|
|
12688
13062
|
}
|
|
13063
|
+
function formatBytes(n) {
|
|
13064
|
+
if (n >= 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
13065
|
+
if (n >= 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
13066
|
+
return `${n} B`;
|
|
13067
|
+
}
|
|
13068
|
+
function renameProjectPath(workspaceRoot, relPath, newName) {
|
|
13069
|
+
const base = projectRoot(workspaceRoot);
|
|
13070
|
+
const rel = (relPath ?? "").trim().replace(/^[/\\]+/, "");
|
|
13071
|
+
if (!rel) throw new Error("\u4E0D\u80FD\u91CD\u547D\u540D\u9879\u76EE\u6839\u76EE\u5F55");
|
|
13072
|
+
const abs = resolveWithin(base, rel);
|
|
13073
|
+
const name = (newName ?? "").trim();
|
|
13074
|
+
if (!name) throw new Error("\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
|
|
13075
|
+
if (name === "." || name === "..") throw new Error("\u540D\u79F0\u975E\u6CD5");
|
|
13076
|
+
if (/[/\\]/.test(name)) throw new Error("\u540D\u79F0\u4E0D\u80FD\u5305\u542B\u8DEF\u5F84\u5206\u9694\u7B26");
|
|
13077
|
+
if (!fs20.existsSync(abs)) throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
13078
|
+
const parent = path23.dirname(abs);
|
|
13079
|
+
const target = path23.join(parent, name);
|
|
13080
|
+
if (!isWithinRoot(base, target)) throw new Error("\u8DEF\u5F84\u8D8A\u754C\uFF08\u8D85\u51FA\u9879\u76EE\u5DE5\u4F5C\u533A\uFF09");
|
|
13081
|
+
if (fs20.existsSync(target)) {
|
|
13082
|
+
let same = false;
|
|
13083
|
+
try {
|
|
13084
|
+
const a = fs20.statSync(target);
|
|
13085
|
+
const b = fs20.statSync(abs);
|
|
13086
|
+
same = a.ino === b.ino && a.dev === b.dev;
|
|
13087
|
+
} catch {
|
|
13088
|
+
same = false;
|
|
13089
|
+
}
|
|
13090
|
+
if (!same) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u6587\u4EF6\u6216\u76EE\u5F55: ${name}`);
|
|
13091
|
+
}
|
|
13092
|
+
if (path23.resolve(target) === path23.resolve(abs)) {
|
|
13093
|
+
return { from: toPosix(path23.relative(base, abs)), path: toPosix(path23.relative(base, abs)) };
|
|
13094
|
+
}
|
|
13095
|
+
try {
|
|
13096
|
+
fs20.renameSync(abs, target);
|
|
13097
|
+
} catch (e) {
|
|
13098
|
+
throw new Error(`\u91CD\u547D\u540D\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
13099
|
+
}
|
|
13100
|
+
return { from: toPosix(path23.relative(base, abs)), path: toPosix(path23.relative(base, target)) };
|
|
13101
|
+
}
|
|
13102
|
+
function zipProjectPaths(workspaceRoot, relPaths, opts = {}) {
|
|
13103
|
+
const base = projectRoot(workspaceRoot);
|
|
13104
|
+
const maxBytes = opts.maxBytes && opts.maxBytes > 0 ? opts.maxBytes : MAX_ZIP_BYTES;
|
|
13105
|
+
const rels = (relPaths ?? []).map((x) => (x ?? "").trim().replace(/^[/\\]+/, ""));
|
|
13106
|
+
if (rels.length === 0) throw new Error("\u8BF7\u9009\u62E9\u8981\u538B\u7F29\u7684\u6587\u4EF6\u6216\u76EE\u5F55");
|
|
13107
|
+
if (rels.some((r) => !r)) throw new Error("\u4E0D\u80FD\u538B\u7F29\u9879\u76EE\u6839\u76EE\u5F55");
|
|
13108
|
+
const targets = rels.map((rel) => ({ rel, abs: resolveWithin(base, rel) }));
|
|
13109
|
+
const single = targets.length === 1 ? targets[0].abs : "";
|
|
13110
|
+
const items = [];
|
|
13111
|
+
let total = 0;
|
|
13112
|
+
const overLimit = () => new Error(
|
|
13113
|
+
`\u5171 ${formatBytes(total)}\uFF0C\u8D85\u8FC7 ${formatBytes(maxBytes)} \u4E0A\u9650\uFF0C\u4E0D\u652F\u6301\u538B\u7F29\u4E0B\u8F7D`
|
|
13114
|
+
);
|
|
13115
|
+
const walk = (absDir) => {
|
|
13116
|
+
let dirents;
|
|
13117
|
+
try {
|
|
13118
|
+
dirents = fs20.readdirSync(absDir, { withFileTypes: true });
|
|
13119
|
+
} catch {
|
|
13120
|
+
return 0;
|
|
13121
|
+
}
|
|
13122
|
+
dirents.sort((a, b) => a.name.localeCompare(b.name));
|
|
13123
|
+
let files = 0;
|
|
13124
|
+
for (const d of dirents) {
|
|
13125
|
+
if (d.isSymbolicLink()) continue;
|
|
13126
|
+
const child = path23.join(absDir, d.name);
|
|
13127
|
+
if (d.isDirectory()) {
|
|
13128
|
+
files += walk(child);
|
|
13129
|
+
continue;
|
|
13130
|
+
}
|
|
13131
|
+
if (!d.isFile()) continue;
|
|
13132
|
+
let st;
|
|
13133
|
+
try {
|
|
13134
|
+
st = fs20.statSync(child);
|
|
13135
|
+
} catch {
|
|
13136
|
+
continue;
|
|
13137
|
+
}
|
|
13138
|
+
total += st.size;
|
|
13139
|
+
if (total > maxBytes) throw overLimit();
|
|
13140
|
+
if (items.length >= MAX_ZIP_ENTRIES) {
|
|
13141
|
+
throw new Error(`\u5F52\u6863\u6761\u76EE\u8FC7\u591A\uFF08\u8D85\u8FC7 ${MAX_ZIP_ENTRIES} \u9879\uFF09\uFF0C\u8BF7\u7F29\u5C0F\u8303\u56F4\u540E\u518D\u8BD5`);
|
|
13142
|
+
}
|
|
13143
|
+
items.push({ abs: child, dir: false, size: st.size, mtime: st.mtime });
|
|
13144
|
+
files++;
|
|
13145
|
+
}
|
|
13146
|
+
if (files === 0) {
|
|
13147
|
+
let mtime = /* @__PURE__ */ new Date();
|
|
13148
|
+
try {
|
|
13149
|
+
mtime = fs20.statSync(absDir).mtime;
|
|
13150
|
+
} catch {
|
|
13151
|
+
}
|
|
13152
|
+
items.push({ abs: absDir, dir: true, size: 0, mtime });
|
|
13153
|
+
}
|
|
13154
|
+
return files;
|
|
13155
|
+
};
|
|
13156
|
+
for (const t of targets) {
|
|
13157
|
+
let st;
|
|
13158
|
+
try {
|
|
13159
|
+
st = fs20.statSync(t.abs);
|
|
13160
|
+
} catch {
|
|
13161
|
+
throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${t.rel}`);
|
|
13162
|
+
}
|
|
13163
|
+
if (st.isDirectory()) {
|
|
13164
|
+
walk(t.abs);
|
|
13165
|
+
continue;
|
|
13166
|
+
}
|
|
13167
|
+
total += st.size;
|
|
13168
|
+
if (total > maxBytes) throw overLimit();
|
|
13169
|
+
items.push({ abs: t.abs, dir: false, size: st.size, mtime: st.mtime });
|
|
13170
|
+
}
|
|
13171
|
+
const nameOf = (abs) => {
|
|
13172
|
+
const rel = toPosix(path23.relative(single || base, abs));
|
|
13173
|
+
return rel || path23.posix.basename(toPosix(abs));
|
|
13174
|
+
};
|
|
13175
|
+
const zipEntries = items.map((it) => {
|
|
13176
|
+
const name2 = nameOf(it.abs);
|
|
13177
|
+
if (it.dir) return { name: `${name2}/`, mtime: it.mtime };
|
|
13178
|
+
return { name: name2, data: fs20.readFileSync(it.abs), mtime: it.mtime };
|
|
13179
|
+
});
|
|
13180
|
+
const buf = buildZip(zipEntries);
|
|
13181
|
+
const first = targets[0].rel;
|
|
13182
|
+
const name = single ? `${path23.posix.basename(toPosix(first))}.zip` : "archive.zip";
|
|
13183
|
+
return {
|
|
13184
|
+
name,
|
|
13185
|
+
size: buf.length,
|
|
13186
|
+
total,
|
|
13187
|
+
entries: zipEntries.length,
|
|
13188
|
+
base64: buf.toString("base64")
|
|
13189
|
+
};
|
|
13190
|
+
}
|
|
12689
13191
|
|
|
12690
13192
|
// src/server/projectOps.ts
|
|
12691
13193
|
init_cjs_shims();
|
|
@@ -13015,6 +13517,7 @@ function projectStats(manager, tenantId) {
|
|
|
13015
13517
|
|
|
13016
13518
|
// src/server/nodeHost.ts
|
|
13017
13519
|
init_cjs_shims();
|
|
13520
|
+
var import_child_process = require("child_process");
|
|
13018
13521
|
var fs21 = __toESM(require("fs"), 1);
|
|
13019
13522
|
var os6 = __toESM(require("os"), 1);
|
|
13020
13523
|
function diskInfo(pathStr) {
|
|
@@ -13029,6 +13532,64 @@ function diskInfo(pathStr) {
|
|
|
13029
13532
|
return null;
|
|
13030
13533
|
}
|
|
13031
13534
|
}
|
|
13535
|
+
var PSEUDO_FS = /^(devfs|tmpfs|devtmpfs|proc|procfs|sysfs|none|udev|shm|mqueue|devpts|hugetlbfs|debugfs|tracefs|securityfs|pstore|bpf|configfs|fusectl|nsfs|autofs|efivarfs|ramfs|map)$/;
|
|
13536
|
+
function isInternalMount(mount) {
|
|
13537
|
+
if (mount === "/") return false;
|
|
13538
|
+
const prefixes = ["/proc", "/sys", "/run", "/snap"];
|
|
13539
|
+
if (process.platform === "darwin") prefixes.push("/System/Volumes");
|
|
13540
|
+
return prefixes.some((p) => mount === p || mount.startsWith(`${p}/`));
|
|
13541
|
+
}
|
|
13542
|
+
function readOnlyVolumes() {
|
|
13543
|
+
const out = /* @__PURE__ */ new Set();
|
|
13544
|
+
const res = (0, import_child_process.spawnSync)("mount", [], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
13545
|
+
if (res.error || res.status !== 0 || !res.stdout) return out;
|
|
13546
|
+
for (const line of res.stdout.split("\n")) {
|
|
13547
|
+
const m = line.match(/^.+? on (.+?) \(([^)]*)\)$/);
|
|
13548
|
+
if (!m || m[1] === "/") continue;
|
|
13549
|
+
if (m[2].split(",").some((opt) => opt.trim() === "read-only")) out.add(m[1]);
|
|
13550
|
+
}
|
|
13551
|
+
return out;
|
|
13552
|
+
}
|
|
13553
|
+
function diskList(workspacePath) {
|
|
13554
|
+
const out = [];
|
|
13555
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13556
|
+
const push = (path36, total, free) => {
|
|
13557
|
+
if (!path36 || seen.has(path36) || !Number.isFinite(total) || total <= 0) return;
|
|
13558
|
+
seen.add(path36);
|
|
13559
|
+
out.push({ path: path36, total, free });
|
|
13560
|
+
};
|
|
13561
|
+
if (process.platform === "win32") {
|
|
13562
|
+
for (const letter of "CDEFGHIJKLMNOPQRSTUVWXYZ") {
|
|
13563
|
+
const root = `${letter}:\\`;
|
|
13564
|
+
const d = diskInfo(root);
|
|
13565
|
+
if (d) push(root, d.total, d.free);
|
|
13566
|
+
}
|
|
13567
|
+
} else {
|
|
13568
|
+
const readonly = process.platform === "darwin" ? readOnlyVolumes() : /* @__PURE__ */ new Set();
|
|
13569
|
+
const res = (0, import_child_process.spawnSync)("df", ["-kP"], {
|
|
13570
|
+
encoding: "utf-8",
|
|
13571
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
13572
|
+
});
|
|
13573
|
+
if (!res.error && res.status === 0 && res.stdout) {
|
|
13574
|
+
for (const line of res.stdout.split("\n").slice(1)) {
|
|
13575
|
+
const m = line.trim().match(/^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s+(.+)$/);
|
|
13576
|
+
if (!m || PSEUDO_FS.test(m[1])) continue;
|
|
13577
|
+
if (/^\/dev\/loop\d+$/.test(m[1])) continue;
|
|
13578
|
+
if (isInternalMount(m[5]) || readonly.has(m[5])) continue;
|
|
13579
|
+
push(m[5], Number(m[2]) * 1024, Number(m[4]) * 1024);
|
|
13580
|
+
}
|
|
13581
|
+
}
|
|
13582
|
+
}
|
|
13583
|
+
const primary = diskInfo(workspacePath);
|
|
13584
|
+
if (primary) {
|
|
13585
|
+
const covered = out.some((d) => {
|
|
13586
|
+
const root = d.path.endsWith("/") || d.path.endsWith("\\") ? d.path : `${d.path}/`;
|
|
13587
|
+
return workspacePath === d.path || workspacePath.startsWith(root);
|
|
13588
|
+
});
|
|
13589
|
+
if (!covered) push(primary.path, primary.total, primary.free);
|
|
13590
|
+
}
|
|
13591
|
+
return out.sort((a, b) => b.total - a.total);
|
|
13592
|
+
}
|
|
13032
13593
|
function networkInfo() {
|
|
13033
13594
|
const out = [];
|
|
13034
13595
|
const ifaces = os6.networkInterfaces();
|
|
@@ -13084,6 +13645,7 @@ function overview(store, manager) {
|
|
|
13084
13645
|
function buildNodeHost(store, manager, diskPath) {
|
|
13085
13646
|
const cpus2 = os6.cpus();
|
|
13086
13647
|
const ov = overview(store, manager);
|
|
13648
|
+
const disks = diskList(diskPath || process.cwd());
|
|
13087
13649
|
return {
|
|
13088
13650
|
nodeId: store.loadNodeId() ?? "",
|
|
13089
13651
|
hostname: os6.hostname(),
|
|
@@ -13091,7 +13653,7 @@ function buildNodeHost(store, manager, diskPath) {
|
|
|
13091
13653
|
arch: process.arch,
|
|
13092
13654
|
release: os6.release(),
|
|
13093
13655
|
uptimeSec: Math.round(os6.uptime()),
|
|
13094
|
-
version: "0.2.
|
|
13656
|
+
version: "0.2.20",
|
|
13095
13657
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
13096
13658
|
cpu: {
|
|
13097
13659
|
count: cpus2.length,
|
|
@@ -13101,6 +13663,7 @@ function buildNodeHost(store, manager, diskPath) {
|
|
|
13101
13663
|
memory: { total: os6.totalmem(), free: os6.freemem() },
|
|
13102
13664
|
network: networkInfo(),
|
|
13103
13665
|
disk: diskInfo(diskPath || process.cwd()),
|
|
13666
|
+
disks,
|
|
13104
13667
|
...ov
|
|
13105
13668
|
};
|
|
13106
13669
|
}
|
|
@@ -14326,6 +14889,41 @@ function createApp(store, manager, opts = {}) {
|
|
|
14326
14889
|
}
|
|
14327
14890
|
}
|
|
14328
14891
|
},
|
|
14892
|
+
{
|
|
14893
|
+
method: "POST",
|
|
14894
|
+
pattern: ["files", "rename"],
|
|
14895
|
+
// 重命名(只改同目录下的名字)
|
|
14896
|
+
fn: (_req, res, { tenant, body }) => {
|
|
14897
|
+
const raw = body ?? {};
|
|
14898
|
+
try {
|
|
14899
|
+
ok(
|
|
14900
|
+
res,
|
|
14901
|
+
renameProjectPath(
|
|
14902
|
+
tenant.workspaceRoot ?? null,
|
|
14903
|
+
String(raw.path ?? ""),
|
|
14904
|
+
String(raw.name ?? "")
|
|
14905
|
+
)
|
|
14906
|
+
);
|
|
14907
|
+
} catch (e) {
|
|
14908
|
+
throw new ApiError(400, "bad_request", e instanceof Error ? e.message : String(e));
|
|
14909
|
+
}
|
|
14910
|
+
}
|
|
14911
|
+
},
|
|
14912
|
+
{
|
|
14913
|
+
method: "POST",
|
|
14914
|
+
pattern: ["files", "zip"],
|
|
14915
|
+
// 压缩下载(多选一起打包;超上限由实现拒绝)
|
|
14916
|
+
fn: (_req, res, { tenant, body }) => {
|
|
14917
|
+
const raw = body ?? {};
|
|
14918
|
+
const paths = Array.isArray(raw.paths) ? raw.paths.map((x) => String(x)) : [];
|
|
14919
|
+
if (paths.length === 0) throw new ApiError(400, "bad_request", "paths \u4E0D\u80FD\u4E3A\u7A7A");
|
|
14920
|
+
try {
|
|
14921
|
+
ok(res, zipProjectPaths(tenant.workspaceRoot ?? null, paths));
|
|
14922
|
+
} catch (e) {
|
|
14923
|
+
throw new ApiError(400, "bad_request", e instanceof Error ? e.message : String(e));
|
|
14924
|
+
}
|
|
14925
|
+
}
|
|
14926
|
+
},
|
|
14329
14927
|
// ---------- 项目(= 租户)管理:控制台多项目视图 ----------
|
|
14330
14928
|
// 仅管理员密钥可用;项目级与会话级接口仍用项目自己的 tenant API Key
|
|
14331
14929
|
// (或管理员密钥 + X-Project-Id 头),互不暴露明文密钥。
|
|
@@ -14384,6 +14982,29 @@ function createApp(store, manager, opts = {}) {
|
|
|
14384
14982
|
ok(res, nodeLlmView(next));
|
|
14385
14983
|
}
|
|
14386
14984
|
},
|
|
14985
|
+
// 大模型账户余额(控制台「余额不足 → 查看余额并充值」用;由节点向 Hub 代查)
|
|
14986
|
+
{
|
|
14987
|
+
method: "GET",
|
|
14988
|
+
pattern: ["node", "llm", "balance"],
|
|
14989
|
+
adminOnly: true,
|
|
14990
|
+
fn: async (_req, res) => {
|
|
14991
|
+
ok(res, await fetchLlmBalance(store));
|
|
14992
|
+
}
|
|
14993
|
+
},
|
|
14994
|
+
// 重新拉取平台内置模型凭据(用户点了「刷新内置模型」,或 Key 在控制台被重置)
|
|
14995
|
+
{
|
|
14996
|
+
method: "POST",
|
|
14997
|
+
pattern: ["node", "llm", "builtin-refresh"],
|
|
14998
|
+
adminOnly: true,
|
|
14999
|
+
fn: async (_req, res) => {
|
|
15000
|
+
const r = await ensureBuiltinProvider(store, { force: true });
|
|
15001
|
+
if (r.status !== "ready") {
|
|
15002
|
+
ok(res, { ok: false, reason: r.reason });
|
|
15003
|
+
return;
|
|
15004
|
+
}
|
|
15005
|
+
ok(res, { ok: true, view: nodeLlmView(store.loadNodeLlm()) });
|
|
15006
|
+
}
|
|
15007
|
+
},
|
|
14387
15008
|
// ---------- 用量(账本 + 汇总 + 价格) ----------
|
|
14388
15009
|
{
|
|
14389
15010
|
method: "GET",
|
|
@@ -14810,7 +15431,7 @@ function createApp(store, manager, opts = {}) {
|
|
|
14810
15431
|
json(res, 200, {
|
|
14811
15432
|
status: "ok",
|
|
14812
15433
|
// 版本让运维/探针能确认线上跑的是哪个构建(更新与回滚据此判断)
|
|
14813
|
-
version: "0.2.
|
|
15434
|
+
version: "0.2.20",
|
|
14814
15435
|
features: { mcp: hasServerMcpConfig() }
|
|
14815
15436
|
});
|
|
14816
15437
|
return;
|
|
@@ -15150,6 +15771,22 @@ async function dispatchFleetCommand(deps, env3) {
|
|
|
15150
15771
|
store.saveNodeLlm(next);
|
|
15151
15772
|
return ok2(nodeLlmView(next));
|
|
15152
15773
|
}
|
|
15774
|
+
/*
|
|
15775
|
+
* 重新拉取平台内置模型(志前 API)。
|
|
15776
|
+
*
|
|
15777
|
+
* 用途:用户在控制台点了「刷新」、或内置模型的 Key 在控制台被重置后需要换新。
|
|
15778
|
+
* 走写链串行——它会改写 connections.json,不能与 update_node_llm 并发。
|
|
15779
|
+
*/
|
|
15780
|
+
// 余额查询:只读、幂等,可进只读并发池
|
|
15781
|
+
case "get_llm_balance":
|
|
15782
|
+
return ok2(await fetchLlmBalance(store));
|
|
15783
|
+
case "refresh_builtin_llm": {
|
|
15784
|
+
const r = await ensureBuiltinProvider(store, { force: true });
|
|
15785
|
+
if (r.status !== "ready") {
|
|
15786
|
+
return ok2({ ok: false, reason: r.reason });
|
|
15787
|
+
}
|
|
15788
|
+
return ok2({ ok: true, view: nodeLlmView(store.loadNodeLlm()) });
|
|
15789
|
+
}
|
|
15153
15790
|
case "get_usage_summary": {
|
|
15154
15791
|
const ledger = manager.usageLedger();
|
|
15155
15792
|
if (!ledger) return ok2({ total: null, groups: [] });
|
|
@@ -15334,6 +15971,24 @@ async function dispatchFleetCommand(deps, env3) {
|
|
|
15334
15971
|
const t = project();
|
|
15335
15972
|
return ok2(searchProjectFiles(t.workspaceRoot ?? null, String(payload.query ?? "")));
|
|
15336
15973
|
}
|
|
15974
|
+
case "rename_path": {
|
|
15975
|
+
const t = project();
|
|
15976
|
+
return ok2(
|
|
15977
|
+
renameProjectPath(
|
|
15978
|
+
t.workspaceRoot ?? null,
|
|
15979
|
+
String(payload.path ?? ""),
|
|
15980
|
+
String(payload.name ?? "")
|
|
15981
|
+
)
|
|
15982
|
+
);
|
|
15983
|
+
}
|
|
15984
|
+
case "zip_download": {
|
|
15985
|
+
const t = project();
|
|
15986
|
+
const raw = payload.paths;
|
|
15987
|
+
const paths = Array.isArray(raw) ? raw.map((x) => String(x)) : [];
|
|
15988
|
+
if (paths.length === 0) throw new ApiError(400, "bad_request", "paths \u4E0D\u80FD\u4E3A\u7A7A");
|
|
15989
|
+
const maxBytes = typeof payload.maxBytes === "number" ? payload.maxBytes : void 0;
|
|
15990
|
+
return ok2(zipProjectPaths(t.workspaceRoot ?? null, paths, { maxBytes }));
|
|
15991
|
+
}
|
|
15337
15992
|
case "list_tools": {
|
|
15338
15993
|
const t = project();
|
|
15339
15994
|
return ok2(buildToolCatalog(t.workspaceRoot ?? process.cwd()));
|
|
@@ -15686,6 +16341,22 @@ function toWireAck(ack) {
|
|
|
15686
16341
|
data: ack.data === void 0 ? void 0 : utf8Encode(JSON.stringify(ack.data))
|
|
15687
16342
|
};
|
|
15688
16343
|
}
|
|
16344
|
+
var ACK_JSON_MAX_BYTES = MAX_FRAME_BYTES - 64 * 1024;
|
|
16345
|
+
function mb(bytes) {
|
|
16346
|
+
return (bytes / (1024 * 1024)).toFixed(1);
|
|
16347
|
+
}
|
|
16348
|
+
function guardAckSize(ack, json2) {
|
|
16349
|
+
if (!ack.ok || ack.data === void 0) return ack;
|
|
16350
|
+
const bytes = Buffer.byteLength(json2, "utf8");
|
|
16351
|
+
if (bytes <= ACK_JSON_MAX_BYTES) return ack;
|
|
16352
|
+
return {
|
|
16353
|
+
reqId: ack.reqId,
|
|
16354
|
+
type: ack.type,
|
|
16355
|
+
ok: false,
|
|
16356
|
+
// 带上实际大小:用户据此能判断「差一点」还是「差很多」,而不是只看到「失败」
|
|
16357
|
+
error: `\u7ED3\u679C\u8FC7\u5927\uFF08${mb(bytes)} MB\uFF09\uFF0C\u8D85\u8FC7\u5355\u5E27\u4F20\u8F93\u4E0A\u9650\uFF08${mb(ACK_JSON_MAX_BYTES)} MB\uFF09\uFF0C\u8BF7\u7F29\u5C0F\u8303\u56F4\u540E\u91CD\u8BD5`
|
|
16358
|
+
};
|
|
16359
|
+
}
|
|
15689
16360
|
function fromWireCommand(env3) {
|
|
15690
16361
|
let payload = {};
|
|
15691
16362
|
if (env3.payload && env3.payload.length > 0) {
|
|
@@ -16156,6 +16827,7 @@ var READONLY_CMD_TYPES = /* @__PURE__ */ new Set([
|
|
|
16156
16827
|
"get_node_llm",
|
|
16157
16828
|
"list_node_llm_models",
|
|
16158
16829
|
"get_usage_summary",
|
|
16830
|
+
"get_llm_balance",
|
|
16159
16831
|
"get_usage_records",
|
|
16160
16832
|
"get_usage_pricing",
|
|
16161
16833
|
"list_projects",
|
|
@@ -16580,7 +17252,7 @@ var FleetNodeBridge = class {
|
|
|
16580
17252
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
16581
17253
|
platform: process.platform,
|
|
16582
17254
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
16583
|
-
version: "0.2.
|
|
17255
|
+
version: "0.2.20",
|
|
16584
17256
|
ip: primaryIp()
|
|
16585
17257
|
};
|
|
16586
17258
|
}
|
|
@@ -16678,7 +17350,7 @@ var FleetNodeBridge = class {
|
|
|
16678
17350
|
label: this.opts.deps.store.loadNodeLabel() ?? void 0,
|
|
16679
17351
|
platform: process.platform,
|
|
16680
17352
|
mode: process.env.NODE_MODE ?? "standalone",
|
|
16681
|
-
version: "0.2.
|
|
17353
|
+
version: "0.2.20",
|
|
16682
17354
|
ip: primaryIp(),
|
|
16683
17355
|
projects,
|
|
16684
17356
|
totals,
|
|
@@ -16840,7 +17512,13 @@ var FleetNodeBridge = class {
|
|
|
16840
17512
|
const stamped = { ...ack, reqId: env3.reqId, type: env3.type };
|
|
16841
17513
|
const json2 = JSON.stringify(stamped);
|
|
16842
17514
|
const __t1 = dbgNow();
|
|
16843
|
-
|
|
17515
|
+
const payload = guardAckSize(stamped, json2);
|
|
17516
|
+
if (payload !== stamped) {
|
|
17517
|
+
console.warn(
|
|
17518
|
+
`[fleet] ${env3.type} \u56DE\u6267 ${(json2.length / 1024 / 1024).toFixed(1)}MB \u8D85\u8FC7\u5355\u5E27\u4E0A\u9650\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u9519\u8BEF\u56DE\u6267`
|
|
17519
|
+
);
|
|
17520
|
+
}
|
|
17521
|
+
this._send({ kind: "ack", clientId: env3.clientId, ack: payload });
|
|
16844
17522
|
dbgMs(
|
|
16845
17523
|
"publishAck",
|
|
16846
17524
|
__t0,
|
|
@@ -18416,7 +19094,7 @@ var path29 = __toESM(require("path"), 1);
|
|
|
18416
19094
|
var import_node_child_process5 = require("child_process");
|
|
18417
19095
|
var import_node_module3 = require("module");
|
|
18418
19096
|
function currentVersion() {
|
|
18419
|
-
const injected = "0.2.
|
|
19097
|
+
const injected = "0.2.20"?.trim();
|
|
18420
19098
|
if (injected) return injected;
|
|
18421
19099
|
try {
|
|
18422
19100
|
const req2 = (0, import_node_module3.createRequire)(importMetaUrl);
|
|
@@ -19142,7 +19820,9 @@ function seedDemoTenant(store) {
|
|
|
19142
19820
|
// 未显式指定时随机生成:固定默认密钥一旦随 HOST=0.0.0.0 暴露到公网,等于公开凭据。
|
|
19143
19821
|
// 只在首次创建时生成并落盘,后续启动沿用磁盘上的值(启动日志会打印完整密钥)。
|
|
19144
19822
|
apiKey: env2("TENANT_API_KEY") ?? generateApiKey(),
|
|
19145
|
-
|
|
19823
|
+
// 不再硬编码具体厂商型号:志前 API 只认 deepseek-flash,写死 deepseek-v4-flash
|
|
19824
|
+
// 会让新装节点的第一次请求 404。默认值统一走 defaultTenantModel(见 nodeLlm.ts)。
|
|
19825
|
+
model: defaultTenantModel(store),
|
|
19146
19826
|
provider: {
|
|
19147
19827
|
apiKey: env2("OPENAI_API_KEY"),
|
|
19148
19828
|
baseURL: env2("OPENAI_BASE_URL")
|
|
@@ -19282,6 +19962,8 @@ function startServer(opts = {}) {
|
|
|
19282
19962
|
const store = new Store(dataDir);
|
|
19283
19963
|
const demo = seedDemoTenant(store);
|
|
19284
19964
|
syncTenantModels(store);
|
|
19965
|
+
const nodeModel = nodeDefaultProvider(store)?.model;
|
|
19966
|
+
if (nodeModel) adoptBuiltinModelForTenants(store, nodeModel);
|
|
19285
19967
|
const usageLedger = new UsageLedger(dataDir);
|
|
19286
19968
|
usageLedger.init();
|
|
19287
19969
|
const manager = new SessionManager(store, void 0, { usage: usageLedger });
|
|
@@ -19425,8 +20107,12 @@ function startServer(opts = {}) {
|
|
|
19425
20107
|
if (initialCred) {
|
|
19426
20108
|
connectFleet(initialCred);
|
|
19427
20109
|
} else if (opts.autoEnroll) {
|
|
19428
|
-
void fleetController.enroll(opts.autoEnroll).then((st) =>
|
|
20110
|
+
void fleetController.enroll(opts.autoEnroll).then((st) => {
|
|
20111
|
+
console.log(`[fleet] \u5DF2\u81EA\u52A8\u63A5\u5165\uFF0C\u8282\u70B9 ${st.nodeId}\uFF08${st.gatewayUrl}\uFF09`);
|
|
20112
|
+
scheduleBuiltinProvider(store, (m) => console.log(m));
|
|
20113
|
+
}).catch((e) => console.warn(`[fleet] \u81EA\u52A8\u63A5\u5165\u5931\u8D25\uFF08\u53EF\u7A0D\u540E /link \u91CD\u8BD5\uFF09: ${e instanceof Error ? e.message : e}`));
|
|
19429
20114
|
}
|
|
20115
|
+
if (initialCred) scheduleBuiltinProvider(store, (m) => console.log(m));
|
|
19430
20116
|
const pairingCodes = new PairingCodeStore();
|
|
19431
20117
|
const qq = new QqManager({
|
|
19432
20118
|
store,
|