@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.
@@ -588,7 +588,7 @@ var import_config = require("dotenv/config");
588
588
  var fs34 = __toESM(require("fs"), 1);
589
589
  var readline4 = __toESM(require("readline"), 1);
590
590
  var path43 = __toESM(require("path"), 1);
591
- var import_child_process = require("child_process");
591
+ var import_child_process2 = require("child_process");
592
592
  var import_util = require("util");
593
593
 
594
594
  // src/index.ts
@@ -747,15 +747,27 @@ function networkErrorHints(message, errorType = "") {
747
747
  hints.push("\u2464 IPv6 \u6296\u52A8\u53EF\u8BD5\uFF1ANODE_OPTIONS=--dns-result-order=ipv4first");
748
748
  return hints;
749
749
  }
750
+ function authErrorHints(message, code = "") {
751
+ const text = `${message} ${code}`;
752
+ if (!/401|403|unauthorized|forbidden|invalid[_\s]*api[_\s]*key|api[\s_-]*key\s*(?:无效|invalid)|管理令牌无效/i.test(text)) {
753
+ return [];
754
+ }
755
+ return [
756
+ "\u51ED\u636E\u65E0\u6548\uFF1A\u82E5\u7528\u7684\u662F\u300C\u5FD7\u524D\u5927\u6A21\u578B\uFF08\u5185\u7F6E\uFF09\u300D\uFF0C\u5728\u8282\u70B9\u8BBE\u7F6E\u7684\u6A21\u578B\u9875\u70B9\u300C\u5237\u65B0\u5185\u7F6E\u6A21\u578B\u300D\u91CD\u65B0\u9886\u53D6 Key\uFF08\u5728\u63A7\u5236\u53F0\u91CD\u7F6E\u8FC7 Key \u540E\u9700\u8981\u8FD9\u4E00\u6B65\uFF09\uFF1B\u82E5\u662F\u81EA\u586B\u7684\u4F9B\u5E94\u5546\uFF0C\u8BF7\u68C0\u67E5 Key \u662F\u5426\u6B63\u786E\u6216\u5DF2\u8FC7\u671F"
757
+ ];
758
+ }
750
759
  function paramErrorHints(message, code = "") {
751
760
  const text = `${message} ${code}`;
752
761
  const hints = [];
753
- if (/not[_\s]*(activated|enabled|purchased)|arrearage|quota|subscription|insufficient|未开通|欠费|额度/i.test(
762
+ if (/not[_\s]*(activated|enabled|purchased)|arrearage|quota|subscription|insufficient|未开通|欠费|额度|余额不足/i.test(
754
763
  text
755
764
  )) {
756
765
  hints.push(
757
766
  "\u8BE5\u6A21\u578B\u5728\u6B64 **API Key/\u8D26\u53F7** \u4E0B\u672A\u5F00\u901A\uFF08\u6216\u8D26\u53F7\u6B20\u8D39\u3001\u989D\u5EA6\u7528\u5C3D\uFF09\uFF1A\u5230\u7F51\u5173\u63A7\u5236\u53F0\u5F00\u901A\u8BE5\u6A21\u578B\uFF0C\u6216\u6362\u4E00\u4E2A\u5DF2\u5F00\u901A\u7684\u6A21\u578B"
758
767
  );
768
+ hints.push(
769
+ "\u5982\u679C\u4F60\u7528\u7684\u662F\u300C\u5FD7\u524D\u5927\u6A21\u578B\uFF08\u5185\u7F6E\uFF09\u300D\uFF1A\u5230 https://hub.cd-zq.com \u67E5\u770B\u4F59\u989D\u5E76\u5145\u503C\uFF1B\u4E5F\u53EF\u4EE5\u5148\u5207\u6362\u5230\u4F60\u81EA\u5DF1\u914D\u7F6E\u7684\u5176\u5B83\u6A21\u578B\u7EE7\u7EED\uFF08\u8282\u70B9\u8BBE\u7F6E \u2192 \u6A21\u578B \u2192 \u8BBE\u4E3A\u9ED8\u8BA4\uFF09"
770
+ );
759
771
  }
760
772
  if (/invalid[_\s]*parameter|invalidparameter|required[_\s]+parameter|missing[_\s]+parameter|invalid[_\s]*model|model[_\s]*not[_\s]*found|not[_\s]*support|unsupported/i.test(
761
773
  text
@@ -4998,6 +5010,9 @@ var ConsoleHook = class {
4998
5010
  for (const hint of networkErrorHints(error2, errorType)) {
4999
5011
  cliLog(` \u{1F4A1} ${hint}`);
5000
5012
  }
5013
+ for (const hint of authErrorHints(error2, errorType)) {
5014
+ cliLog(` \u{1F4A1} ${hint}`);
5015
+ }
5001
5016
  for (const hint of paramErrorHints(error2)) {
5002
5017
  cliLog(` \u{1F4A1} ${hint}`);
5003
5018
  }
@@ -9991,6 +10006,15 @@ function commonLegacyFiles() {
9991
10006
  return [path17.join(stateDir(), "data", "node-llm.json")];
9992
10007
  }
9993
10008
 
10009
+ // src/server/llmIds.ts
10010
+ init_cjs_shims();
10011
+ var GLOBAL_PROVIDER_ID = "__env__";
10012
+ var BUILTIN_PROVIDER_ID = "__zqai__";
10013
+ function isBuiltinProvider(id) {
10014
+ return id === BUILTIN_PROVIDER_ID;
10015
+ }
10016
+ var FALLBACK_MODEL = "gpt-4o";
10017
+
9994
10018
  // src/cli/connection.ts
9995
10019
  var PROVIDERS = Object.fromEntries(
9996
10020
  LLM_PRESETS.map((p) => [
@@ -10105,6 +10129,7 @@ function useConnection(id) {
10105
10129
  return true;
10106
10130
  }
10107
10131
  function removeConnection(id) {
10132
+ if (id === BUILTIN_PROVIDER_ID) return false;
10108
10133
  const f = read();
10109
10134
  const next = f.providers.filter((p) => p.id !== id);
10110
10135
  if (next.length === f.providers.length) return false;
@@ -10388,7 +10413,7 @@ var GLYPHS = {
10388
10413
  T: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", " "]
10389
10414
  };
10390
10415
  var GLYPH_ROWS = 6;
10391
- var GLYPH_COLS = 9;
10416
+ var GLYPH_COLS = 11;
10392
10417
  var GRADIENT = [
10393
10418
  [34, 211, 238],
10394
10419
  [99, 102, 241],
@@ -10994,7 +11019,23 @@ var Store = class {
10994
11019
  legacyFiles: [path19.join(this.root, "node-llm.json")]
10995
11020
  });
10996
11021
  const old = new Map(cur.providers.map((p) => [p.id, p]));
10997
- const providers = cfg.providers.map((p) => {
11022
+ const src = [...cfg.providers];
11023
+ const prevBuiltin = cur.providers.find((p) => p.id === BUILTIN_PROVIDER_ID);
11024
+ if (prevBuiltin && !src.some((p) => p.id === BUILTIN_PROVIDER_ID)) {
11025
+ src.unshift({
11026
+ id: prevBuiltin.id,
11027
+ name: prevBuiltin.name,
11028
+ type: prevBuiltin.type,
11029
+ baseURL: prevBuiltin.baseURL,
11030
+ model: prevBuiltin.model,
11031
+ apiKey: decryptKey(prevBuiltin.apiKeyEnc, file) ?? void 0,
11032
+ reasoningEffort: prevBuiltin.reasoningEffort ?? null,
11033
+ ...prevBuiltin.models ? { models: prevBuiltin.models } : {},
11034
+ ...prevBuiltin.nonChat ? { nonChat: prevBuiltin.nonChat } : {},
11035
+ ...prevBuiltin.modelMeta ? { modelMeta: prevBuiltin.modelMeta } : {}
11036
+ });
11037
+ }
11038
+ const providers = src.map((p) => {
10998
11039
  const prev = old.get(p.id);
10999
11040
  const apiKeyEnc = p.apiKey ? encryptKey(p.apiKey, file) : prev?.apiKeyEnc ?? "";
11000
11041
  return {
@@ -11185,7 +11226,6 @@ function loadServerMcpConfigs(tenant) {
11185
11226
 
11186
11227
  // src/server/nodeLlm.ts
11187
11228
  init_cjs_shims();
11188
- var GLOBAL_PROVIDER_ID = "__env__";
11189
11229
  function envProvider() {
11190
11230
  const apiKey = process.env.OPENAI_API_KEY;
11191
11231
  if (!apiKey) return null;
@@ -11228,19 +11268,26 @@ function nodeLlmView(cfg) {
11228
11268
  global: true
11229
11269
  }
11230
11270
  ] : [],
11231
- ...cfg.providers.map((p) => ({
11232
- id: p.id,
11233
- name: p.name,
11234
- type: p.type ?? "",
11235
- baseURL: p.baseURL,
11236
- model: p.model,
11237
- hasApiKey: Boolean(p.apiKey),
11238
- models: p.models ?? [],
11239
- nonChat: p.nonChat ?? [],
11240
- modelMeta: p.modelMeta ?? [],
11241
- reasoningEffort: p.reasoningEffort ?? null,
11242
- global: false
11243
- }))
11271
+ ...cfg.providers.map((p) => {
11272
+ const builtin = isBuiltinProvider(p.id);
11273
+ return {
11274
+ id: p.id,
11275
+ name: p.name,
11276
+ type: p.type ?? "",
11277
+ baseURL: p.baseURL,
11278
+ model: p.model,
11279
+ hasApiKey: Boolean(p.apiKey),
11280
+ models: p.models ?? [],
11281
+ nonChat: p.nonChat ?? [],
11282
+ modelMeta: p.modelMeta ?? [],
11283
+ reasoningEffort: p.reasoningEffort ?? null,
11284
+ global: false,
11285
+ /** 平台内置(接入 Hub 后自动下发,用户不用关心 Key) */
11286
+ builtin,
11287
+ /** 不可编辑的字段:控制台据此把 baseURL / key 显示为只读 */
11288
+ ...builtin ? { locked: ["baseURL", "apiKey"] } : {}
11289
+ };
11290
+ })
11244
11291
  ];
11245
11292
  return {
11246
11293
  defaultProviderId: cfg.defaultProviderId ?? null,
@@ -11253,27 +11300,42 @@ function applyNodeLlmPatch(cfg, raw) {
11253
11300
  const next = { providers: cfg.providers, defaultProviderId: cfg.defaultProviderId };
11254
11301
  if (Array.isArray(raw?.providers)) {
11255
11302
  const old = new Map(cfg.providers.map((p) => [p.id, p]));
11256
- next.providers = raw.providers.filter((x) => Boolean(x) && typeof x === "object").filter((x) => x.global !== true && String(x.id ?? "") !== GLOBAL_PROVIDER_ID).map((x) => {
11257
- const id = String(x.id ?? "").trim();
11258
- if (!id) throw new ApiError(400, "bad_request", "\u4F9B\u5E94\u5546 id \u4E0D\u80FD\u4E3A\u7A7A");
11259
- const prev = old.get(id);
11260
- const apiKey = String(x.apiKey ?? "").trim();
11261
- const models = normalizeModelList(x.models) ?? prev?.models;
11262
- const nonChat = normalizeModelList(x.nonChat) ?? prev?.nonChat;
11263
- const modelMeta = normalizeModelMeta(x.modelMeta) ?? prev?.modelMeta;
11264
- return {
11265
- id,
11266
- name: String(x.name ?? prev?.name ?? id).trim(),
11267
- type: x.type == null ? prev?.type : String(x.type),
11268
- baseURL: String(x.baseURL ?? prev?.baseURL ?? "").trim(),
11269
- model: String(x.model ?? prev?.model ?? "").trim(),
11270
- apiKey: apiKey || prev?.apiKey,
11271
- reasoningEffort: x.reasoningEffort === void 0 ? prev?.reasoningEffort ?? null : normReasoningEffort(x.reasoningEffort),
11272
- ...models ? { models } : {},
11273
- ...nonChat ? { nonChat } : {},
11274
- ...modelMeta ? { modelMeta } : {}
11275
- };
11276
- });
11303
+ const incoming = raw.providers.filter((x) => Boolean(x) && typeof x === "object").filter((x) => x.global !== true && String(x.id ?? "") !== GLOBAL_PROVIDER_ID);
11304
+ const prevBuiltin = cfg.providers.find((p) => isBuiltinProvider(p.id));
11305
+ const builtinPatch = incoming.find((x) => isBuiltinProvider(String(x.id ?? "")));
11306
+ const keptBuiltin = prevBuiltin ? [
11307
+ {
11308
+ ...prevBuiltin,
11309
+ ...builtinPatch?.model != null ? { model: String(builtinPatch.model).trim() || prevBuiltin.model } : {},
11310
+ ...normalizeModelList(builtinPatch?.models) ? { models: normalizeModelList(builtinPatch?.models) } : {},
11311
+ ...builtinPatch?.reasoningEffort !== void 0 ? { reasoningEffort: normReasoningEffort(builtinPatch.reasoningEffort) } : {},
11312
+ ...typeof builtinPatch?.name === "string" && builtinPatch.name.trim() ? { name: builtinPatch.name.trim() } : {}
11313
+ }
11314
+ ] : [];
11315
+ next.providers = [
11316
+ ...keptBuiltin,
11317
+ ...incoming.filter((x) => !isBuiltinProvider(String(x.id ?? ""))).map((x) => {
11318
+ const id = String(x.id ?? "").trim();
11319
+ if (!id) throw new ApiError(400, "bad_request", "\u4F9B\u5E94\u5546 id \u4E0D\u80FD\u4E3A\u7A7A");
11320
+ const prev = old.get(id);
11321
+ const apiKey = String(x.apiKey ?? "").trim();
11322
+ const models = normalizeModelList(x.models) ?? prev?.models;
11323
+ const nonChat = normalizeModelList(x.nonChat) ?? prev?.nonChat;
11324
+ const modelMeta = normalizeModelMeta(x.modelMeta) ?? prev?.modelMeta;
11325
+ return {
11326
+ id,
11327
+ name: String(x.name ?? prev?.name ?? id).trim(),
11328
+ type: x.type == null ? prev?.type : String(x.type),
11329
+ baseURL: String(x.baseURL ?? prev?.baseURL ?? "").trim(),
11330
+ model: String(x.model ?? prev?.model ?? "").trim(),
11331
+ apiKey: apiKey || prev?.apiKey,
11332
+ reasoningEffort: x.reasoningEffort === void 0 ? prev?.reasoningEffort ?? null : normReasoningEffort(x.reasoningEffort),
11333
+ ...models ? { models } : {},
11334
+ ...nonChat ? { nonChat } : {},
11335
+ ...modelMeta ? { modelMeta } : {}
11336
+ };
11337
+ })
11338
+ ];
11277
11339
  }
11278
11340
  if (typeof raw?.defaultProviderId === "string") {
11279
11341
  const id = raw.defaultProviderId.trim();
@@ -11293,6 +11355,9 @@ function nodeDefaultProvider(store) {
11293
11355
  if (cfg.providers.length === 0) return null;
11294
11356
  return cfg.providers.find((p) => p.id === cfg.defaultProviderId) ?? cfg.providers[0] ?? null;
11295
11357
  }
11358
+ function defaultTenantModel(store) {
11359
+ return process.env.OPENAI_MODEL || process.env.MODEL || nodeDefaultProvider(store)?.model || FALLBACK_MODEL;
11360
+ }
11296
11361
  function resolveSessionProvider(store, tenant, providerId) {
11297
11362
  const own = tenant.provider ?? {};
11298
11363
  if (providerId) {
@@ -11322,6 +11387,208 @@ function resolveSessionProvider(store, tenant, providerId) {
11322
11387
  return { ...own, model: tenant.model };
11323
11388
  }
11324
11389
 
11390
+ // src/server/builtinLlm.ts
11391
+ init_cjs_shims();
11392
+ var FETCH_TIMEOUT_MS = 8e3;
11393
+ var RETRY_AFTER_MS = 5 * 6e4;
11394
+ function hubBase(cred) {
11395
+ return (cred.hubUrl ?? "").replace(/\/+$/, "");
11396
+ }
11397
+ function asProvider(grant) {
11398
+ const baseURL = String(grant.baseURL ?? "").trim();
11399
+ const apiKey = String(grant.apiKey ?? "").trim();
11400
+ const model = String(grant.model ?? "").trim();
11401
+ if (!baseURL || !apiKey) return null;
11402
+ return {
11403
+ id: BUILTIN_PROVIDER_ID,
11404
+ name: String(grant.name ?? "").trim() || "\u5FD7\u524D\u5927\u6A21\u578B\uFF08\u5185\u7F6E\uFF09",
11405
+ type: "builtin",
11406
+ baseURL,
11407
+ model,
11408
+ apiKey
11409
+ };
11410
+ }
11411
+ async function enrichModels(p) {
11412
+ try {
11413
+ const r = await Promise.race([
11414
+ fetchModels(p.baseURL, p.apiKey ?? "", p.model),
11415
+ new Promise((resolve25) => {
11416
+ const t = setTimeout(() => resolve25(null), FETCH_TIMEOUT_MS);
11417
+ t.unref?.();
11418
+ })
11419
+ ]);
11420
+ if (!r?.models?.length) return p;
11421
+ return {
11422
+ ...p,
11423
+ // 控制面没给默认型号时,取供应商返回的第一个,避免模型为空让用户卡住
11424
+ model: p.model || r.models[0],
11425
+ models: r.models,
11426
+ ...r.nonChat?.length ? { nonChat: r.nonChat } : {},
11427
+ ...r.modelMeta?.length ? { modelMeta: r.modelMeta } : {}
11428
+ };
11429
+ } catch {
11430
+ return p;
11431
+ }
11432
+ }
11433
+ function saveBuiltin(store, provider) {
11434
+ const cfg = store.loadNodeLlm();
11435
+ const others = cfg.providers.filter((p) => p.id !== BUILTIN_PROVIDER_ID);
11436
+ const prev = cfg.providers.find((p) => p.id === BUILTIN_PROVIDER_ID);
11437
+ const merged = {
11438
+ ...provider,
11439
+ // 用户选过的型号/候选列表/推理强度/显示名都保留;只有托管字段(baseURL/key)强制以控制面为准
11440
+ ...prev?.model ? { model: prev.model } : {},
11441
+ ...prev?.models?.length ? { models: prev.models } : {},
11442
+ ...prev?.reasoningEffort != null ? { reasoningEffort: prev.reasoningEffort } : {},
11443
+ ...prev?.name ? { name: prev.name } : {}
11444
+ };
11445
+ store.saveNodeLlm({
11446
+ providers: [merged, ...others],
11447
+ // 之前没有默认且用户也没指定时,让内置模型成为默认——「开箱即用」
11448
+ defaultProviderId: cfg.defaultProviderId ?? BUILTIN_PROVIDER_ID
11449
+ });
11450
+ adoptBuiltinModelForTenants(store, merged.model);
11451
+ }
11452
+ var LEGACY_DEFAULT_MODELS = /* @__PURE__ */ new Set([FALLBACK_MODEL, "deepseek-v4-flash"]);
11453
+ function adoptBuiltinModelForTenants(store, model) {
11454
+ if (!model) return;
11455
+ for (const id of store.listTenants()) {
11456
+ const tenant = store.loadTenant(id);
11457
+ if (!tenant || !LEGACY_DEFAULT_MODELS.has(tenant.model)) continue;
11458
+ if (tenant.provider?.apiKey) continue;
11459
+ tenant.model = model;
11460
+ store.saveTenant(tenant);
11461
+ }
11462
+ }
11463
+ async function ensureBuiltinProvider(store, opts = {}) {
11464
+ const cred = store.loadFleetCredential();
11465
+ if (!cred?.fleetToken || !cred.fleetSecret) {
11466
+ return { status: "disabled", reason: "\u8282\u70B9\u672A\u63A5\u5165 Hub" };
11467
+ }
11468
+ const hub = hubBase(cred);
11469
+ if (!hub) return { status: "disabled", reason: "\u7F3A\u5C11 Hub \u5730\u5740" };
11470
+ const local = store.loadNodeLlm().providers.find((p) => p.id === BUILTIN_PROVIDER_ID);
11471
+ if (local && !opts.force) {
11472
+ adoptBuiltinModelForTenants(store, local.model);
11473
+ return { status: "ready", provider: local, refreshed: false };
11474
+ }
11475
+ const cached3 = cred.builtinLlm ? asProvider(cred.builtinLlm) : null;
11476
+ let res;
11477
+ try {
11478
+ res = await fetch(`${hub}/api/hub/node/builtin-llm`, {
11479
+ headers: {
11480
+ Authorization: `Bearer ${cred.fleetToken}`,
11481
+ "X-Fleet-Secret": cred.fleetSecret,
11482
+ "X-Node-Id": cred.nodeId
11483
+ },
11484
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
11485
+ });
11486
+ } catch (e) {
11487
+ if (cached3) {
11488
+ const provider2 = await enrichModels(cached3);
11489
+ saveBuiltin(store, provider2);
11490
+ return { status: "ready", provider: provider2, refreshed: true };
11491
+ }
11492
+ return { status: "error", reason: `\u8FDE\u63A5 Hub \u5931\u8D25\uFF1A${e.message}` };
11493
+ }
11494
+ let body = null;
11495
+ try {
11496
+ body = await res.json();
11497
+ } catch {
11498
+ }
11499
+ if (!res.ok) {
11500
+ const msg = body?.error?.message ?? `HTTP ${res.status}`;
11501
+ if (res.status === 401 || res.status === 403) {
11502
+ return { status: "unavailable", reason: msg };
11503
+ }
11504
+ if (cached3) {
11505
+ const provider2 = await enrichModels(cached3);
11506
+ saveBuiltin(store, provider2);
11507
+ return { status: "ready", provider: provider2, refreshed: true };
11508
+ }
11509
+ return { status: "error", reason: msg };
11510
+ }
11511
+ const grant = body?.data?.builtinLlm;
11512
+ if (!grant) {
11513
+ const why = String(body?.data?.reason ?? "").trim();
11514
+ return { status: "unavailable", reason: why || "\u63A7\u5236\u9762\u672A\u4E0B\u53D1\u5185\u7F6E\u6A21\u578B" };
11515
+ }
11516
+ const parsed = asProvider(grant);
11517
+ if (!parsed) return { status: "unavailable", reason: "\u5185\u7F6E\u6A21\u578B\u914D\u7F6E\u4E0D\u5B8C\u6574" };
11518
+ const provider = await enrichModels(parsed);
11519
+ saveBuiltin(store, provider);
11520
+ return { status: "ready", provider, refreshed: true };
11521
+ }
11522
+ async function fetchLlmBalance(store) {
11523
+ const cred = store.loadFleetCredential();
11524
+ const hub = cred ? hubBase(cred) : "";
11525
+ if (!cred?.fleetToken || !cred.fleetSecret || !hub) {
11526
+ return { available: null, currency: "CNY", reason: "\u8282\u70B9\u672A\u63A5\u5165 Hub" };
11527
+ }
11528
+ try {
11529
+ const res = await fetch(`${hub}/api/hub/node/balance`, {
11530
+ headers: {
11531
+ Authorization: `Bearer ${cred.fleetToken}`,
11532
+ "X-Fleet-Secret": cred.fleetSecret,
11533
+ "X-Node-Id": cred.nodeId
11534
+ },
11535
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
11536
+ });
11537
+ const body = await res.json().catch(() => null);
11538
+ if (!res.ok) {
11539
+ return {
11540
+ available: null,
11541
+ currency: "CNY",
11542
+ reason: body?.data ? "" : `HTTP ${res.status}`
11543
+ };
11544
+ }
11545
+ const d = body?.data ?? {};
11546
+ const available = typeof d.available === "number" ? d.available : null;
11547
+ return {
11548
+ available,
11549
+ currency: typeof d.currency === "string" && d.currency ? d.currency : "CNY",
11550
+ ...typeof d.reserved === "number" ? { reserved: d.reserved } : {},
11551
+ // Hub 没配 HUB_PUBLIC_URL 时退回用接入时的 hubUrl
11552
+ topupUrl: typeof d.topupUrl === "string" && d.topupUrl ? d.topupUrl : `${hub}/topup`,
11553
+ ...available === null && typeof d.reason === "string" ? { reason: d.reason } : {}
11554
+ };
11555
+ } catch (e) {
11556
+ return {
11557
+ available: null,
11558
+ currency: "CNY",
11559
+ reason: `\u8FDE\u63A5 Hub \u5931\u8D25\uFF1A${e.message}`
11560
+ };
11561
+ }
11562
+ }
11563
+ var AUTH_RETRY_THROTTLE_MS = 6e4;
11564
+ var lastAuthRetryAt = 0;
11565
+ function healBuiltinOnAuthFailure(store, message) {
11566
+ if (!/401|unauthorized|invalid[_\s]*api[_\s]*key|api[\s_-]*key\s*(?:无效|invalid)/i.test(message)) return;
11567
+ if (!store.loadFleetCredential()) return;
11568
+ if (!store.loadNodeLlm().providers.some((p) => p.id === BUILTIN_PROVIDER_ID)) return;
11569
+ if (Date.now() - lastAuthRetryAt < AUTH_RETRY_THROTTLE_MS) return;
11570
+ lastAuthRetryAt = Date.now();
11571
+ void ensureBuiltinProvider(store, { force: true }).catch(() => {
11572
+ });
11573
+ }
11574
+ var lastFailureAt = 0;
11575
+ function scheduleBuiltinProvider(store, log = () => {
11576
+ }) {
11577
+ if (Date.now() - lastFailureAt < RETRY_AFTER_MS) return;
11578
+ void ensureBuiltinProvider(store).then((r) => {
11579
+ if (r.status === "ready") {
11580
+ if (r.refreshed) log(`[builtin-llm] \u5DF2\u4E0B\u53D1\u5185\u7F6E\u6A21\u578B\uFF1A${r.provider.name}\uFF08${r.provider.model}\uFF09`);
11581
+ return;
11582
+ }
11583
+ if (r.status === "disabled") return;
11584
+ lastFailureAt = Date.now();
11585
+ if (r.status === "unavailable") log(`[builtin-llm] \u6682\u65E0\u5185\u7F6E\u6A21\u578B\uFF1A${r.reason}`);
11586
+ else log(`[builtin-llm] \u62C9\u53D6\u5185\u7F6E\u6A21\u578B\u5931\u8D25\uFF1A${r.reason}`);
11587
+ }).catch(() => {
11588
+ lastFailureAt = Date.now();
11589
+ });
11590
+ }
11591
+
11325
11592
  // src/server/usage.ts
11326
11593
  init_cjs_shims();
11327
11594
  var fs19 = __toESM(require("fs"), 1);
@@ -13383,6 +13650,7 @@ data: ${JSON.stringify(entry.data)}
13383
13650
  this._setStatus(rt, "error");
13384
13651
  this._persist(rt);
13385
13652
  this._emitNotify(rt, "error", this._turnEndData(rt, event.data, {}));
13653
+ healBuiltinOnAuthFailure(this.store, String(event.data["error"] ?? ""));
13386
13654
  break;
13387
13655
  case "interrupted" /* INTERRUPTED */:
13388
13656
  this._setStatus(rt, "interrupted");
@@ -13806,7 +14074,6 @@ var ApiError = class extends Error {
13806
14074
  };
13807
14075
 
13808
14076
  // src/server/projects.ts
13809
- var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
13810
14077
  function toProjectSummary(store, tenant) {
13811
14078
  return {
13812
14079
  id: tenant.id,
@@ -13946,7 +14213,7 @@ function createProject(store, input) {
13946
14213
  id,
13947
14214
  name: explicitName || defaultName,
13948
14215
  apiKey: generateApiKey(),
13949
- model: typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : DEFAULT_MODEL(),
14216
+ model: typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : defaultTenantModel(store),
13950
14217
  provider: {
13951
14218
  apiKey: typeof raw.providerApiKey === "string" && raw.providerApiKey.trim() ? raw.providerApiKey.trim() : void 0,
13952
14219
  baseURL: typeof raw.baseURL === "string" && raw.baseURL.trim() ? raw.baseURL.trim() : void 0
@@ -13971,7 +14238,7 @@ function buildNodeInfo(store, opts) {
13971
14238
  nodeId: opts.nodeId ?? ensureNodeId(store),
13972
14239
  platform: process.platform,
13973
14240
  mode: process.env.NODE_MODE ?? "standalone",
13974
- version: "0.2.16",
14241
+ version: "0.2.20",
13975
14242
  admin: opts.admin,
13976
14243
  projects: opts.projects
13977
14244
  };
@@ -14187,7 +14454,7 @@ var path25 = __toESM(require("path"), 1);
14187
14454
  var import_node_child_process4 = require("child_process");
14188
14455
  var import_node_module3 = require("module");
14189
14456
  function currentVersion() {
14190
- const injected = "0.2.16"?.trim();
14457
+ const injected = "0.2.20"?.trim();
14191
14458
  if (injected) return injected;
14192
14459
  try {
14193
14460
  const req2 = (0, import_node_module3.createRequire)(importMetaUrl);
@@ -15523,10 +15790,133 @@ function makeDirIn(parentInput, name) {
15523
15790
  init_cjs_shims();
15524
15791
  var fs23 = __toESM(require("fs"), 1);
15525
15792
  var path30 = __toESM(require("path"), 1);
15793
+
15794
+ // src/server/zipArchive.ts
15795
+ init_cjs_shims();
15796
+ var zlib = __toESM(require("zlib"), 1);
15797
+ var MAX_ZIP_ENTRIES = 2e4;
15798
+ var LOCAL_SIG = 67324752;
15799
+ var CENTRAL_SIG = 33639248;
15800
+ var EOCD_SIG = 101010256;
15801
+ var FLAG_UTF8 = 2048;
15802
+ var METHOD_STORE = 0;
15803
+ var METHOD_DEFLATE = 8;
15804
+ var CRC_TABLE = (() => {
15805
+ const table = new Int32Array(256);
15806
+ for (let n = 0; n < 256; n++) {
15807
+ let c = n;
15808
+ for (let k = 0; k < 8; k++) {
15809
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
15810
+ }
15811
+ table[n] = c;
15812
+ }
15813
+ return table;
15814
+ })();
15815
+ function crc32(buf) {
15816
+ let c = -1;
15817
+ for (let i = 0; i < buf.length; i++) {
15818
+ c = CRC_TABLE[(c ^ buf[i]) & 255] ^ c >>> 8;
15819
+ }
15820
+ return (c ^ -1) >>> 0;
15821
+ }
15822
+ function dosDateTime(d) {
15823
+ const year = Math.max(1980, d.getFullYear());
15824
+ return {
15825
+ time: d.getHours() << 11 | d.getMinutes() << 5 | d.getSeconds() >> 1,
15826
+ date: year - 1980 << 9 | d.getMonth() + 1 << 5 | d.getDate()
15827
+ };
15828
+ }
15829
+ function buildZip(entries) {
15830
+ if (entries.length > MAX_ZIP_ENTRIES) {
15831
+ throw new Error(`\u5F52\u6863\u6761\u76EE\u8FC7\u591A\uFF08${entries.length} > ${MAX_ZIP_ENTRIES}\uFF09`);
15832
+ }
15833
+ const chunks = [];
15834
+ const central = [];
15835
+ let offset = 0;
15836
+ for (const e of entries) {
15837
+ const name = Buffer.from(e.name, "utf-8");
15838
+ const isDir = e.name.endsWith("/");
15839
+ const raw = isDir ? Buffer.alloc(0) : e.data ?? Buffer.alloc(0);
15840
+ let method = METHOD_STORE;
15841
+ let body = raw;
15842
+ if (!isDir && raw.length > 0) {
15843
+ const deflated = zlib.deflateRawSync(raw, { level: 6 });
15844
+ if (deflated.length < raw.length) {
15845
+ method = METHOD_DEFLATE;
15846
+ body = deflated;
15847
+ }
15848
+ }
15849
+ const crc = isDir ? 0 : crc32(raw);
15850
+ const { time, date } = dosDateTime(e.mtime ?? /* @__PURE__ */ new Date());
15851
+ const local = Buffer.alloc(30);
15852
+ local.writeUInt32LE(LOCAL_SIG, 0);
15853
+ local.writeUInt16LE(20, 4);
15854
+ local.writeUInt16LE(FLAG_UTF8, 6);
15855
+ local.writeUInt16LE(method, 8);
15856
+ local.writeUInt16LE(time, 10);
15857
+ local.writeUInt16LE(date, 12);
15858
+ local.writeUInt32LE(crc, 14);
15859
+ local.writeUInt32LE(body.length, 18);
15860
+ local.writeUInt32LE(raw.length, 22);
15861
+ local.writeUInt16LE(name.length, 26);
15862
+ local.writeUInt16LE(0, 28);
15863
+ chunks.push(local, name);
15864
+ if (body.length > 0) chunks.push(body);
15865
+ central.push({
15866
+ name,
15867
+ method,
15868
+ crc,
15869
+ csize: body.length,
15870
+ usize: raw.length,
15871
+ offset,
15872
+ time,
15873
+ date,
15874
+ isDir
15875
+ });
15876
+ offset += local.length + name.length + body.length;
15877
+ }
15878
+ const cdStart = offset;
15879
+ for (const c of central) {
15880
+ const head = Buffer.alloc(46);
15881
+ head.writeUInt32LE(CENTRAL_SIG, 0);
15882
+ head.writeUInt16LE(20, 4);
15883
+ head.writeUInt16LE(20, 6);
15884
+ head.writeUInt16LE(FLAG_UTF8, 8);
15885
+ head.writeUInt16LE(c.method, 10);
15886
+ head.writeUInt16LE(c.time, 12);
15887
+ head.writeUInt16LE(c.date, 14);
15888
+ head.writeUInt32LE(c.crc, 16);
15889
+ head.writeUInt32LE(c.csize, 20);
15890
+ head.writeUInt32LE(c.usize, 24);
15891
+ head.writeUInt16LE(c.name.length, 28);
15892
+ head.writeUInt16LE(0, 30);
15893
+ head.writeUInt16LE(0, 32);
15894
+ head.writeUInt16LE(0, 34);
15895
+ head.writeUInt16LE(0, 36);
15896
+ head.writeUInt32LE(c.isDir ? 16 : 0, 38);
15897
+ head.writeUInt32LE(c.offset, 42);
15898
+ chunks.push(head, c.name);
15899
+ offset += head.length + c.name.length;
15900
+ }
15901
+ const eocd = Buffer.alloc(22);
15902
+ eocd.writeUInt32LE(EOCD_SIG, 0);
15903
+ eocd.writeUInt16LE(0, 4);
15904
+ eocd.writeUInt16LE(0, 6);
15905
+ eocd.writeUInt16LE(central.length, 8);
15906
+ eocd.writeUInt16LE(central.length, 10);
15907
+ eocd.writeUInt32LE(offset - cdStart, 12);
15908
+ eocd.writeUInt32LE(cdStart, 16);
15909
+ eocd.writeUInt16LE(0, 20);
15910
+ chunks.push(eocd);
15911
+ return Buffer.concat(chunks);
15912
+ }
15913
+
15914
+ // src/server/projectFiles.ts
15526
15915
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
15527
15916
  var MAX_DOWNLOAD_BYTES = 16 * 1024 * 1024;
15528
15917
  var MAX_BINARY_BYTES = 2.5 * 1024 * 1024;
15529
15918
  var MAX_ENTRIES2 = 2e3;
15919
+ var MAX_ZIP_BYTES = 100 * 1024 * 1024;
15530
15920
  function toPosix(p) {
15531
15921
  return p.split(path30.sep).join("/");
15532
15922
  }
@@ -15802,6 +16192,134 @@ function deleteProjectPaths(workspaceRoot, relPaths) {
15802
16192
  }
15803
16193
  return out;
15804
16194
  }
16195
+ function formatBytes(n) {
16196
+ if (n >= 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
16197
+ if (n >= 1024) return `${(n / 1024).toFixed(1)} KB`;
16198
+ return `${n} B`;
16199
+ }
16200
+ function renameProjectPath(workspaceRoot, relPath, newName) {
16201
+ const base = projectRoot(workspaceRoot);
16202
+ const rel = (relPath ?? "").trim().replace(/^[/\\]+/, "");
16203
+ if (!rel) throw new Error("\u4E0D\u80FD\u91CD\u547D\u540D\u9879\u76EE\u6839\u76EE\u5F55");
16204
+ const abs = resolveWithin(base, rel);
16205
+ const name = (newName ?? "").trim();
16206
+ if (!name) throw new Error("\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
16207
+ if (name === "." || name === "..") throw new Error("\u540D\u79F0\u975E\u6CD5");
16208
+ if (/[/\\]/.test(name)) throw new Error("\u540D\u79F0\u4E0D\u80FD\u5305\u542B\u8DEF\u5F84\u5206\u9694\u7B26");
16209
+ if (!fs23.existsSync(abs)) throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
16210
+ const parent = path30.dirname(abs);
16211
+ const target = path30.join(parent, name);
16212
+ if (!isWithinRoot(base, target)) throw new Error("\u8DEF\u5F84\u8D8A\u754C\uFF08\u8D85\u51FA\u9879\u76EE\u5DE5\u4F5C\u533A\uFF09");
16213
+ if (fs23.existsSync(target)) {
16214
+ let same = false;
16215
+ try {
16216
+ const a = fs23.statSync(target);
16217
+ const b = fs23.statSync(abs);
16218
+ same = a.ino === b.ino && a.dev === b.dev;
16219
+ } catch {
16220
+ same = false;
16221
+ }
16222
+ if (!same) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u6587\u4EF6\u6216\u76EE\u5F55: ${name}`);
16223
+ }
16224
+ if (path30.resolve(target) === path30.resolve(abs)) {
16225
+ return { from: toPosix(path30.relative(base, abs)), path: toPosix(path30.relative(base, abs)) };
16226
+ }
16227
+ try {
16228
+ fs23.renameSync(abs, target);
16229
+ } catch (e) {
16230
+ throw new Error(`\u91CD\u547D\u540D\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
16231
+ }
16232
+ return { from: toPosix(path30.relative(base, abs)), path: toPosix(path30.relative(base, target)) };
16233
+ }
16234
+ function zipProjectPaths(workspaceRoot, relPaths, opts = {}) {
16235
+ const base = projectRoot(workspaceRoot);
16236
+ const maxBytes = opts.maxBytes && opts.maxBytes > 0 ? opts.maxBytes : MAX_ZIP_BYTES;
16237
+ const rels = (relPaths ?? []).map((x) => (x ?? "").trim().replace(/^[/\\]+/, ""));
16238
+ if (rels.length === 0) throw new Error("\u8BF7\u9009\u62E9\u8981\u538B\u7F29\u7684\u6587\u4EF6\u6216\u76EE\u5F55");
16239
+ if (rels.some((r) => !r)) throw new Error("\u4E0D\u80FD\u538B\u7F29\u9879\u76EE\u6839\u76EE\u5F55");
16240
+ const targets = rels.map((rel) => ({ rel, abs: resolveWithin(base, rel) }));
16241
+ const single = targets.length === 1 ? targets[0].abs : "";
16242
+ const items = [];
16243
+ let total = 0;
16244
+ const overLimit = () => new Error(
16245
+ `\u5171 ${formatBytes(total)}\uFF0C\u8D85\u8FC7 ${formatBytes(maxBytes)} \u4E0A\u9650\uFF0C\u4E0D\u652F\u6301\u538B\u7F29\u4E0B\u8F7D`
16246
+ );
16247
+ const walk = (absDir) => {
16248
+ let dirents;
16249
+ try {
16250
+ dirents = fs23.readdirSync(absDir, { withFileTypes: true });
16251
+ } catch {
16252
+ return 0;
16253
+ }
16254
+ dirents.sort((a, b) => a.name.localeCompare(b.name));
16255
+ let files = 0;
16256
+ for (const d of dirents) {
16257
+ if (d.isSymbolicLink()) continue;
16258
+ const child = path30.join(absDir, d.name);
16259
+ if (d.isDirectory()) {
16260
+ files += walk(child);
16261
+ continue;
16262
+ }
16263
+ if (!d.isFile()) continue;
16264
+ let st;
16265
+ try {
16266
+ st = fs23.statSync(child);
16267
+ } catch {
16268
+ continue;
16269
+ }
16270
+ total += st.size;
16271
+ if (total > maxBytes) throw overLimit();
16272
+ if (items.length >= MAX_ZIP_ENTRIES) {
16273
+ 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`);
16274
+ }
16275
+ items.push({ abs: child, dir: false, size: st.size, mtime: st.mtime });
16276
+ files++;
16277
+ }
16278
+ if (files === 0) {
16279
+ let mtime = /* @__PURE__ */ new Date();
16280
+ try {
16281
+ mtime = fs23.statSync(absDir).mtime;
16282
+ } catch {
16283
+ }
16284
+ items.push({ abs: absDir, dir: true, size: 0, mtime });
16285
+ }
16286
+ return files;
16287
+ };
16288
+ for (const t of targets) {
16289
+ let st;
16290
+ try {
16291
+ st = fs23.statSync(t.abs);
16292
+ } catch {
16293
+ throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${t.rel}`);
16294
+ }
16295
+ if (st.isDirectory()) {
16296
+ walk(t.abs);
16297
+ continue;
16298
+ }
16299
+ total += st.size;
16300
+ if (total > maxBytes) throw overLimit();
16301
+ items.push({ abs: t.abs, dir: false, size: st.size, mtime: st.mtime });
16302
+ }
16303
+ const nameOf = (abs) => {
16304
+ const rel = toPosix(path30.relative(single || base, abs));
16305
+ return rel || path30.posix.basename(toPosix(abs));
16306
+ };
16307
+ const zipEntries = items.map((it) => {
16308
+ const name2 = nameOf(it.abs);
16309
+ if (it.dir) return { name: `${name2}/`, mtime: it.mtime };
16310
+ return { name: name2, data: fs23.readFileSync(it.abs), mtime: it.mtime };
16311
+ });
16312
+ const buf = buildZip(zipEntries);
16313
+ const first = targets[0].rel;
16314
+ const name = single ? `${path30.posix.basename(toPosix(first))}.zip` : "archive.zip";
16315
+ return {
16316
+ name,
16317
+ size: buf.length,
16318
+ total,
16319
+ entries: zipEntries.length,
16320
+ base64: buf.toString("base64")
16321
+ };
16322
+ }
15805
16323
 
15806
16324
  // src/server/projectOps.ts
15807
16325
  init_cjs_shims();
@@ -16131,6 +16649,7 @@ function projectStats(manager, tenantId) {
16131
16649
 
16132
16650
  // src/server/nodeHost.ts
16133
16651
  init_cjs_shims();
16652
+ var import_child_process = require("child_process");
16134
16653
  var fs24 = __toESM(require("fs"), 1);
16135
16654
  var os9 = __toESM(require("os"), 1);
16136
16655
  function diskInfo(pathStr) {
@@ -16145,6 +16664,64 @@ function diskInfo(pathStr) {
16145
16664
  return null;
16146
16665
  }
16147
16666
  }
16667
+ 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)$/;
16668
+ function isInternalMount(mount) {
16669
+ if (mount === "/") return false;
16670
+ const prefixes = ["/proc", "/sys", "/run", "/snap"];
16671
+ if (process.platform === "darwin") prefixes.push("/System/Volumes");
16672
+ return prefixes.some((p) => mount === p || mount.startsWith(`${p}/`));
16673
+ }
16674
+ function readOnlyVolumes() {
16675
+ const out = /* @__PURE__ */ new Set();
16676
+ const res = (0, import_child_process.spawnSync)("mount", [], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
16677
+ if (res.error || res.status !== 0 || !res.stdout) return out;
16678
+ for (const line of res.stdout.split("\n")) {
16679
+ const m = line.match(/^.+? on (.+?) \(([^)]*)\)$/);
16680
+ if (!m || m[1] === "/") continue;
16681
+ if (m[2].split(",").some((opt) => opt.trim() === "read-only")) out.add(m[1]);
16682
+ }
16683
+ return out;
16684
+ }
16685
+ function diskList(workspacePath) {
16686
+ const out = [];
16687
+ const seen = /* @__PURE__ */ new Set();
16688
+ const push = (path44, total, free) => {
16689
+ if (!path44 || seen.has(path44) || !Number.isFinite(total) || total <= 0) return;
16690
+ seen.add(path44);
16691
+ out.push({ path: path44, total, free });
16692
+ };
16693
+ if (process.platform === "win32") {
16694
+ for (const letter of "CDEFGHIJKLMNOPQRSTUVWXYZ") {
16695
+ const root = `${letter}:\\`;
16696
+ const d = diskInfo(root);
16697
+ if (d) push(root, d.total, d.free);
16698
+ }
16699
+ } else {
16700
+ const readonly = process.platform === "darwin" ? readOnlyVolumes() : /* @__PURE__ */ new Set();
16701
+ const res = (0, import_child_process.spawnSync)("df", ["-kP"], {
16702
+ encoding: "utf-8",
16703
+ stdio: ["ignore", "pipe", "ignore"]
16704
+ });
16705
+ if (!res.error && res.status === 0 && res.stdout) {
16706
+ for (const line of res.stdout.split("\n").slice(1)) {
16707
+ const m = line.trim().match(/^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s+(.+)$/);
16708
+ if (!m || PSEUDO_FS.test(m[1])) continue;
16709
+ if (/^\/dev\/loop\d+$/.test(m[1])) continue;
16710
+ if (isInternalMount(m[5]) || readonly.has(m[5])) continue;
16711
+ push(m[5], Number(m[2]) * 1024, Number(m[4]) * 1024);
16712
+ }
16713
+ }
16714
+ }
16715
+ const primary = diskInfo(workspacePath);
16716
+ if (primary) {
16717
+ const covered = out.some((d) => {
16718
+ const root = d.path.endsWith("/") || d.path.endsWith("\\") ? d.path : `${d.path}/`;
16719
+ return workspacePath === d.path || workspacePath.startsWith(root);
16720
+ });
16721
+ if (!covered) push(primary.path, primary.total, primary.free);
16722
+ }
16723
+ return out.sort((a, b) => b.total - a.total);
16724
+ }
16148
16725
  function networkInfo() {
16149
16726
  const out = [];
16150
16727
  const ifaces = os9.networkInterfaces();
@@ -16200,6 +16777,7 @@ function overview(store, manager) {
16200
16777
  function buildNodeHost(store, manager, diskPath) {
16201
16778
  const cpus2 = os9.cpus();
16202
16779
  const ov = overview(store, manager);
16780
+ const disks = diskList(diskPath || process.cwd());
16203
16781
  return {
16204
16782
  nodeId: store.loadNodeId() ?? "",
16205
16783
  hostname: os9.hostname(),
@@ -16207,7 +16785,7 @@ function buildNodeHost(store, manager, diskPath) {
16207
16785
  arch: process.arch,
16208
16786
  release: os9.release(),
16209
16787
  uptimeSec: Math.round(os9.uptime()),
16210
- version: "0.2.16",
16788
+ version: "0.2.20",
16211
16789
  mode: process.env.NODE_MODE ?? "standalone",
16212
16790
  cpu: {
16213
16791
  count: cpus2.length,
@@ -16217,6 +16795,7 @@ function buildNodeHost(store, manager, diskPath) {
16217
16795
  memory: { total: os9.totalmem(), free: os9.freemem() },
16218
16796
  network: networkInfo(),
16219
16797
  disk: diskInfo(diskPath || process.cwd()),
16798
+ disks,
16220
16799
  ...ov
16221
16800
  };
16222
16801
  }
@@ -17442,6 +18021,41 @@ function createApp(store, manager, opts = {}) {
17442
18021
  }
17443
18022
  }
17444
18023
  },
18024
+ {
18025
+ method: "POST",
18026
+ pattern: ["files", "rename"],
18027
+ // 重命名(只改同目录下的名字)
18028
+ fn: (_req, res, { tenant, body }) => {
18029
+ const raw = body ?? {};
18030
+ try {
18031
+ ok(
18032
+ res,
18033
+ renameProjectPath(
18034
+ tenant.workspaceRoot ?? null,
18035
+ String(raw.path ?? ""),
18036
+ String(raw.name ?? "")
18037
+ )
18038
+ );
18039
+ } catch (e) {
18040
+ throw new ApiError(400, "bad_request", e instanceof Error ? e.message : String(e));
18041
+ }
18042
+ }
18043
+ },
18044
+ {
18045
+ method: "POST",
18046
+ pattern: ["files", "zip"],
18047
+ // 压缩下载(多选一起打包;超上限由实现拒绝)
18048
+ fn: (_req, res, { tenant, body }) => {
18049
+ const raw = body ?? {};
18050
+ const paths = Array.isArray(raw.paths) ? raw.paths.map((x) => String(x)) : [];
18051
+ if (paths.length === 0) throw new ApiError(400, "bad_request", "paths \u4E0D\u80FD\u4E3A\u7A7A");
18052
+ try {
18053
+ ok(res, zipProjectPaths(tenant.workspaceRoot ?? null, paths));
18054
+ } catch (e) {
18055
+ throw new ApiError(400, "bad_request", e instanceof Error ? e.message : String(e));
18056
+ }
18057
+ }
18058
+ },
17445
18059
  // ---------- 项目(= 租户)管理:控制台多项目视图 ----------
17446
18060
  // 仅管理员密钥可用;项目级与会话级接口仍用项目自己的 tenant API Key
17447
18061
  // (或管理员密钥 + X-Project-Id 头),互不暴露明文密钥。
@@ -17500,6 +18114,29 @@ function createApp(store, manager, opts = {}) {
17500
18114
  ok(res, nodeLlmView(next));
17501
18115
  }
17502
18116
  },
18117
+ // 大模型账户余额(控制台「余额不足 → 查看余额并充值」用;由节点向 Hub 代查)
18118
+ {
18119
+ method: "GET",
18120
+ pattern: ["node", "llm", "balance"],
18121
+ adminOnly: true,
18122
+ fn: async (_req, res) => {
18123
+ ok(res, await fetchLlmBalance(store));
18124
+ }
18125
+ },
18126
+ // 重新拉取平台内置模型凭据(用户点了「刷新内置模型」,或 Key 在控制台被重置)
18127
+ {
18128
+ method: "POST",
18129
+ pattern: ["node", "llm", "builtin-refresh"],
18130
+ adminOnly: true,
18131
+ fn: async (_req, res) => {
18132
+ const r = await ensureBuiltinProvider(store, { force: true });
18133
+ if (r.status !== "ready") {
18134
+ ok(res, { ok: false, reason: r.reason });
18135
+ return;
18136
+ }
18137
+ ok(res, { ok: true, view: nodeLlmView(store.loadNodeLlm()) });
18138
+ }
18139
+ },
17503
18140
  // ---------- 用量(账本 + 汇总 + 价格) ----------
17504
18141
  {
17505
18142
  method: "GET",
@@ -17926,7 +18563,7 @@ function createApp(store, manager, opts = {}) {
17926
18563
  json(res, 200, {
17927
18564
  status: "ok",
17928
18565
  // 版本让运维/探针能确认线上跑的是哪个构建(更新与回滚据此判断)
17929
- version: "0.2.16",
18566
+ version: "0.2.20",
17930
18567
  features: { mcp: hasServerMcpConfig() }
17931
18568
  });
17932
18569
  return;
@@ -18266,6 +18903,22 @@ async function dispatchFleetCommand(deps, env3) {
18266
18903
  store.saveNodeLlm(next);
18267
18904
  return ok2(nodeLlmView(next));
18268
18905
  }
18906
+ /*
18907
+ * 重新拉取平台内置模型(志前 API)。
18908
+ *
18909
+ * 用途:用户在控制台点了「刷新」、或内置模型的 Key 在控制台被重置后需要换新。
18910
+ * 走写链串行——它会改写 connections.json,不能与 update_node_llm 并发。
18911
+ */
18912
+ // 余额查询:只读、幂等,可进只读并发池
18913
+ case "get_llm_balance":
18914
+ return ok2(await fetchLlmBalance(store));
18915
+ case "refresh_builtin_llm": {
18916
+ const r = await ensureBuiltinProvider(store, { force: true });
18917
+ if (r.status !== "ready") {
18918
+ return ok2({ ok: false, reason: r.reason });
18919
+ }
18920
+ return ok2({ ok: true, view: nodeLlmView(store.loadNodeLlm()) });
18921
+ }
18269
18922
  case "get_usage_summary": {
18270
18923
  const ledger = manager.usageLedger();
18271
18924
  if (!ledger) return ok2({ total: null, groups: [] });
@@ -18450,6 +19103,24 @@ async function dispatchFleetCommand(deps, env3) {
18450
19103
  const t = project();
18451
19104
  return ok2(searchProjectFiles(t.workspaceRoot ?? null, String(payload.query ?? "")));
18452
19105
  }
19106
+ case "rename_path": {
19107
+ const t = project();
19108
+ return ok2(
19109
+ renameProjectPath(
19110
+ t.workspaceRoot ?? null,
19111
+ String(payload.path ?? ""),
19112
+ String(payload.name ?? "")
19113
+ )
19114
+ );
19115
+ }
19116
+ case "zip_download": {
19117
+ const t = project();
19118
+ const raw = payload.paths;
19119
+ const paths = Array.isArray(raw) ? raw.map((x) => String(x)) : [];
19120
+ if (paths.length === 0) throw new ApiError(400, "bad_request", "paths \u4E0D\u80FD\u4E3A\u7A7A");
19121
+ const maxBytes = typeof payload.maxBytes === "number" ? payload.maxBytes : void 0;
19122
+ return ok2(zipProjectPaths(t.workspaceRoot ?? null, paths, { maxBytes }));
19123
+ }
18453
19124
  case "list_tools": {
18454
19125
  const t = project();
18455
19126
  return ok2(buildToolCatalog(t.workspaceRoot ?? process.cwd()));
@@ -18802,6 +19473,22 @@ function toWireAck(ack) {
18802
19473
  data: ack.data === void 0 ? void 0 : utf8Encode(JSON.stringify(ack.data))
18803
19474
  };
18804
19475
  }
19476
+ var ACK_JSON_MAX_BYTES = MAX_FRAME_BYTES - 64 * 1024;
19477
+ function mb(bytes) {
19478
+ return (bytes / (1024 * 1024)).toFixed(1);
19479
+ }
19480
+ function guardAckSize(ack, json2) {
19481
+ if (!ack.ok || ack.data === void 0) return ack;
19482
+ const bytes = Buffer.byteLength(json2, "utf8");
19483
+ if (bytes <= ACK_JSON_MAX_BYTES) return ack;
19484
+ return {
19485
+ reqId: ack.reqId,
19486
+ type: ack.type,
19487
+ ok: false,
19488
+ // 带上实际大小:用户据此能判断「差一点」还是「差很多」,而不是只看到「失败」
19489
+ 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`
19490
+ };
19491
+ }
18805
19492
  function fromWireCommand(env3) {
18806
19493
  let payload = {};
18807
19494
  if (env3.payload && env3.payload.length > 0) {
@@ -19207,6 +19894,7 @@ var READONLY_CMD_TYPES = /* @__PURE__ */ new Set([
19207
19894
  "get_node_llm",
19208
19895
  "list_node_llm_models",
19209
19896
  "get_usage_summary",
19897
+ "get_llm_balance",
19210
19898
  "get_usage_records",
19211
19899
  "get_usage_pricing",
19212
19900
  "list_projects",
@@ -19631,7 +20319,7 @@ var FleetNodeBridge = class {
19631
20319
  label: this.opts.deps.store.loadNodeLabel() ?? void 0,
19632
20320
  platform: process.platform,
19633
20321
  mode: process.env.NODE_MODE ?? "standalone",
19634
- version: "0.2.16",
20322
+ version: "0.2.20",
19635
20323
  ip: primaryIp()
19636
20324
  };
19637
20325
  }
@@ -19729,7 +20417,7 @@ var FleetNodeBridge = class {
19729
20417
  label: this.opts.deps.store.loadNodeLabel() ?? void 0,
19730
20418
  platform: process.platform,
19731
20419
  mode: process.env.NODE_MODE ?? "standalone",
19732
- version: "0.2.16",
20420
+ version: "0.2.20",
19733
20421
  ip: primaryIp(),
19734
20422
  projects,
19735
20423
  totals,
@@ -19891,7 +20579,13 @@ var FleetNodeBridge = class {
19891
20579
  const stamped = { ...ack, reqId: env3.reqId, type: env3.type };
19892
20580
  const json2 = JSON.stringify(stamped);
19893
20581
  const __t1 = dbgNow();
19894
- this._send({ kind: "ack", clientId: env3.clientId, ack: stamped });
20582
+ const payload = guardAckSize(stamped, json2);
20583
+ if (payload !== stamped) {
20584
+ console.warn(
20585
+ `[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`
20586
+ );
20587
+ }
20588
+ this._send({ kind: "ack", clientId: env3.clientId, ack: payload });
19895
20589
  dbgMs(
19896
20590
  "publishAck",
19897
20591
  __t0,
@@ -21935,7 +22629,9 @@ function seedDemoTenant(store) {
21935
22629
  // 未显式指定时随机生成:固定默认密钥一旦随 HOST=0.0.0.0 暴露到公网,等于公开凭据。
21936
22630
  // 只在首次创建时生成并落盘,后续启动沿用磁盘上的值(启动日志会打印完整密钥)。
21937
22631
  apiKey: env2("TENANT_API_KEY") ?? generateApiKey(),
21938
- model: env2("MODEL") ?? env2("OPENAI_MODEL") ?? "deepseek-v4-flash",
22632
+ // 不再硬编码具体厂商型号:志前 API 只认 deepseek-flash,写死 deepseek-v4-flash
22633
+ // 会让新装节点的第一次请求 404。默认值统一走 defaultTenantModel(见 nodeLlm.ts)。
22634
+ model: defaultTenantModel(store),
21939
22635
  provider: {
21940
22636
  apiKey: env2("OPENAI_API_KEY"),
21941
22637
  baseURL: env2("OPENAI_BASE_URL")
@@ -22075,6 +22771,8 @@ function startServer(opts = {}) {
22075
22771
  const store = new Store(dataDir);
22076
22772
  const demo = seedDemoTenant(store);
22077
22773
  syncTenantModels(store);
22774
+ const nodeModel = nodeDefaultProvider(store)?.model;
22775
+ if (nodeModel) adoptBuiltinModelForTenants(store, nodeModel);
22078
22776
  const usageLedger = new UsageLedger(dataDir);
22079
22777
  usageLedger.init();
22080
22778
  const manager = new SessionManager(store, void 0, { usage: usageLedger });
@@ -22218,8 +22916,12 @@ function startServer(opts = {}) {
22218
22916
  if (initialCred) {
22219
22917
  connectFleet(initialCred);
22220
22918
  } else if (opts.autoEnroll) {
22221
- 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}`));
22919
+ void fleetController.enroll(opts.autoEnroll).then((st) => {
22920
+ console.log(`[fleet] \u5DF2\u81EA\u52A8\u63A5\u5165\uFF0C\u8282\u70B9 ${st.nodeId}\uFF08${st.gatewayUrl}\uFF09`);
22921
+ scheduleBuiltinProvider(store, (m) => console.log(m));
22922
+ }).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}`));
22222
22923
  }
22924
+ if (initialCred) scheduleBuiltinProvider(store, (m) => console.log(m));
22223
22925
  const pairingCodes = new PairingCodeStore();
22224
22926
  const qq = new QqManager({
22225
22927
  store,
@@ -22794,7 +23496,7 @@ ${envDump()}`);
22794
23496
  }
22795
23497
 
22796
23498
  // src/cli/index.ts
22797
- var execAsync2 = (0, import_util.promisify)(import_child_process.exec);
23499
+ var execAsync2 = (0, import_util.promisify)(import_child_process2.exec);
22798
23500
  if (enableDebug(process.argv)) {
22799
23501
  logEnvironment();
22800
23502
  }
@@ -23273,7 +23975,10 @@ async function connectWizard() {
23273
23975
  break;
23274
23976
  }
23275
23977
  cliLog(`\u274C \u81EA\u68C0\u5931\u8D25: ${probe.error}${probe.code ? ` | ${probe.code}` : ""}`);
23276
- for (const hint of paramErrorHints(probe.error ?? "", probe.code ?? "")) {
23978
+ for (const hint of [
23979
+ ...authErrorHints(probe.error ?? "", probe.code ?? ""),
23980
+ ...paramErrorHints(probe.error ?? "", probe.code ?? "")
23981
+ ]) {
23277
23982
  cliLog(` \u{1F4A1} ${hint}`);
23278
23983
  }
23279
23984
  const keep = (await question("\u91CD\u65B0\u9009\u62E9\u6A21\u578B (\u56DE\u8F66\u91CD\u9009) / \u8F93\u5165 y \u4ECD\u4F7F\u7528\u8BE5\u6A21\u578B: ")).trim().toLowerCase();
@@ -23507,7 +24212,7 @@ async function offerTerminalSetup(engine, canUseAgent) {
23507
24212
  }
23508
24213
  var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set(["link", "serve", "update", "rename"]);
23509
24214
  function cliVersion() {
23510
- const env3 = "0.2.16"?.trim();
24215
+ const env3 = "0.2.20"?.trim();
23511
24216
  if (env3) return env3;
23512
24217
  try {
23513
24218
  const req2 = (0, import_module.createRequire)(importMetaUrl);
@@ -24020,6 +24725,12 @@ async function main() {
24020
24725
  cliLog(`\u274C \u672A\u627E\u5230\u4F9B\u5E94\u5546: ${arg}`);
24021
24726
  break;
24022
24727
  }
24728
+ if (target.id === BUILTIN_PROVIDER_ID) {
24729
+ cliLog(
24730
+ "\u274C \u5E73\u53F0\u5185\u7F6E\u6A21\u578B\u4E0D\u53EF\u5220\u9664\uFF08\u63A5\u5165 Hub \u540E\u81EA\u52A8\u914D\u7F6E\uFF0CKey \u7531\u5E73\u53F0\u6258\u7BA1\uFF09\u3002\n \u5982\u9700\u6539\u7528\u5176\u5B83\u6A21\u578B\uFF1A/connect \u9009\u62E9\u6216\u6DFB\u52A0\u4F9B\u5E94\u5546\u540E\u8BBE\u4E3A\u9ED8\u8BA4\u3002"
24731
+ );
24732
+ break;
24733
+ }
24023
24734
  if (removeConnection(target.id)) {
24024
24735
  currentConnection = loadConnection();
24025
24736
  engine = buildEngine(SESSION_ID);