@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.
@@ -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 providers = cfg.providers.map((p) => {
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
- id: p.id,
9296
- name: p.name,
9297
- type: p.type ?? "",
9298
- baseURL: p.baseURL,
9299
- model: p.model,
9300
- hasApiKey: Boolean(p.apiKey),
9301
- models: p.models ?? [],
9302
- nonChat: p.nonChat ?? [],
9303
- modelMeta: p.modelMeta ?? [],
9304
- reasoningEffort: p.reasoningEffort ?? null,
9305
- global: false
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
- next.providers = raw.providers.filter((x) => Boolean(x) && typeof x === "object").filter((x) => x.global !== true && String(x.id ?? "") !== GLOBAL_PROVIDER_ID).map((x) => {
9320
- const id = String(x.id ?? "").trim();
9321
- if (!id) throw new ApiError(400, "bad_request", "\u4F9B\u5E94\u5546 id \u4E0D\u80FD\u4E3A\u7A7A");
9322
- const prev = old.get(id);
9323
- const apiKey = String(x.apiKey ?? "").trim();
9324
- const models = normalizeModelList(x.models) ?? prev?.models;
9325
- const nonChat = normalizeModelList(x.nonChat) ?? prev?.nonChat;
9326
- const modelMeta = normalizeModelMeta(x.modelMeta) ?? prev?.modelMeta;
9327
- return {
9328
- id,
9329
- name: String(x.name ?? prev?.name ?? id).trim(),
9330
- type: x.type == null ? prev?.type : String(x.type),
9331
- baseURL: String(x.baseURL ?? prev?.baseURL ?? "").trim(),
9332
- model: String(x.model ?? prev?.model ?? "").trim(),
9333
- apiKey: apiKey || prev?.apiKey,
9334
- reasoningEffort: x.reasoningEffort === void 0 ? prev?.reasoningEffort ?? null : normReasoningEffort(x.reasoningEffort),
9335
- ...models ? { models } : {},
9336
- ...nonChat ? { nonChat } : {},
9337
- ...modelMeta ? { modelMeta } : {}
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() : DEFAULT_MODEL(),
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.16",
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.16",
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.16",
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.16",
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.16",
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
- this._send({ kind: "ack", clientId: env3.clientId, ack: stamped });
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.16"?.trim();
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
- model: env2("MODEL") ?? env2("OPENAI_MODEL") ?? "deepseek-v4-flash",
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) => console.log(`[fleet] \u5DF2\u81EA\u52A8\u63A5\u5165\uFF0C\u8282\u70B9 ${st.nodeId}\uFF08${st.gatewayUrl}\uFF09`)).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}`));
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,