@zqagent/core 0.2.10 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -553,7 +553,7 @@ var init_wire = __esm({
553
553
  init_esm_shims();
554
554
  import "dotenv/config";
555
555
  import * as fs34 from "fs";
556
- import * as readline2 from "readline";
556
+ import * as readline3 from "readline";
557
557
  import * as path43 from "path";
558
558
  import { exec } from "child_process";
559
559
  import { promisify } from "util";
@@ -10077,6 +10077,38 @@ function countRawEscapes(chunk) {
10077
10077
  for (const ch of cleaned) if (ch === "\x1B") escCount++;
10078
10078
  return escCount;
10079
10079
  }
10080
+ function patchReadlineHistoryNav(rl2) {
10081
+ const iface = rl2;
10082
+ const orig = iface._ttyWrite;
10083
+ let draft = null;
10084
+ iface._ttyWrite = function(s, key) {
10085
+ const k = key;
10086
+ const name = k?.name;
10087
+ const plainArrow = (name === "up" || name === "down") && !k?.ctrl && !k?.meta && !k?.shift;
10088
+ if (plainArrow) {
10089
+ if (name === "up" && iface.historyIndex === -1) {
10090
+ draft = { line: iface.line, cursor: iface.cursor };
10091
+ }
10092
+ if (iface.cursor > 0) {
10093
+ iface._moveCursor(-iface.cursor);
10094
+ }
10095
+ }
10096
+ const prevHistoryIndex = iface.historyIndex;
10097
+ orig.call(rl2, s, key);
10098
+ if (plainArrow && name === "down" && prevHistoryIndex >= 0 && iface.historyIndex === -1) {
10099
+ const saved = draft;
10100
+ draft = null;
10101
+ if (saved) {
10102
+ iface.line = saved.line;
10103
+ iface.cursor = Math.min(saved.cursor, saved.line.length);
10104
+ iface._refreshLine?.();
10105
+ }
10106
+ }
10107
+ };
10108
+ rl2.on("line", () => {
10109
+ draft = null;
10110
+ });
10111
+ }
10080
10112
  var PASTE_PLACEHOLDER_PREFIX = "\u7C98\u8D34\u6587\u672C #";
10081
10113
  var InputRouter = class {
10082
10114
  rl;
@@ -10464,7 +10496,8 @@ function renderTerminalTutorial() {
10464
10496
 
10465
10497
  // src/cli/link.ts
10466
10498
  init_esm_shims();
10467
- import * as path22 from "path";
10499
+ import * as path28 from "path";
10500
+ import * as readline2 from "readline";
10468
10501
 
10469
10502
  // src/server/store.ts
10470
10503
  init_esm_shims();
@@ -10872,726 +10905,368 @@ async function hubEnrollByAccount(baseUrl, input) {
10872
10905
  return { hubUrl: baseUrl.replace(/\/$/, ""), ...data };
10873
10906
  }
10874
10907
 
10875
- // src/cli/serve-instances.ts
10908
+ // src/cli/account-enroll.ts
10876
10909
  init_esm_shims();
10877
- import { spawnSync as spawnSync2 } from "child_process";
10878
- import * as fs18 from "fs";
10879
- import * as path21 from "path";
10880
- var DEFAULT_SERVE_HOST = "127.0.0.1";
10881
- var DEFAULT_SERVE_PORT = 8787;
10882
- function serveSubcommandIndex(argv) {
10883
- for (let i = 1; i < argv.length; i++) {
10884
- if (argv[i].startsWith("-")) continue;
10885
- return argv[i] === "serve" ? i : -1;
10886
- }
10887
- return -1;
10888
- }
10889
- function parseServeArgs(argv) {
10890
- const at = serveSubcommandIndex(argv);
10891
- if (at < 0) return null;
10892
- let dataDir = null;
10893
- let host = DEFAULT_SERVE_HOST;
10894
- let port = DEFAULT_SERVE_PORT;
10895
- for (let i = at + 1; i < argv.length; i++) {
10896
- const arg = argv[i];
10897
- if (arg === "--data-dir" && argv[i + 1]) {
10898
- dataDir = argv[++i];
10899
- } else if (arg.startsWith("--data-dir=")) {
10900
- dataDir = arg.slice("--data-dir=".length);
10901
- } else if (arg === "--host" && argv[i + 1]) {
10902
- host = argv[++i];
10903
- } else if (arg.startsWith("--host=")) {
10904
- host = arg.slice("--host=".length);
10905
- } else if (arg === "--port" && argv[i + 1]) {
10906
- port = Number(argv[++i]);
10907
- } else if (arg.startsWith("--port=")) {
10908
- port = Number(arg.slice("--port=".length));
10909
- }
10910
- }
10911
- if (!Number.isFinite(port) || port <= 0) port = DEFAULT_SERVE_PORT;
10912
- if (host === "0.0.0.0" || host === "::" || host === "") host = DEFAULT_SERVE_HOST;
10913
- return { dataDir, host, port };
10910
+ import * as path25 from "path";
10911
+ import * as readline from "readline/promises";
10912
+
10913
+ // src/server/projects.ts
10914
+ init_esm_shims();
10915
+ import * as path24 from "path";
10916
+
10917
+ // src/server/session.ts
10918
+ init_esm_shims();
10919
+ import * as path23 from "path";
10920
+ import { AsyncLocalStorage } from "async_hooks";
10921
+
10922
+ // src/server/mcpConfig.ts
10923
+ init_esm_shims();
10924
+ import fs18 from "fs";
10925
+ import path21 from "path";
10926
+ function isValidMcpConfig(v) {
10927
+ return !!v && typeof v === "object" && typeof v.name === "string" && (v.type === "stdio" || v.type === "http");
10914
10928
  }
10915
- function buildHotCandidates(explicitDataDir, discovered, fallback) {
10916
- const out = explicitDataDir ? [{ dataDir: path21.resolve(explicitDataDir), ...fallback }] : [{ dataDir: path21.resolve(DEFAULT_DATA_DIR), ...fallback }];
10917
- for (const inst of discovered) {
10918
- if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
10929
+ function readConfigFile(file) {
10930
+ if (!file) return [];
10931
+ try {
10932
+ if (!fs18.existsSync(file)) return [];
10933
+ const raw = JSON.parse(fs18.readFileSync(file, "utf8"));
10934
+ const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.servers) ? raw.servers : [];
10935
+ return list.filter(isValidMcpConfig);
10936
+ } catch (err) {
10937
+ console.error(`[mcp] \u8BFB\u53D6\u914D\u7F6E\u5931\u8D25 ${file}: ${err.message}`);
10938
+ return [];
10919
10939
  }
10920
- return out;
10921
- }
10922
- function isWindowsAbsolute(p) {
10923
- return /^[A-Za-z]:[\\/]/.test(p) || /^\\\\/.test(p);
10924
10940
  }
10925
- function isAbsolutePath(p) {
10926
- return path21.isAbsolute(p) || isWindowsAbsolute(p);
10941
+ function hasServerMcpConfig() {
10942
+ if (readConfigFile(process.env.MCP_CONFIG_PATH).length > 0) return true;
10943
+ const workspace = process.env.WORKSPACE;
10944
+ if (!workspace) return false;
10945
+ return readConfigFile(path21.join(workspace, ".agent", "mcp.json")).length > 0;
10927
10946
  }
10928
- function normalizePath(p) {
10929
- return isWindowsAbsolute(p) ? path21.win32.normalize(p) : path21.normalize(p);
10947
+ function loadServerMcpConfigs(tenant) {
10948
+ const fromEnv = readConfigFile(process.env.MCP_CONFIG_PATH);
10949
+ if (fromEnv.length > 0) return fromEnv;
10950
+ if (Array.isArray(tenant.mcpServers) && tenant.mcpServers.length > 0) {
10951
+ return tenant.mcpServers.filter(isValidMcpConfig);
10952
+ }
10953
+ const workspace = tenant.workspaceRoot ?? process.env.WORKSPACE;
10954
+ if (workspace)
10955
+ return readConfigFile(path21.join(workspace, ".agent", "mcp.json"));
10956
+ return [];
10930
10957
  }
10931
- function parseServeCommandLine(line) {
10932
- if (!/(?:^|\s)["']?serve["']?(?=\s|$)/.test(line)) return null;
10933
- const m = /--data-dir(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(.+?))(?=\s+--\w|\s*$)/.exec(line);
10934
- const rawDataDir = (m?.[1] ?? m?.[2] ?? m?.[3] ?? "").trim();
10935
- if (!rawDataDir || !isAbsolutePath(rawDataDir)) return null;
10936
- const host = /--host(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(\S+))/.exec(line);
10937
- const rawHost = (host?.[1] ?? host?.[2] ?? host?.[3] ?? DEFAULT_SERVE_HOST).trim();
10938
- const portMatch = /--port(?:=|\s+)(\d+)/.exec(line);
10939
- const port = Number(portMatch?.[1] ?? DEFAULT_SERVE_PORT);
10958
+
10959
+ // src/server/nodeLlm.ts
10960
+ init_esm_shims();
10961
+ var GLOBAL_PROVIDER_ID = "__env__";
10962
+ function envProvider() {
10963
+ const apiKey = process.env.OPENAI_API_KEY;
10964
+ if (!apiKey) return null;
10940
10965
  return {
10941
- dataDir: normalizePath(rawDataDir),
10942
- host: rawHost === "0.0.0.0" || rawHost === "::" || rawHost === "" ? DEFAULT_SERVE_HOST : rawHost,
10943
- port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT
10966
+ id: GLOBAL_PROVIDER_ID,
10967
+ name: process.env.OPENAI_MODEL || process.env.MODEL || "\u5168\u5C40 LLM",
10968
+ type: "env",
10969
+ baseURL: process.env.OPENAI_BASE_URL ?? "",
10970
+ model: process.env.OPENAI_MODEL || process.env.MODEL || "",
10971
+ apiKey
10944
10972
  };
10945
10973
  }
10946
- function processCwd(pid) {
10947
- try {
10948
- return fs18.readlinkSync(`/proc/${pid}/cwd`);
10949
- } catch {
10950
- return null;
10951
- }
10974
+ function normReasoningEffort(v) {
10975
+ return v === "low" || v === "medium" || v === "high" ? v : null;
10952
10976
  }
10953
- function processEnv(pid) {
10954
- const out = {};
10955
- try {
10956
- const raw = fs18.readFileSync(`/proc/${pid}/environ`, "utf-8");
10957
- for (const entry of raw.split("\0")) {
10958
- const eq = entry.indexOf("=");
10959
- if (eq > 0) out[entry.slice(0, eq)] = entry.slice(eq + 1);
10960
- }
10961
- } catch {
10977
+ function normalizeModelList(v) {
10978
+ if (!Array.isArray(v)) return void 0;
10979
+ const out = [];
10980
+ const seen = /* @__PURE__ */ new Set();
10981
+ for (const x of v) {
10982
+ const s = String(x ?? "").trim();
10983
+ if (!s || seen.has(s)) continue;
10984
+ seen.add(s);
10985
+ out.push(s);
10986
+ if (out.length >= 500) break;
10962
10987
  }
10963
10988
  return out;
10964
10989
  }
10965
- function resolveDataDir(rawDataDir, cwd, env3) {
10966
- const pick = rawDataDir ?? env3.DATA_DIR ?? null;
10967
- if (pick) return path21.isAbsolute(pick) ? path21.normalize(pick) : cwd ? path21.resolve(cwd, pick) : null;
10968
- return cwd ? path21.resolve(cwd, DEFAULT_DATA_DIR) : null;
10990
+ function nodeLlmView(cfg) {
10991
+ const g = envProvider();
10992
+ const providers = [
10993
+ ...g ? [
10994
+ {
10995
+ id: g.id,
10996
+ name: g.name,
10997
+ type: g.type,
10998
+ baseURL: g.baseURL,
10999
+ model: g.model,
11000
+ hasApiKey: true,
11001
+ global: true
11002
+ }
11003
+ ] : [],
11004
+ ...cfg.providers.map((p) => ({
11005
+ id: p.id,
11006
+ name: p.name,
11007
+ type: p.type ?? "",
11008
+ baseURL: p.baseURL,
11009
+ model: p.model,
11010
+ hasApiKey: Boolean(p.apiKey),
11011
+ models: p.models ?? [],
11012
+ nonChat: p.nonChat ?? [],
11013
+ modelMeta: p.modelMeta ?? [],
11014
+ reasoningEffort: p.reasoningEffort ?? null,
11015
+ global: false
11016
+ }))
11017
+ ];
11018
+ return {
11019
+ defaultProviderId: cfg.defaultProviderId ?? null,
11020
+ providers,
11021
+ /** 未显式设置默认时,是否由环境变量全局配置兜底 */
11022
+ globalIsDefault: !cfg.defaultProviderId && Boolean(g)
11023
+ };
10969
11024
  }
10970
- function discoverFromProc() {
10971
- const out = [];
10972
- let pids;
10973
- try {
10974
- pids = fs18.readdirSync("/proc");
10975
- } catch {
10976
- return out;
11025
+ function applyNodeLlmPatch(cfg, raw) {
11026
+ const next = { providers: cfg.providers, defaultProviderId: cfg.defaultProviderId };
11027
+ if (Array.isArray(raw?.providers)) {
11028
+ const old = new Map(cfg.providers.map((p) => [p.id, p]));
11029
+ next.providers = raw.providers.filter((x) => Boolean(x) && typeof x === "object").filter((x) => x.global !== true && String(x.id ?? "") !== GLOBAL_PROVIDER_ID).map((x) => {
11030
+ const id = String(x.id ?? "").trim();
11031
+ if (!id) throw new ApiError(400, "bad_request", "\u4F9B\u5E94\u5546 id \u4E0D\u80FD\u4E3A\u7A7A");
11032
+ const prev = old.get(id);
11033
+ const apiKey = String(x.apiKey ?? "").trim();
11034
+ const models = normalizeModelList(x.models) ?? prev?.models;
11035
+ const nonChat = normalizeModelList(x.nonChat) ?? prev?.nonChat;
11036
+ const modelMeta = normalizeModelMeta(x.modelMeta) ?? prev?.modelMeta;
11037
+ return {
11038
+ id,
11039
+ name: String(x.name ?? prev?.name ?? id).trim(),
11040
+ type: x.type == null ? prev?.type : String(x.type),
11041
+ baseURL: String(x.baseURL ?? prev?.baseURL ?? "").trim(),
11042
+ model: String(x.model ?? prev?.model ?? "").trim(),
11043
+ apiKey: apiKey || prev?.apiKey,
11044
+ reasoningEffort: x.reasoningEffort === void 0 ? prev?.reasoningEffort ?? null : normReasoningEffort(x.reasoningEffort),
11045
+ ...models ? { models } : {},
11046
+ ...nonChat ? { nonChat } : {},
11047
+ ...modelMeta ? { modelMeta } : {}
11048
+ };
11049
+ });
10977
11050
  }
10978
- for (const name of pids) {
10979
- if (!/^\d+$/.test(name)) continue;
10980
- const pid = Number(name);
10981
- if (pid === process.pid) continue;
10982
- let argv;
10983
- try {
10984
- argv = fs18.readFileSync(`/proc/${pid}/cmdline`, "utf-8").split("\0").filter(Boolean);
10985
- } catch {
10986
- continue;
10987
- }
10988
- const parsed = parseServeArgs(argv);
10989
- if (!parsed) continue;
10990
- const dataDir = resolveDataDir(parsed.dataDir, processCwd(pid), processEnv(pid));
10991
- if (!dataDir) continue;
10992
- out.push({ dataDir, host: parsed.host, port: parsed.port });
11051
+ if (typeof raw?.defaultProviderId === "string") {
11052
+ const id = raw.defaultProviderId.trim();
11053
+ next.defaultProviderId = id || void 0;
11054
+ } else if (raw?.defaultProviderId === null) {
11055
+ next.defaultProviderId = void 0;
10993
11056
  }
10994
- return out;
11057
+ return next;
10995
11058
  }
10996
- function discoverFromPs() {
10997
- const out = [];
10998
- const res = spawnSync2("ps", ["-axo", "command="], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
10999
- if (res.status !== 0 || !res.stdout) return out;
11000
- for (const line of res.stdout.split("\n")) {
11001
- const inst = parseServeCommandLine(line);
11002
- if (inst) out.push(inst);
11003
- }
11004
- return out;
11059
+ function findNodeProvider(store, id) {
11060
+ if (!id) return null;
11061
+ if (id === GLOBAL_PROVIDER_ID) return envProvider();
11062
+ return store.loadNodeLlm().providers.find((p) => p.id === id) ?? null;
11005
11063
  }
11006
- function discoverFromWindows() {
11007
- const out = [];
11008
- const script = `[Console]::OutputEncoding=[Text.Encoding]::UTF8; Get-CimInstance Win32_Process -Filter "Name='zqagent.exe' OR Name='node.exe'" | ForEach-Object { $_.CommandLine }`;
11009
- const res = spawnSync2(
11010
- "powershell.exe",
11011
- ["-NoProfile", "-NonInteractive", "-Command", script],
11012
- { encoding: "utf-8", timeout: 15e3, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
11013
- );
11014
- if (res.status !== 0 || !res.stdout) return out;
11015
- for (const line of res.stdout.replace(/^\uFEFF/, "").split(/\r?\n/)) {
11016
- const inst = parseServeCommandLine(line);
11017
- if (inst) out.push(inst);
11018
- }
11019
- return out;
11064
+ function nodeDefaultProvider(store) {
11065
+ const cfg = store.loadNodeLlm();
11066
+ if (cfg.providers.length === 0) return null;
11067
+ return cfg.providers.find((p) => p.id === cfg.defaultProviderId) ?? cfg.providers[0] ?? null;
11020
11068
  }
11021
- function discoverRunningServeInstances() {
11022
- const found = process.platform === "linux" ? discoverFromProc() : process.platform === "win32" ? discoverFromWindows() : discoverFromPs();
11023
- const seen = /* @__PURE__ */ new Set();
11024
- return found.filter((inst) => {
11025
- if (seen.has(inst.dataDir)) return false;
11026
- seen.add(inst.dataDir);
11027
- return true;
11028
- });
11069
+ function resolveSessionProvider(store, tenant, providerId) {
11070
+ const own = tenant.provider ?? {};
11071
+ if (providerId) {
11072
+ const picked = findNodeProvider(store, providerId);
11073
+ if (picked) {
11074
+ return {
11075
+ apiKey: picked.apiKey || own.apiKey,
11076
+ baseURL: picked.baseURL || own.baseURL,
11077
+ model: picked.model,
11078
+ providerId,
11079
+ reasoningEffort: picked.reasoningEffort ?? null,
11080
+ ...picked.modelMeta ? { modelMeta: picked.modelMeta } : {}
11081
+ };
11082
+ }
11083
+ }
11084
+ if (own.apiKey) return { ...own, model: tenant.model };
11085
+ const def = nodeDefaultProvider(store);
11086
+ if (def?.apiKey) {
11087
+ return {
11088
+ apiKey: def.apiKey,
11089
+ baseURL: own.baseURL || def.baseURL,
11090
+ model: def.model || tenant.model,
11091
+ reasoningEffort: def.reasoningEffort ?? null,
11092
+ ...def.modelMeta ? { modelMeta: def.modelMeta } : {}
11093
+ };
11094
+ }
11095
+ return { ...own, model: tenant.model };
11029
11096
  }
11030
11097
 
11031
- // src/cli/link.ts
11032
- function expandHome(p) {
11033
- const home = process.env.HOME || process.env.USERPROFILE;
11034
- if (!home) return p;
11035
- if (p === "~") return home;
11036
- if (p.startsWith("~/") || p.startsWith("~\\")) return path22.join(home, p.slice(2));
11037
- return p;
11098
+ // src/server/usage.ts
11099
+ init_esm_shims();
11100
+ import * as fs19 from "fs";
11101
+ import * as path22 from "path";
11102
+ var LEDGER_RETENTION_MONTHS = 12;
11103
+ var USAGE_SCHEMA = 1;
11104
+ var DEFAULT_PRICING = {
11105
+ schema: USAGE_SCHEMA,
11106
+ base: "USD",
11107
+ rate: { CNY: 7.2 },
11108
+ vendors: {
11109
+ openai: {
11110
+ "gpt-4o": { input: 2.5, output: 10, cacheRead: 1.25 },
11111
+ "gpt-4o-mini": { input: 0.15, output: 0.6, cacheRead: 0.075 },
11112
+ "gpt-4.1": { input: 2, output: 8, cacheRead: 0.5 }
11113
+ },
11114
+ deepseek: {
11115
+ "deepseek-flash": {
11116
+ input: 0.27,
11117
+ output: 1.1,
11118
+ cacheRead: 0.027,
11119
+ cacheWrite: 0.27
11120
+ },
11121
+ "deepseek-reasoner": {
11122
+ input: 0.55,
11123
+ output: 2.19,
11124
+ cacheRead: 0.055,
11125
+ cacheWrite: 0.55
11126
+ },
11127
+ "deepseek-*": {
11128
+ input: 0.27,
11129
+ output: 1.1,
11130
+ cacheRead: 0.027,
11131
+ cacheWrite: 0.27
11132
+ }
11133
+ },
11134
+ qwen: {
11135
+ "qwen-plus": { input: 0.4, output: 1.2 },
11136
+ "qwen-turbo": { input: 0.05, output: 0.2 }
11137
+ },
11138
+ zhipu: { "glm-4-plus": { input: 0.7, output: 0.7 } },
11139
+ moonshot: { "moonshot-v1-8k": { input: 1.68, output: 1.68 } },
11140
+ xiaomi: {},
11141
+ custom: {},
11142
+ env: {}
11143
+ },
11144
+ fallback: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
11145
+ };
11146
+ function zeroAgg() {
11147
+ return {
11148
+ calls: 0,
11149
+ input: 0,
11150
+ output: 0,
11151
+ cacheRead: 0,
11152
+ cacheWrite: 0,
11153
+ costUsd: 0,
11154
+ unpriced: 0
11155
+ };
11038
11156
  }
11039
- function installDataDirHint() {
11040
- if (process.platform === "darwin") return "$HOME/Library/Application Support/zqagent";
11041
- if (process.platform === "win32") return "%LOCALAPPDATA%\\zqagent";
11042
- return "/var/lib/zqagent\uFF08\u9700 root\uFF1B\u666E\u901A\u7528\u6237\u7528 ~/.zqagent_data\uFF09";
11157
+ function addAgg(into, tokens, costUsd) {
11158
+ into.calls += 1;
11159
+ into.input += tokens.input;
11160
+ into.output += tokens.output;
11161
+ into.cacheRead += tokens.cacheRead;
11162
+ into.cacheWrite += tokens.cacheWrite;
11163
+ if (costUsd == null) into.unpriced += 1;
11164
+ else into.costUsd += costUsd;
11043
11165
  }
11044
- function parseLinkArgs(argv) {
11045
- const out = {};
11046
- for (let i = 0; i < argv.length; i++) {
11047
- const a = argv[i];
11048
- const val = argv[i + 1];
11049
- if (a === "--hub") {
11050
- out.hubUrl = val;
11051
- i++;
11052
- } else if (a === "--code") {
11053
- out.code = val;
11054
- i++;
11055
- } else if (a === "--data-dir") {
11056
- out.dataDir = val;
11057
- i++;
11058
- } else if (a === "--label") {
11059
- out.label = val;
11060
- i++;
11061
- } else if (a === "--host") {
11062
- out.host = val;
11063
- i++;
11064
- } else if (a === "--port") {
11065
- out.port = Number(val);
11066
- i++;
11067
- } else if (a === "-h" || a === "--help") {
11068
- out.help = true;
11069
- }
11166
+ function utcDay(iso) {
11167
+ return iso.slice(0, 10);
11168
+ }
11169
+ function monthOf(iso) {
11170
+ return iso.slice(0, 7);
11171
+ }
11172
+ function retainedMonths(now = /* @__PURE__ */ new Date()) {
11173
+ const out = /* @__PURE__ */ new Set();
11174
+ const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
11175
+ for (let i = 0; i < LEDGER_RETENTION_MONTHS; i += 1) {
11176
+ out.add(d.toISOString().slice(0, 7));
11177
+ d.setUTCMonth(d.getUTCMonth() - 1);
11070
11178
  }
11071
11179
  return out;
11072
11180
  }
11073
- function ensureNodeId(store) {
11074
- let id = store.loadNodeId();
11075
- if (!id) {
11076
- id = newId();
11077
- store.saveNodeId(id);
11078
- }
11079
- return id;
11181
+ function atomicWrite(file, content) {
11182
+ fs19.mkdirSync(path22.dirname(file), { recursive: true });
11183
+ const tmp = `${file}.tmp`;
11184
+ fs19.writeFileSync(tmp, content, "utf-8");
11185
+ fs19.renameSync(tmp, file);
11080
11186
  }
11081
- async function probeServer(host, port, adminKey) {
11082
- const ctrl = new AbortController();
11083
- const timer = setTimeout(() => ctrl.abort(), 1500);
11084
- try {
11085
- const res = await fetch(`http://${host}:${port}/api/v1/fleet/status`, {
11086
- headers: { Authorization: `Bearer ${adminKey}` },
11087
- signal: ctrl.signal
11088
- });
11089
- return res.ok;
11090
- } catch {
11091
- return false;
11092
- } finally {
11093
- clearTimeout(timer);
11187
+ var UsageLedger = class {
11188
+ constructor(root) {
11189
+ this.root = root;
11190
+ this.dir = path22.join(root, "usage");
11094
11191
  }
11095
- }
11096
- async function enrollViaServer(host, port, adminKey, opts) {
11097
- const res = await fetch(`http://${host}:${port}/api/v1/fleet/enroll`, {
11098
- method: "POST",
11099
- headers: {
11100
- "Content-Type": "application/json",
11101
- Authorization: `Bearer ${adminKey}`
11102
- },
11103
- body: JSON.stringify({ hubUrl: opts.hubUrl, code: opts.code, label: opts.label })
11104
- });
11105
- const text = await res.text();
11106
- let payload = null;
11107
- try {
11108
- payload = text ? JSON.parse(text) : null;
11109
- } catch {
11192
+ root;
11193
+ dir;
11194
+ rollup;
11195
+ pricing;
11196
+ rollupDirty = false;
11197
+ rollupTimer = null;
11198
+ inited = false;
11199
+ get ledgerDir() {
11200
+ return this.dir;
11110
11201
  }
11111
- if (!res.ok) {
11112
- throw new Error(payload?.error?.message ?? `HTTP ${res.status}`);
11202
+ ledgerFile(month) {
11203
+ return path22.join(this.ledgerDir, `ledger-${month}.jsonl`);
11113
11204
  }
11114
- return payload?.data;
11115
- }
11116
- async function linkNode(opts) {
11117
- const rawDataDir = opts.dataDir?.trim() || process.env.DATA_DIR?.trim() || void 0;
11118
- const explicitDataDir = rawDataDir ? expandHome(rawDataDir) : void 0;
11119
- const host = opts.host?.trim() || process.env.HOST?.trim() || DEFAULT_SERVE_HOST;
11120
- const port = opts.port ?? Number(process.env.PORT ?? DEFAULT_SERVE_PORT);
11121
- const running = discoverRunningServeInstances();
11122
- const candidates = buildHotCandidates(explicitDataDir, running, { host, port });
11123
- for (const inst of candidates) {
11124
- const store2 = new Store(inst.dataDir);
11125
- const adminKey = process.env.ADMIN_API_KEY ?? store2.loadAdminKey();
11126
- if (!adminKey) continue;
11127
- if (!await probeServer(inst.host, inst.port, adminKey)) continue;
11128
- const status = await enrollViaServer(inst.host, inst.port, adminKey, opts);
11129
- return { mode: "hot", ...status, dataDir: inst.dataDir };
11205
+ get rollupFile() {
11206
+ return path22.join(this.ledgerDir, "rollup.json");
11130
11207
  }
11131
- const dataDir = path22.resolve(explicitDataDir ?? DEFAULT_DATA_DIR);
11132
- const store = new Store(dataDir);
11133
- const nodeId = ensureNodeId(store);
11134
- const cred = await hubEnrollByCode(opts.hubUrl, {
11135
- code: opts.code,
11136
- nodeId,
11137
- label: opts.label
11138
- });
11139
- try {
11140
- store.saveFleetCredential({
11141
- ...cred,
11142
- label: opts.label ?? cred.label,
11143
- enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
11144
- });
11145
- } catch (e) {
11146
- const err = e;
11147
- if (err.code === "EACCES" || err.code === "EPERM") {
11148
- throw new Error(
11149
- `\u65E0\u6CD5\u5199\u5165\u6570\u636E\u76EE\u5F55 ${dataDir}\uFF08${err.code}: ${err.message}\uFF09
11150
- \u8BE5\u76EE\u5F55\u5F53\u524D\u7528\u6237\u6CA1\u6709\u5199\u6743\u9650\u3002\u4E24\u79CD\u6539\u6CD5\uFF1A
11151
- 1) \u53BB\u6389 --data-dir\uFF0C\u8BA9 link \u81EA\u52A8\u53D1\u73B0\u672C\u673A\u8FD0\u884C\u7684\u8282\u70B9\uFF08\u63A8\u8350\uFF09\uFF1B
11152
- 2) \u7528\u5F53\u524D\u7528\u6237\u53EF\u5199\u7684\u76EE\u5F55\uFF0C\u672C\u5E73\u53F0\u5B89\u88C5\u811A\u672C\u9ED8\u8BA4\uFF1A${installDataDirHint()}
11153
- \uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF09`
11154
- );
11155
- }
11156
- throw e;
11208
+ get pricingFile() {
11209
+ return path22.join(this.ledgerDir, "pricing.json");
11157
11210
  }
11158
- const otherRunningDirs = [
11159
- ...new Set(running.map((r) => r.dataDir).filter((d) => d !== dataDir))
11160
- ];
11161
- return {
11162
- mode: "saved",
11163
- nodeId,
11164
- gatewayUrl: cred.gatewayUrl,
11165
- label: opts.label,
11166
- dataDir,
11167
- explicitDataDir: Boolean(explicitDataDir),
11168
- runningServiceDirs: otherRunningDirs
11169
- };
11170
- }
11171
- function printLinkHelp() {
11172
- cliLog("\u7528\u6CD5: zqagent link --hub <url> --code <\u6CE8\u518C\u7801> [--label \u540D\u79F0] [--data-dir \u76EE\u5F55]");
11173
- cliLog(" \u5728 Hub \u7528\u6237\u4E2D\u5FC3\u751F\u6210\u4E00\u6B21\u6027\u6CE8\u518C\u7801\u540E\uFF0C\u4E8E\u8282\u70B9\u673A\u5668\u6267\u884C\u672C\u547D\u4EE4\u5373\u53EF\u63A5\u5165\u3002");
11174
- cliLog(" \u5DF2\u6709\u672C\u673A server \u65F6\u70ED\u63A5\u5165\uFF08\u514D\u91CD\u542F\uFF0C\u4F1A\u81EA\u52A8\u53D1\u73B0\u8FD0\u884C\u4E2D\u670D\u52A1\u7684 --data-dir\uFF09\uFF1B");
11175
- cliLog(" \u5426\u5219\u5199\u5165 <\u6570\u636E\u76EE\u5F55>/fleet.json\uFF0C\u4E0B\u6B21\u542F\u52A8\u81EA\u52A8\u63A5\u5165\u3002");
11176
- }
11177
- async function runLinkCommand(argv) {
11178
- const args = parseLinkArgs(argv);
11179
- if (args.help) {
11180
- printLinkHelp();
11181
- return true;
11182
- }
11183
- const hubUrl = (args.hubUrl ?? process.env.HUB_URL ?? "").trim().replace(/\/$/, "");
11184
- const code = (args.code ?? "").trim();
11185
- if (!hubUrl || !code) {
11186
- cliErr("\u7F3A\u5C11 --hub \u6216 --code\u3002");
11187
- printLinkHelp();
11188
- return false;
11211
+ // ===================== 初始化 =====================
11212
+ init(now = /* @__PURE__ */ new Date()) {
11213
+ if (this.inited) return;
11214
+ fs19.mkdirSync(this.ledgerDir, { recursive: true });
11215
+ this.pricing = this.loadPricing();
11216
+ this.rollup = this.loadRollup(now);
11217
+ this.pruneLedgers(now);
11218
+ this.inited = true;
11189
11219
  }
11190
- try {
11191
- const r = await linkNode({
11192
- hubUrl,
11193
- code,
11194
- dataDir: args.dataDir,
11195
- label: args.label,
11196
- host: args.host,
11197
- port: args.port
11198
- });
11199
- if (r.mode === "hot") {
11200
- cliLog("\u2705 \u5DF2\u63A5\u5165 Hub\uFF08\u70ED\u63A5\u5165\uFF0C\u514D\u91CD\u542F\uFF09");
11201
- cliLog(` \u8282\u70B9: ${r.nodeId}${r.label ? ` (${r.label})` : ""}`);
11202
- cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"} \u5728\u7EBF: ${r.online ? "\u662F" : "\u8FDE\u63A5\u4E2D\u2026"}`);
11203
- cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}`);
11204
- } else {
11205
- cliLog("\u2705 \u63A5\u5165\u51ED\u636E\u5DF2\u4FDD\u5B58\uFF08\u672C\u673A\u6682\u65E0\u8FD0\u884C\u4E2D\u7684 server\uFF09");
11206
- cliLog(` \u8282\u70B9: ${r.nodeId}`);
11207
- cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"}`);
11208
- cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}`);
11209
- cliLog(" \u4E0B\u6B21\u542F\u52A8 server \u5373\u81EA\u52A8\u63A5\u5165\uFF1B\u6216\u8FD0\u884C zqagent serve \u5E38\u9A7B\u4E3A\u8282\u70B9\u3002");
11210
- if (r.runningServiceDirs.length > 0) {
11211
- cliLog("");
11212
- cliLog("\u26A0\uFE0F \u672C\u673A\u6709\u6B63\u5728\u8FD0\u884C\u7684\u8282\u70B9\uFF0C\u4F46\u6570\u636E\u76EE\u5F55\u4E0E\u672C\u6B21\u5199\u5165\u7684\u4E0D\u4E00\u81F4\uFF1A");
11213
- cliLog(` \u672C\u6B21\u5199\u5165: ${r.dataDir}`);
11214
- for (const d of r.runningServiceDirs) cliLog(` \u670D\u52A1\u4F7F\u7528: ${d}`);
11215
- cliLog(" \u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1\u8BFB\u4E0D\u5230\u8FD9\u4EFD\u51ED\u636E\uFF0CHub \u4F1A\u4E00\u76F4\u663E\u793A\u672A\u63A5\u5165\u3002");
11216
- cliLog(" \u8BF7\u786E\u8BA4\u670D\u52A1\u7684\u6570\u636E\u76EE\u5F55\u540E\u91CD\u65B0\u6267\u884C\uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF09\uFF1A");
11217
- cliLog(` zqagent link --hub <Hub\u5730\u5740> --code <\u65B0\u63A5\u5165\u7801> --data-dir "${r.runningServiceDirs[0]}"`);
11218
- } else if (!r.explicitDataDir) {
11219
- cliLog("");
11220
- cliLog("\u26A0\uFE0F \u672A\u6307\u5B9A --data-dir\uFF0C\u51ED\u636E\u5199\u5165\u4E86\u5F53\u524D\u76EE\u5F55\u4E0B\u7684 .zqagent_data\u3002");
11221
- cliLog(" \u82E5\u672C\u673A\u670D\u52A1\u662F\u7531\u5B89\u88C5\u811A\u672C\uFF08systemd / launchd\uFF09\u542F\u52A8\u7684\uFF0C\u5B83\u8BFB\u4E0D\u5230\u8FD9\u4EFD\u51ED\u636E\uFF0C");
11222
- cliLog(" Hub \u4F1A\u4E00\u76F4\u663E\u793A\u672A\u5728\u7EBF\u3002\u8BF7\u7528\u670D\u52A1\u7684\u6570\u636E\u76EE\u5F55\u91CD\u65B0\u6267\u884C\uFF1A");
11223
- cliLog(" zqagent link --hub <Hub\u5730\u5740> --code <\u65B0\u63A5\u5165\u7801> --data-dir <\u670D\u52A1\u6570\u636E\u76EE\u5F55>");
11224
- cliLog(` \uFF08\u672C\u5E73\u53F0\u5B89\u88C5\u811A\u672C\u9ED8\u8BA4\uFF1A${installDataDirHint()}\uFF1B\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF09`);
11220
+ loadPricing() {
11221
+ try {
11222
+ const raw = fs19.readFileSync(this.pricingFile, "utf-8");
11223
+ const parsed = JSON.parse(raw);
11224
+ const vendors = {};
11225
+ for (const v of /* @__PURE__ */ new Set([
11226
+ ...Object.keys(DEFAULT_PRICING.vendors),
11227
+ ...Object.keys(parsed.vendors ?? {})
11228
+ ])) {
11229
+ vendors[v] = {
11230
+ ...DEFAULT_PRICING.vendors[v] ?? {},
11231
+ ...(parsed.vendors ?? {})[v] ?? {}
11232
+ };
11233
+ }
11234
+ return {
11235
+ schema: USAGE_SCHEMA,
11236
+ base: "USD",
11237
+ rate: { ...DEFAULT_PRICING.rate, ...parsed.rate ?? {} },
11238
+ vendors,
11239
+ fallback: { ...DEFAULT_PRICING.fallback, ...parsed.fallback ?? {} }
11240
+ };
11241
+ } catch {
11242
+ try {
11243
+ atomicWrite(this.pricingFile, JSON.stringify(DEFAULT_PRICING, null, 2));
11244
+ } catch {
11225
11245
  }
11246
+ return JSON.parse(JSON.stringify(DEFAULT_PRICING));
11226
11247
  }
11227
- return true;
11228
- } catch (e) {
11229
- cliErr(`\u63A5\u5165\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
11230
- return false;
11231
- }
11232
- }
11233
-
11234
- // src/cli/serve.ts
11235
- init_esm_shims();
11236
-
11237
- // src/server/runtime.ts
11238
- init_esm_shims();
11239
- import * as fs31 from "fs";
11240
- import * as path38 from "path";
11241
-
11242
- // src/server/session.ts
11243
- init_esm_shims();
11244
- import * as path25 from "path";
11245
- import { AsyncLocalStorage } from "async_hooks";
11246
-
11247
- // src/server/mcpConfig.ts
11248
- init_esm_shims();
11249
- import fs19 from "fs";
11250
- import path23 from "path";
11251
- function isValidMcpConfig(v) {
11252
- return !!v && typeof v === "object" && typeof v.name === "string" && (v.type === "stdio" || v.type === "http");
11253
- }
11254
- function readConfigFile(file) {
11255
- if (!file) return [];
11256
- try {
11257
- if (!fs19.existsSync(file)) return [];
11258
- const raw = JSON.parse(fs19.readFileSync(file, "utf8"));
11259
- const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.servers) ? raw.servers : [];
11260
- return list.filter(isValidMcpConfig);
11261
- } catch (err) {
11262
- console.error(`[mcp] \u8BFB\u53D6\u914D\u7F6E\u5931\u8D25 ${file}: ${err.message}`);
11263
- return [];
11264
- }
11265
- }
11266
- function hasServerMcpConfig() {
11267
- if (readConfigFile(process.env.MCP_CONFIG_PATH).length > 0) return true;
11268
- const workspace = process.env.WORKSPACE;
11269
- if (!workspace) return false;
11270
- return readConfigFile(path23.join(workspace, ".agent", "mcp.json")).length > 0;
11271
- }
11272
- function loadServerMcpConfigs(tenant) {
11273
- const fromEnv = readConfigFile(process.env.MCP_CONFIG_PATH);
11274
- if (fromEnv.length > 0) return fromEnv;
11275
- if (Array.isArray(tenant.mcpServers) && tenant.mcpServers.length > 0) {
11276
- return tenant.mcpServers.filter(isValidMcpConfig);
11277
- }
11278
- const workspace = tenant.workspaceRoot ?? process.env.WORKSPACE;
11279
- if (workspace)
11280
- return readConfigFile(path23.join(workspace, ".agent", "mcp.json"));
11281
- return [];
11282
- }
11283
-
11284
- // src/server/nodeLlm.ts
11285
- init_esm_shims();
11286
- var GLOBAL_PROVIDER_ID = "__env__";
11287
- function envProvider() {
11288
- const apiKey = process.env.OPENAI_API_KEY;
11289
- if (!apiKey) return null;
11290
- return {
11291
- id: GLOBAL_PROVIDER_ID,
11292
- name: process.env.OPENAI_MODEL || process.env.MODEL || "\u5168\u5C40 LLM",
11293
- type: "env",
11294
- baseURL: process.env.OPENAI_BASE_URL ?? "",
11295
- model: process.env.OPENAI_MODEL || process.env.MODEL || "",
11296
- apiKey
11297
- };
11298
- }
11299
- function normReasoningEffort(v) {
11300
- return v === "low" || v === "medium" || v === "high" ? v : null;
11301
- }
11302
- function normalizeModelList(v) {
11303
- if (!Array.isArray(v)) return void 0;
11304
- const out = [];
11305
- const seen = /* @__PURE__ */ new Set();
11306
- for (const x of v) {
11307
- const s = String(x ?? "").trim();
11308
- if (!s || seen.has(s)) continue;
11309
- seen.add(s);
11310
- out.push(s);
11311
- if (out.length >= 500) break;
11312
11248
  }
11313
- return out;
11314
- }
11315
- function nodeLlmView(cfg) {
11316
- const g = envProvider();
11317
- const providers = [
11318
- ...g ? [
11319
- {
11320
- id: g.id,
11321
- name: g.name,
11322
- type: g.type,
11323
- baseURL: g.baseURL,
11324
- model: g.model,
11325
- hasApiKey: true,
11326
- global: true
11327
- }
11328
- ] : [],
11329
- ...cfg.providers.map((p) => ({
11330
- id: p.id,
11331
- name: p.name,
11332
- type: p.type ?? "",
11333
- baseURL: p.baseURL,
11334
- model: p.model,
11335
- hasApiKey: Boolean(p.apiKey),
11336
- models: p.models ?? [],
11337
- nonChat: p.nonChat ?? [],
11338
- modelMeta: p.modelMeta ?? [],
11339
- reasoningEffort: p.reasoningEffort ?? null,
11340
- global: false
11341
- }))
11342
- ];
11343
- return {
11344
- defaultProviderId: cfg.defaultProviderId ?? null,
11345
- providers,
11346
- /** 未显式设置默认时,是否由环境变量全局配置兜底 */
11347
- globalIsDefault: !cfg.defaultProviderId && Boolean(g)
11348
- };
11349
- }
11350
- function applyNodeLlmPatch(cfg, raw) {
11351
- const next = { providers: cfg.providers, defaultProviderId: cfg.defaultProviderId };
11352
- if (Array.isArray(raw?.providers)) {
11353
- const old = new Map(cfg.providers.map((p) => [p.id, p]));
11354
- next.providers = raw.providers.filter((x) => Boolean(x) && typeof x === "object").filter((x) => x.global !== true && String(x.id ?? "") !== GLOBAL_PROVIDER_ID).map((x) => {
11355
- const id = String(x.id ?? "").trim();
11356
- if (!id) throw new ApiError(400, "bad_request", "\u4F9B\u5E94\u5546 id \u4E0D\u80FD\u4E3A\u7A7A");
11357
- const prev = old.get(id);
11358
- const apiKey = String(x.apiKey ?? "").trim();
11359
- const models = normalizeModelList(x.models) ?? prev?.models;
11360
- const nonChat = normalizeModelList(x.nonChat) ?? prev?.nonChat;
11361
- const modelMeta = normalizeModelMeta(x.modelMeta) ?? prev?.modelMeta;
11249
+ loadRollup(now) {
11250
+ try {
11251
+ const raw = fs19.readFileSync(this.rollupFile, "utf-8");
11252
+ const parsed = JSON.parse(raw);
11362
11253
  return {
11363
- id,
11364
- name: String(x.name ?? prev?.name ?? id).trim(),
11365
- type: x.type == null ? prev?.type : String(x.type),
11366
- baseURL: String(x.baseURL ?? prev?.baseURL ?? "").trim(),
11367
- model: String(x.model ?? prev?.model ?? "").trim(),
11368
- apiKey: apiKey || prev?.apiKey,
11369
- reasoningEffort: x.reasoningEffort === void 0 ? prev?.reasoningEffort ?? null : normReasoningEffort(x.reasoningEffort),
11370
- ...models ? { models } : {},
11371
- ...nonChat ? { nonChat } : {},
11372
- ...modelMeta ? { modelMeta } : {}
11254
+ schema: USAGE_SCHEMA,
11255
+ since: parsed.since ?? now.toISOString(),
11256
+ legacy: parsed.legacy,
11257
+ total: { ...zeroAgg(), ...parsed.total },
11258
+ byDay: parsed.byDay ?? {},
11259
+ byModel: parsed.byModel ?? {},
11260
+ byProject: parsed.byProject ?? {}
11373
11261
  };
11374
- });
11375
- }
11376
- if (typeof raw?.defaultProviderId === "string") {
11377
- const id = raw.defaultProviderId.trim();
11378
- next.defaultProviderId = id || void 0;
11379
- } else if (raw?.defaultProviderId === null) {
11380
- next.defaultProviderId = void 0;
11381
- }
11382
- return next;
11383
- }
11384
- function findNodeProvider(store, id) {
11385
- if (!id) return null;
11386
- if (id === GLOBAL_PROVIDER_ID) return envProvider();
11387
- return store.loadNodeLlm().providers.find((p) => p.id === id) ?? null;
11388
- }
11389
- function nodeDefaultProvider(store) {
11390
- const cfg = store.loadNodeLlm();
11391
- if (cfg.providers.length === 0) return null;
11392
- return cfg.providers.find((p) => p.id === cfg.defaultProviderId) ?? cfg.providers[0] ?? null;
11393
- }
11394
- function resolveSessionProvider(store, tenant, providerId) {
11395
- const own = tenant.provider ?? {};
11396
- if (providerId) {
11397
- const picked = findNodeProvider(store, providerId);
11398
- if (picked) {
11262
+ } catch {
11399
11263
  return {
11400
- apiKey: picked.apiKey || own.apiKey,
11401
- baseURL: picked.baseURL || own.baseURL,
11402
- model: picked.model,
11403
- providerId,
11404
- reasoningEffort: picked.reasoningEffort ?? null,
11405
- ...picked.modelMeta ? { modelMeta: picked.modelMeta } : {}
11406
- };
11407
- }
11408
- }
11409
- if (own.apiKey) return { ...own, model: tenant.model };
11410
- const def = nodeDefaultProvider(store);
11411
- if (def?.apiKey) {
11412
- return {
11413
- apiKey: def.apiKey,
11414
- baseURL: own.baseURL || def.baseURL,
11415
- model: def.model || tenant.model,
11416
- reasoningEffort: def.reasoningEffort ?? null,
11417
- ...def.modelMeta ? { modelMeta: def.modelMeta } : {}
11418
- };
11419
- }
11420
- return { ...own, model: tenant.model };
11421
- }
11422
-
11423
- // src/server/usage.ts
11424
- init_esm_shims();
11425
- import * as fs20 from "fs";
11426
- import * as path24 from "path";
11427
- var LEDGER_RETENTION_MONTHS = 12;
11428
- var USAGE_SCHEMA = 1;
11429
- var DEFAULT_PRICING = {
11430
- schema: USAGE_SCHEMA,
11431
- base: "USD",
11432
- rate: { CNY: 7.2 },
11433
- vendors: {
11434
- openai: {
11435
- "gpt-4o": { input: 2.5, output: 10, cacheRead: 1.25 },
11436
- "gpt-4o-mini": { input: 0.15, output: 0.6, cacheRead: 0.075 },
11437
- "gpt-4.1": { input: 2, output: 8, cacheRead: 0.5 }
11438
- },
11439
- deepseek: {
11440
- "deepseek-flash": {
11441
- input: 0.27,
11442
- output: 1.1,
11443
- cacheRead: 0.027,
11444
- cacheWrite: 0.27
11445
- },
11446
- "deepseek-reasoner": {
11447
- input: 0.55,
11448
- output: 2.19,
11449
- cacheRead: 0.055,
11450
- cacheWrite: 0.55
11451
- },
11452
- "deepseek-*": {
11453
- input: 0.27,
11454
- output: 1.1,
11455
- cacheRead: 0.027,
11456
- cacheWrite: 0.27
11457
- }
11458
- },
11459
- qwen: {
11460
- "qwen-plus": { input: 0.4, output: 1.2 },
11461
- "qwen-turbo": { input: 0.05, output: 0.2 }
11462
- },
11463
- zhipu: { "glm-4-plus": { input: 0.7, output: 0.7 } },
11464
- moonshot: { "moonshot-v1-8k": { input: 1.68, output: 1.68 } },
11465
- xiaomi: {},
11466
- custom: {},
11467
- env: {}
11468
- },
11469
- fallback: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
11470
- };
11471
- function zeroAgg() {
11472
- return {
11473
- calls: 0,
11474
- input: 0,
11475
- output: 0,
11476
- cacheRead: 0,
11477
- cacheWrite: 0,
11478
- costUsd: 0,
11479
- unpriced: 0
11480
- };
11481
- }
11482
- function addAgg(into, tokens, costUsd) {
11483
- into.calls += 1;
11484
- into.input += tokens.input;
11485
- into.output += tokens.output;
11486
- into.cacheRead += tokens.cacheRead;
11487
- into.cacheWrite += tokens.cacheWrite;
11488
- if (costUsd == null) into.unpriced += 1;
11489
- else into.costUsd += costUsd;
11490
- }
11491
- function utcDay(iso) {
11492
- return iso.slice(0, 10);
11493
- }
11494
- function monthOf(iso) {
11495
- return iso.slice(0, 7);
11496
- }
11497
- function retainedMonths(now = /* @__PURE__ */ new Date()) {
11498
- const out = /* @__PURE__ */ new Set();
11499
- const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
11500
- for (let i = 0; i < LEDGER_RETENTION_MONTHS; i += 1) {
11501
- out.add(d.toISOString().slice(0, 7));
11502
- d.setUTCMonth(d.getUTCMonth() - 1);
11503
- }
11504
- return out;
11505
- }
11506
- function atomicWrite(file, content) {
11507
- fs20.mkdirSync(path24.dirname(file), { recursive: true });
11508
- const tmp = `${file}.tmp`;
11509
- fs20.writeFileSync(tmp, content, "utf-8");
11510
- fs20.renameSync(tmp, file);
11511
- }
11512
- var UsageLedger = class {
11513
- constructor(root) {
11514
- this.root = root;
11515
- this.dir = path24.join(root, "usage");
11516
- }
11517
- root;
11518
- dir;
11519
- rollup;
11520
- pricing;
11521
- rollupDirty = false;
11522
- rollupTimer = null;
11523
- inited = false;
11524
- get ledgerDir() {
11525
- return this.dir;
11526
- }
11527
- ledgerFile(month) {
11528
- return path24.join(this.ledgerDir, `ledger-${month}.jsonl`);
11529
- }
11530
- get rollupFile() {
11531
- return path24.join(this.ledgerDir, "rollup.json");
11532
- }
11533
- get pricingFile() {
11534
- return path24.join(this.ledgerDir, "pricing.json");
11535
- }
11536
- // ===================== 初始化 =====================
11537
- init(now = /* @__PURE__ */ new Date()) {
11538
- if (this.inited) return;
11539
- fs20.mkdirSync(this.ledgerDir, { recursive: true });
11540
- this.pricing = this.loadPricing();
11541
- this.rollup = this.loadRollup(now);
11542
- this.pruneLedgers(now);
11543
- this.inited = true;
11544
- }
11545
- loadPricing() {
11546
- try {
11547
- const raw = fs20.readFileSync(this.pricingFile, "utf-8");
11548
- const parsed = JSON.parse(raw);
11549
- const vendors = {};
11550
- for (const v of /* @__PURE__ */ new Set([
11551
- ...Object.keys(DEFAULT_PRICING.vendors),
11552
- ...Object.keys(parsed.vendors ?? {})
11553
- ])) {
11554
- vendors[v] = {
11555
- ...DEFAULT_PRICING.vendors[v] ?? {},
11556
- ...(parsed.vendors ?? {})[v] ?? {}
11557
- };
11558
- }
11559
- return {
11560
- schema: USAGE_SCHEMA,
11561
- base: "USD",
11562
- rate: { ...DEFAULT_PRICING.rate, ...parsed.rate ?? {} },
11563
- vendors,
11564
- fallback: { ...DEFAULT_PRICING.fallback, ...parsed.fallback ?? {} }
11565
- };
11566
- } catch {
11567
- try {
11568
- atomicWrite(this.pricingFile, JSON.stringify(DEFAULT_PRICING, null, 2));
11569
- } catch {
11570
- }
11571
- return JSON.parse(JSON.stringify(DEFAULT_PRICING));
11572
- }
11573
- }
11574
- loadRollup(now) {
11575
- try {
11576
- const raw = fs20.readFileSync(this.rollupFile, "utf-8");
11577
- const parsed = JSON.parse(raw);
11578
- return {
11579
- schema: USAGE_SCHEMA,
11580
- since: parsed.since ?? now.toISOString(),
11581
- legacy: parsed.legacy,
11582
- total: { ...zeroAgg(), ...parsed.total },
11583
- byDay: parsed.byDay ?? {},
11584
- byModel: parsed.byModel ?? {},
11585
- byProject: parsed.byProject ?? {}
11586
- };
11587
- } catch {
11588
- return {
11589
- schema: USAGE_SCHEMA,
11590
- since: now.toISOString(),
11591
- total: zeroAgg(),
11592
- byDay: {},
11593
- byModel: {},
11594
- byProject: {}
11264
+ schema: USAGE_SCHEMA,
11265
+ since: now.toISOString(),
11266
+ total: zeroAgg(),
11267
+ byDay: {},
11268
+ byModel: {},
11269
+ byProject: {}
11595
11270
  };
11596
11271
  }
11597
11272
  }
@@ -11600,10 +11275,10 @@ var UsageLedger = class {
11600
11275
  const keep = retainedMonths(now);
11601
11276
  let changed = false;
11602
11277
  try {
11603
- for (const f of fs20.readdirSync(this.ledgerDir)) {
11278
+ for (const f of fs19.readdirSync(this.ledgerDir)) {
11604
11279
  const m = /^ledger-(\d{4}-\d{2})\.jsonl$/.exec(f);
11605
11280
  if (m && !keep.has(m[1])) {
11606
- fs20.unlinkSync(path24.join(this.ledgerDir, f));
11281
+ fs19.unlinkSync(path22.join(this.ledgerDir, f));
11607
11282
  }
11608
11283
  }
11609
11284
  } catch {
@@ -11680,7 +11355,7 @@ var UsageLedger = class {
11680
11355
  };
11681
11356
  const line = JSON.stringify(rec) + "\n";
11682
11357
  try {
11683
- fs20.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
11358
+ fs19.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
11684
11359
  } catch (e) {
11685
11360
  console.error(`[usage] \u5199\u8D26\u672C\u5931\u8D25: ${String(e)}`);
11686
11361
  }
@@ -11852,10 +11527,10 @@ var UsageLedger = class {
11852
11527
  const rows = [];
11853
11528
  for (const month of months) {
11854
11529
  const file = this.ledgerFile(month);
11855
- if (!fs20.existsSync(file)) continue;
11530
+ if (!fs19.existsSync(file)) continue;
11856
11531
  let content = "";
11857
11532
  try {
11858
- content = fs20.readFileSync(file, "utf-8");
11533
+ content = fs19.readFileSync(file, "utf-8");
11859
11534
  } catch {
11860
11535
  continue;
11861
11536
  }
@@ -11964,10 +11639,10 @@ var UsageLedger = class {
11964
11639
  const seen = /* @__PURE__ */ new Set();
11965
11640
  for (const month of this.monthsInRange("", "")) {
11966
11641
  const file = this.ledgerFile(month);
11967
- if (!fs20.existsSync(file)) continue;
11642
+ if (!fs19.existsSync(file)) continue;
11968
11643
  let content = "";
11969
11644
  try {
11970
- content = fs20.readFileSync(file, "utf-8");
11645
+ content = fs19.readFileSync(file, "utf-8");
11971
11646
  } catch {
11972
11647
  continue;
11973
11648
  }
@@ -13278,7 +12953,7 @@ data: ${JSON.stringify(entry.data)}
13278
12953
  softThresholdPct: parseRatio(process.env.COMPACTION_SOFT_THRESHOLD_PCT),
13279
12954
  capTokens: parseOptionalPositiveNumber(process.env.COMPACTION_CAP_TOKENS) ?? null,
13280
12955
  // 超预算工具结果按租户/工作区隔离落盘(多租户共用一个状态目录会混内容)
13281
- toolResultDir: path25.join(workspace, ".agent", "tool-results")
12956
+ toolResultDir: path23.join(workspace, ".agent", "tool-results")
13282
12957
  });
13283
12958
  engine.setApprover(this._makeApprover(rt));
13284
12959
  const mcpConfigs = loadServerMcpConfigs(tenant);
@@ -13452,132 +13127,912 @@ var ApiError = class extends Error {
13452
13127
  this.status = status;
13453
13128
  this.code = code;
13454
13129
  }
13455
- status;
13456
- code;
13457
- };
13458
-
13459
- // src/server/routes.ts
13460
- init_esm_shims();
13461
- import { createServer } from "http";
13462
- import * as path30 from "path";
13130
+ status;
13131
+ code;
13132
+ };
13133
+
13134
+ // src/server/projects.ts
13135
+ var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
13136
+ function toProjectSummary(store, tenant) {
13137
+ return {
13138
+ id: tenant.id,
13139
+ name: tenant.name ?? tenant.id,
13140
+ model: tenant.model,
13141
+ baseURL: tenant.provider?.baseURL ?? "",
13142
+ hasApiKey: Boolean(tenant.provider?.apiKey),
13143
+ workspaceRoot: tenant.workspaceRoot ?? null,
13144
+ sessionCount: store.listSessions(tenant.id).length
13145
+ };
13146
+ }
13147
+ function providerViews(tenant) {
13148
+ return {
13149
+ activeProviderId: tenant.activeProviderId ?? null,
13150
+ providers: (tenant.llmProviders ?? []).map((p) => ({
13151
+ id: p.id,
13152
+ name: p.name,
13153
+ type: p.type ?? "",
13154
+ baseURL: p.baseURL,
13155
+ model: p.model,
13156
+ models: p.models ?? [],
13157
+ modelMeta: p.modelMeta ?? [],
13158
+ hasApiKey: Boolean(p.apiKey)
13159
+ }))
13160
+ };
13161
+ }
13162
+ function applyProvidersPatch(tenant, raw) {
13163
+ if (Array.isArray(raw.providers)) {
13164
+ const existing = new Map((tenant.llmProviders ?? []).map((p) => [p.id, p]));
13165
+ const next = [];
13166
+ for (const item of raw.providers) {
13167
+ if (!item || typeof item !== "object") continue;
13168
+ const o = item;
13169
+ const id = String(o.id ?? "").trim() || newId();
13170
+ const prev = existing.get(id);
13171
+ const apiKey = typeof o.apiKey === "string" && o.apiKey.trim() ? o.apiKey.trim() : prev?.apiKey;
13172
+ const models = normalizeModelList(o.models) ?? prev?.models;
13173
+ const modelMeta = normalizeModelMeta(o.modelMeta) ?? prev?.modelMeta;
13174
+ next.push({
13175
+ id,
13176
+ name: String(o.name ?? "").trim() || id,
13177
+ type: typeof o.type === "string" ? o.type : prev?.type,
13178
+ baseURL: String(o.baseURL ?? "").trim(),
13179
+ model: String(o.model ?? "").trim(),
13180
+ ...apiKey ? { apiKey } : {},
13181
+ ...models ? { models } : {},
13182
+ ...modelMeta ? { modelMeta } : {}
13183
+ });
13184
+ }
13185
+ tenant.llmProviders = next;
13186
+ }
13187
+ if (typeof raw.activeProviderId === "string") {
13188
+ const activeId = raw.activeProviderId.trim();
13189
+ tenant.activeProviderId = activeId || void 0;
13190
+ const entry = (tenant.llmProviders ?? []).find((p) => p.id === activeId);
13191
+ if (entry) {
13192
+ tenant.provider = tenant.provider ?? {};
13193
+ tenant.provider.baseURL = entry.baseURL || void 0;
13194
+ if (entry.apiKey) tenant.provider.apiKey = entry.apiKey;
13195
+ if (entry.model) tenant.model = entry.model;
13196
+ }
13197
+ }
13198
+ }
13199
+ function allProjectSummaries(store) {
13200
+ return store.listTenants().map((id) => store.loadTenant(id)).filter((t) => t !== null).map((t) => toProjectSummary(store, t));
13201
+ }
13202
+ function lastUserMessage(rec) {
13203
+ const sent = (rec.lastInput ?? "").trim();
13204
+ if (sent) {
13205
+ return sent.length > LAST_USER_MESSAGE_MAX ? `${sent.slice(0, LAST_USER_MESSAGE_MAX)}\u2026` : sent;
13206
+ }
13207
+ const msgs = rec.messages;
13208
+ for (let i = msgs.length - 1; i >= 0; i--) {
13209
+ const m = msgs[i];
13210
+ if (!m || m.role !== "user" || typeof m.content !== "string") continue;
13211
+ const text = m.content.trim();
13212
+ if (!text) continue;
13213
+ return text.length > LAST_USER_MESSAGE_MAX ? `${text.slice(0, LAST_USER_MESSAGE_MAX)}\u2026` : text;
13214
+ }
13215
+ return null;
13216
+ }
13217
+ function lastCompletedAt(rec) {
13218
+ if (rec.lastCompletedAt) return rec.lastCompletedAt;
13219
+ if (rec.status !== "completed") return null;
13220
+ const msgs = rec.messages;
13221
+ for (let i = msgs.length - 1; i >= 0; i--) {
13222
+ const m = msgs[i];
13223
+ if (!m || m.role !== "assistant") continue;
13224
+ if (typeof m.ts === "number" && Number.isFinite(m.ts)) {
13225
+ return new Date(m.ts).toISOString();
13226
+ }
13227
+ return null;
13228
+ }
13229
+ return null;
13230
+ }
13231
+ function allSessionStates(store, manager) {
13232
+ const out = [];
13233
+ for (const id of store.listTenants()) {
13234
+ const t = store.loadTenant(id);
13235
+ if (!t) continue;
13236
+ try {
13237
+ for (const s of manager.listSessions(t.id)) {
13238
+ out.push({
13239
+ projectId: t.id,
13240
+ projectName: t.name ?? t.id,
13241
+ sessionId: s.sessionId,
13242
+ name: s.name ?? null,
13243
+ status: s.status,
13244
+ lastUserMessage: lastUserMessage(s),
13245
+ lastCompletedAt: lastCompletedAt(s)
13246
+ });
13247
+ }
13248
+ } catch {
13249
+ }
13250
+ }
13251
+ return out;
13252
+ }
13253
+ function ensureNodeId(store) {
13254
+ let id = store.loadNodeId();
13255
+ if (!id) {
13256
+ id = newId();
13257
+ store.saveNodeId(id);
13258
+ }
13259
+ return id;
13260
+ }
13261
+ function createProject(store, input) {
13262
+ const raw = input ?? {};
13263
+ const id = String(raw.id ?? "").trim() || newId();
13264
+ if (store.loadTenant(id)) {
13265
+ throw new ApiError(409, "project_exists", `\u9879\u76EE ${id} \u5DF2\u5B58\u5728`);
13266
+ }
13267
+ const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path24.resolve(raw.workspaceRoot.trim()) : void 0;
13268
+ const explicitName = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : "";
13269
+ const defaultName = workspaceRoot ? path24.basename(workspaceRoot) : id;
13270
+ const tenant = {
13271
+ id,
13272
+ name: explicitName || defaultName,
13273
+ apiKey: generateApiKey(),
13274
+ model: typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : DEFAULT_MODEL(),
13275
+ provider: {
13276
+ apiKey: typeof raw.providerApiKey === "string" && raw.providerApiKey.trim() ? raw.providerApiKey.trim() : void 0,
13277
+ baseURL: typeof raw.baseURL === "string" && raw.baseURL.trim() ? raw.baseURL.trim() : void 0
13278
+ },
13279
+ workspaceRoot,
13280
+ allowedCommands: Array.isArray(raw.allowedCommands) ? raw.allowedCommands.map((c) => String(c)) : []
13281
+ };
13282
+ const def = nodeDefaultProvider(store);
13283
+ if (def) {
13284
+ if (!tenant.provider?.apiKey && def.apiKey) tenant.provider = { ...tenant.provider, apiKey: def.apiKey };
13285
+ if (!tenant.provider?.baseURL && def.baseURL) tenant.provider = { ...tenant.provider, baseURL: def.baseURL };
13286
+ if (!(typeof raw.model === "string" && raw.model.trim()) && def.model) tenant.model = def.model;
13287
+ const nodeCfg = store.loadNodeLlm();
13288
+ tenant.llmProviders = nodeCfg.providers.map((p) => ({ ...p }));
13289
+ tenant.activeProviderId = nodeCfg.defaultProviderId ?? nodeCfg.providers[0]?.id;
13290
+ }
13291
+ store.saveTenant(tenant);
13292
+ return tenant;
13293
+ }
13294
+ function buildNodeInfo(store, opts) {
13295
+ return {
13296
+ nodeId: opts.nodeId ?? ensureNodeId(store),
13297
+ platform: process.platform,
13298
+ mode: process.env.NODE_MODE ?? "standalone",
13299
+ version: "0.2.12",
13300
+ admin: opts.admin,
13301
+ projects: opts.projects
13302
+ };
13303
+ }
13304
+
13305
+ // src/server/fleet-config.ts
13306
+ init_esm_shims();
13307
+ function fleetConfigFromEnv(store) {
13308
+ const fleetToken = process.env.FLEET_TOKEN;
13309
+ const fleetSecret = process.env.FLEET_SECRET;
13310
+ if (!fleetToken || !fleetSecret) return null;
13311
+ return {
13312
+ hubUrl: process.env.HUB_URL,
13313
+ fleetToken,
13314
+ fleetSecret,
13315
+ nodeId: ensureNodeId(store),
13316
+ gatewayUrl: process.env.FLEET_GATEWAY_URL
13317
+ };
13318
+ }
13319
+ function resolveFleetConfig(store) {
13320
+ const saved = store.loadFleetCredential();
13321
+ if (saved?.fleetToken && saved.fleetSecret) {
13322
+ return { ...saved, nodeId: saved.nodeId || ensureNodeId(store) };
13323
+ }
13324
+ return fleetConfigFromEnv(store);
13325
+ }
13326
+
13327
+ // src/cli/account-enroll.ts
13328
+ var SKIP_ANSWERS = /* @__PURE__ */ new Set(["-", "skip", "none", "no"]);
13329
+ function resolveDefaultHub(hubUrl, env3 = process.env) {
13330
+ return (hubUrl ?? env3.HUB_URL ?? DEFAULT_HUB_URL).trim().replace(/\/$/, "");
13331
+ }
13332
+ function parseHubAnswer(answer, fallback) {
13333
+ const a = answer.trim();
13334
+ if (SKIP_ANSWERS.has(a.toLowerCase())) return null;
13335
+ return (a || fallback).replace(/\/$/, "");
13336
+ }
13337
+ async function promptHubUrl(rl2, hubUrl) {
13338
+ const fallback = resolveDefaultHub(hubUrl);
13339
+ const answer = await rl2.question(`Hub \u5730\u5740 [${fallback}]\uFF1A`);
13340
+ return parseHubAnswer(answer, fallback);
13341
+ }
13342
+ async function promptAccountLogin(rl2, hubUrl) {
13343
+ cliLog("\u767B\u5F55\u65B9\u5F0F\uFF1A1) \u90AE\u7BB1 + \u5BC6\u7801 2) \u624B\u673A\u53F7 + \u5BC6\u7801 3) \u624B\u673A\u53F7 + \u77ED\u4FE1\u9A8C\u8BC1\u7801");
13344
+ const mode = (await rl2.question("\u8BF7\u9009\u62E9 [1]\uFF1A")).trim() || "1";
13345
+ const input = {};
13346
+ if (mode === "1") {
13347
+ input.email = (await rl2.question("\u90AE\u7BB1\uFF1A")).trim();
13348
+ input.password = await askHidden(rl2, "\u5BC6\u7801\uFF1A");
13349
+ if (!input.email || !input.password) {
13350
+ cliErr("\u90AE\u7BB1\u4E0E\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
13351
+ return null;
13352
+ }
13353
+ } else if (mode === "2") {
13354
+ input.phone = (await rl2.question("\u624B\u673A\u53F7\uFF1A")).trim();
13355
+ input.password = await askHidden(rl2, "\u5BC6\u7801\uFF1A");
13356
+ if (!input.phone || !input.password) {
13357
+ cliErr("\u624B\u673A\u53F7\u4E0E\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
13358
+ return null;
13359
+ }
13360
+ } else if (mode === "3") {
13361
+ input.phone = (await rl2.question("\u624B\u673A\u53F7\uFF1A")).trim();
13362
+ if (!input.phone) {
13363
+ cliErr("\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
13364
+ return null;
13365
+ }
13366
+ try {
13367
+ const sent = await hubSendSms(resolveDefaultHub(hubUrl), {
13368
+ phone: input.phone,
13369
+ purpose: "login"
13370
+ });
13371
+ if (sent.debugCode) cliLog(`\uFF08\u8C03\u8BD5\u6A21\u5F0F\u9A8C\u8BC1\u7801\uFF1A${sent.debugCode}\uFF09`);
13372
+ else cliLog("\u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0C5 \u5206\u949F\u5185\u6709\u6548\u3002");
13373
+ } catch (e) {
13374
+ cliErr(`\u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
13375
+ cliLog(" \u53EF\u6539\u7528\u65B9\u5F0F 1/2\uFF08\u5BC6\u7801\u767B\u5F55\uFF09\uFF0C\u6216\u5728\u7F51\u9875\u7AEF\u751F\u6210\u63A5\u5165\u7801\u540E\u6267\u884C\uFF1A");
13376
+ cliLog(" zqagent link --hub <Hub\u5730\u5740> --code <\u63A5\u5165\u7801>");
13377
+ return null;
13378
+ }
13379
+ input.code = (await rl2.question("\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF1A")).trim();
13380
+ if (!input.code) {
13381
+ cliErr("\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
13382
+ return null;
13383
+ }
13384
+ } else {
13385
+ cliErr("\u672A\u77E5\u7684\u767B\u5F55\u65B9\u5F0F\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
13386
+ return null;
13387
+ }
13388
+ return input;
13389
+ }
13390
+ async function promptLinkAccount(rl2, hubUrl, opts = {}) {
13391
+ const fallback = resolveDefaultHub(hubUrl);
13392
+ let resolved = fallback;
13393
+ if (opts.askHub !== false) {
13394
+ const answer = await promptHubUrl(rl2, fallback);
13395
+ if (!answer) {
13396
+ cliLog("\u5DF2\u8DF3\u8FC7\u63A5\u5165\uFF1A\u8282\u70B9\u4FDD\u6301\u7EAF\u672C\u5730\u8FD0\u884C\u3002");
13397
+ return null;
13398
+ }
13399
+ resolved = answer;
13400
+ }
13401
+ const auth = await promptAccountLogin(rl2, resolved);
13402
+ if (!auth) return null;
13403
+ return { hubUrl: resolved, auth };
13404
+ }
13405
+ async function promptPassword(rl2, prompt = "\u5BC6\u7801\uFF1A") {
13406
+ return askHidden(rl2, prompt);
13407
+ }
13408
+ function resolveDataDir(dataDir) {
13409
+ return path25.resolve(dataDir ?? process.env.DATA_DIR ?? DEFAULT_DATA_DIR);
13410
+ }
13411
+ async function maybeAccountEnroll(opts = {}) {
13412
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return;
13413
+ let store;
13414
+ try {
13415
+ store = new Store(resolveDataDir(opts.dataDir));
13416
+ if (resolveFleetConfig(store)) return;
13417
+ } catch (e) {
13418
+ cliErr(`\u63A5\u5165\u68C0\u67E5\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
13419
+ return;
13420
+ }
13421
+ const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
13422
+ try {
13423
+ await runWizard(rl2, store, opts);
13424
+ } catch (e) {
13425
+ cliErr(`\u63A5\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
13426
+ cliLog(" \u53EF\u7A0D\u540E\u5728\u63A7\u5236\u53F0\u751F\u6210\u63A5\u5165\u7801\uFF0C\u518D\u6267\u884C\uFF1Azqagent link --hub <Hub\u5730\u5740> --code <\u63A5\u5165\u7801>");
13427
+ } finally {
13428
+ rl2.close();
13429
+ }
13430
+ }
13431
+ async function runWizard(rl2, store, opts) {
13432
+ cliLog("");
13433
+ cliLog("\u672C\u673A\u5C1A\u672A\u63A5\u5165 Hub\u3002\u53EF\u7528 Hub \u8D26\u53F7\u63A5\u5165\uFF08\u8282\u70B9\u9ED8\u8BA4\u8FDB\u5165\u300C\u672A\u5206\u7EC4\u300D\uFF09\uFF1B\u8F93\u5165 - \u8DF3\u8FC7\u4FDD\u6301\u7EAF\u672C\u5730\u3002");
13434
+ const prompted = await promptLinkAccount(rl2, opts.hubUrl);
13435
+ if (!prompted) return;
13436
+ const nodeId = ensureNodeId(store);
13437
+ const cred = await hubEnrollByAccount(prompted.hubUrl, {
13438
+ ...prompted.auth,
13439
+ nodeId,
13440
+ label: opts.label
13441
+ });
13442
+ store.saveFleetCredential({
13443
+ ...cred,
13444
+ label: opts.label ?? cred.label,
13445
+ enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
13446
+ });
13447
+ cliLog(`\u2705 \u5DF2\u63A5\u5165 Hub\uFF1A\u8282\u70B9 ${cred.nodeId}\uFF08\u7F51\u5173 ${cred.gatewayUrl ?? "\u672A\u4E0B\u53D1"}\uFF09`);
13448
+ cliLog(" \u51ED\u636E\u5DF2\u4FDD\u5B58\u5230 fleet.json\uFF0C\u672C\u6B21\u542F\u52A8\u5373\u8FDE\u63A5 Hub\u3002");
13449
+ }
13450
+ async function askHidden(rl2, prompt) {
13451
+ if (!process.stdin.isTTY) return (await rl2.question(prompt)).trim();
13452
+ return new Promise((resolve24) => {
13453
+ const saved = process.stdin.listeners("data");
13454
+ process.stdin.removeAllListeners("data");
13455
+ cliWrite(prompt);
13456
+ rl2.pause();
13457
+ process.stdin.setRawMode(true);
13458
+ process.stdin.resume();
13459
+ let buf = "";
13460
+ const cleanup = () => {
13461
+ process.stdin.removeListener("data", onData);
13462
+ for (const listener of saved) {
13463
+ process.stdin.on("data", listener);
13464
+ }
13465
+ process.stdin.setRawMode(true);
13466
+ rl2.resume();
13467
+ };
13468
+ const onData = (chunk) => {
13469
+ const s = chunk.toString().replace(/\x1b\[[0-9;?]*[A-Za-z~]/g, "").replace(/\x1bO[A-Za-z]/g, "");
13470
+ for (const ch of s) {
13471
+ if (ch === "\r" || ch === "\n") {
13472
+ cleanup();
13473
+ cliWrite("\n");
13474
+ resolve24(buf);
13475
+ return;
13476
+ }
13477
+ if (ch === "") {
13478
+ cleanup();
13479
+ cliWrite("\n");
13480
+ resolve24("");
13481
+ return;
13482
+ }
13483
+ if (ch === "\x7F" || ch === "\b") {
13484
+ if (buf.length > 0) {
13485
+ buf = buf.slice(0, -1);
13486
+ cliWrite("\b \b");
13487
+ }
13488
+ } else if (ch >= " ") {
13489
+ buf += ch;
13490
+ cliWrite("*");
13491
+ }
13492
+ }
13493
+ };
13494
+ process.stdin.on("data", onData);
13495
+ });
13496
+ }
13497
+
13498
+ // src/cli/serve-instances.ts
13499
+ init_esm_shims();
13500
+ import { spawnSync as spawnSync3 } from "child_process";
13501
+ import * as fs21 from "fs";
13502
+ import * as os5 from "os";
13503
+ import * as path27 from "path";
13504
+
13505
+ // src/update/platform.ts
13506
+ init_esm_shims();
13507
+ import * as fs20 from "fs";
13508
+ import * as os4 from "os";
13509
+ import * as path26 from "path";
13510
+ import { spawnSync as spawnSync2 } from "child_process";
13511
+ import { createRequire as createRequire3 } from "module";
13512
+ function currentVersion() {
13513
+ const injected = "0.2.12"?.trim();
13514
+ if (injected) return injected;
13515
+ try {
13516
+ const req2 = createRequire3(import.meta.url);
13517
+ const pkg = req2("../../package.json");
13518
+ if (pkg?.version) return pkg.version;
13519
+ } catch {
13520
+ }
13521
+ return "dev";
13522
+ }
13523
+ function isSeaRuntime() {
13524
+ if (process.env.ZQAGENT_SEA === "1") return true;
13525
+ const base = path26.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
13526
+ return base !== "node" && base !== "nodejs";
13527
+ }
13528
+ var DEFAULT_SERVICE_NAME = "zqagent";
13529
+ var DEFAULT_SERVICE_LABEL = "com.zqagent.node";
13530
+ function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
13531
+ const unitName = `${serviceName}.service`;
13532
+ for (const line of cgroup.split("\n")) {
13533
+ const idx = line.lastIndexOf(":");
13534
+ if (idx < 0) continue;
13535
+ const unitPath = line.slice(idx + 1).trim();
13536
+ if (unitPath.split("/").includes(unitName)) return "systemd";
13537
+ }
13538
+ return null;
13539
+ }
13540
+ function launchdDomain() {
13541
+ const uid = typeof process.getuid === "function" ? process.getuid() : -1;
13542
+ return uid === 0 ? "system" : `gui/${uid}`;
13543
+ }
13544
+ function launchdLabel() {
13545
+ return process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL;
13546
+ }
13547
+ function launchdPlistPath() {
13548
+ const label = launchdLabel();
13549
+ const uid = typeof process.getuid === "function" ? process.getuid() : -1;
13550
+ if (uid === 0) return `/Library/LaunchDaemons/${label}.plist`;
13551
+ return path26.join(os4.homedir(), "Library", "LaunchAgents", `${label}.plist`);
13552
+ }
13553
+ function serviceManagerFromLaunchctl(output, pid, execPath, sea = true) {
13554
+ const pidMatch = /^\s*pid = (\d+)\s*$/m.exec(output);
13555
+ if (pidMatch && Number(pidMatch[1]) === pid) return "launchd";
13556
+ const program = /^\s*program = (.+?)\s*$/m.exec(output);
13557
+ if (program) {
13558
+ const shown = program[1].replace(/^"|"$/g, "");
13559
+ if (path26.resolve(shown) === path26.resolve(execPath)) return "launchd";
13560
+ }
13561
+ if (!sea && /^\s*state = /m.test(output)) return "launchd";
13562
+ return null;
13563
+ }
13564
+ function parseLaunchdEnvironment(xml) {
13565
+ const out = {};
13566
+ const marker = xml.indexOf("<key>EnvironmentVariables</key>");
13567
+ if (marker < 0) return out;
13568
+ const open = xml.indexOf("<dict>", marker);
13569
+ if (open < 0) return out;
13570
+ const close = xml.indexOf("</dict>", open);
13571
+ if (close < 0) return out;
13572
+ const body = xml.slice(open + "<dict>".length, close);
13573
+ for (const m of body.matchAll(/<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g)) {
13574
+ out[m[1]] = m[2];
13575
+ }
13576
+ return out;
13577
+ }
13578
+ function detectServiceManager() {
13579
+ if (process.platform === "darwin") return detectLaunchd();
13580
+ if (process.platform !== "linux") return null;
13581
+ const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
13582
+ try {
13583
+ return serviceManagerFromCgroup(
13584
+ fs20.readFileSync("/proc/self/cgroup", "utf8"),
13585
+ serviceName
13586
+ );
13587
+ } catch {
13588
+ return null;
13589
+ }
13590
+ }
13591
+ function detectLaunchd() {
13592
+ try {
13593
+ const res = spawnSync2(
13594
+ "launchctl",
13595
+ ["print", `${launchdDomain()}/${launchdLabel()}`],
13596
+ { encoding: "utf8", timeout: 3e3 }
13597
+ );
13598
+ if (res.status !== 0 || !res.stdout) return null;
13599
+ return serviceManagerFromLaunchctl(
13600
+ res.stdout,
13601
+ process.pid,
13602
+ process.execPath,
13603
+ isSeaRuntime()
13604
+ );
13605
+ } catch {
13606
+ return null;
13607
+ }
13608
+ }
13609
+ function serviceManager() {
13610
+ const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
13611
+ if (v === "systemd" || v === "launchd") return v;
13612
+ return detectServiceManager();
13613
+ }
13614
+ function parseServiceEnvironment(raw) {
13615
+ const out = {};
13616
+ for (const token of raw.trim().split(/\s+/)) {
13617
+ const idx = token.indexOf("=");
13618
+ if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
13619
+ }
13620
+ return out;
13621
+ }
13622
+ var cachedServiceEnv = null;
13623
+ function readServiceEnvironment() {
13624
+ const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
13625
+ try {
13626
+ const res = spawnSync2(
13627
+ "systemctl",
13628
+ ["show", serviceName, "--property=Environment", "--value", "--no-pager"],
13629
+ { encoding: "utf8", timeout: 3e3 }
13630
+ );
13631
+ if (res.status !== 0 || !res.stdout) return {};
13632
+ return parseServiceEnvironment(res.stdout);
13633
+ } catch {
13634
+ return {};
13635
+ }
13636
+ }
13637
+ function serviceEnvValue(key) {
13638
+ if (process.platform === "darwin") {
13639
+ if (serviceManager() !== "launchd") return void 0;
13640
+ cachedLaunchdEnv ??= readLaunchdEnvironment();
13641
+ return cachedLaunchdEnv[key];
13642
+ }
13643
+ if (process.platform !== "linux") return void 0;
13644
+ if (serviceManager() !== "systemd") return void 0;
13645
+ cachedServiceEnv ??= readServiceEnvironment();
13646
+ return cachedServiceEnv[key];
13647
+ }
13648
+ var cachedLaunchdEnv = null;
13649
+ function readLaunchdEnvironment() {
13650
+ try {
13651
+ return parseLaunchdEnvironment(fs20.readFileSync(launchdPlistPath(), "utf8"));
13652
+ } catch {
13653
+ return {};
13654
+ }
13655
+ }
13656
+ function selfUpdateCapability(hasPublicKey) {
13657
+ if (!isSeaRuntime()) {
13658
+ return {
13659
+ ok: false,
13660
+ reason: "\u5F53\u524D\u4E0D\u662F\u5355\u6587\u4EF6\uFF08SEA\uFF09\u5F62\u6001\uFF1A\u8BF7\u7528 npm \u5347\u7EA7\uFF08npm i -g @zqagent/core@latest\uFF09"
13661
+ };
13662
+ }
13663
+ if (!serviceManager()) {
13664
+ return {
13665
+ ok: false,
13666
+ reason: "\u672A\u88AB systemd/launchd \u6258\u7BA1\uFF1A\u8BF7\u7528 deploy/service/install.sh \u5B89\u88C5\uFF0C\u6216\u624B\u52A8\u5347\u7EA7"
13667
+ };
13668
+ }
13669
+ if (!hasPublicKey) {
13670
+ return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
13671
+ }
13672
+ return { ok: true };
13673
+ }
13463
13674
 
13464
- // src/server/projects.ts
13465
- init_esm_shims();
13466
- import * as path26 from "path";
13467
- var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
13468
- function toProjectSummary(store, tenant) {
13469
- return {
13470
- id: tenant.id,
13471
- name: tenant.name ?? tenant.id,
13472
- model: tenant.model,
13473
- baseURL: tenant.provider?.baseURL ?? "",
13474
- hasApiKey: Boolean(tenant.provider?.apiKey),
13475
- workspaceRoot: tenant.workspaceRoot ?? null,
13476
- sessionCount: store.listSessions(tenant.id).length
13675
+ // src/cli/serve-instances.ts
13676
+ var DEFAULT_SERVE_HOST = "127.0.0.1";
13677
+ var DEFAULT_SERVE_PORT = 8787;
13678
+ var DEFAULT_SERVICE_NAME2 = "zqagent";
13679
+ var DEFAULT_SERVICE_LABEL2 = "com.zqagent.node";
13680
+ function serveSubcommandIndex(argv) {
13681
+ for (let i = 1; i < argv.length; i++) {
13682
+ if (argv[i].startsWith("-")) continue;
13683
+ return argv[i] === "serve" ? i : -1;
13684
+ }
13685
+ return -1;
13686
+ }
13687
+ function parseServeArgs(argv) {
13688
+ const at = serveSubcommandIndex(argv);
13689
+ if (at < 0) return null;
13690
+ let dataDir = null;
13691
+ let host = DEFAULT_SERVE_HOST;
13692
+ let port = DEFAULT_SERVE_PORT;
13693
+ for (let i = at + 1; i < argv.length; i++) {
13694
+ const arg = argv[i];
13695
+ if (arg === "--data-dir" && argv[i + 1]) {
13696
+ dataDir = argv[++i];
13697
+ } else if (arg.startsWith("--data-dir=")) {
13698
+ dataDir = arg.slice("--data-dir=".length);
13699
+ } else if (arg === "--host" && argv[i + 1]) {
13700
+ host = argv[++i];
13701
+ } else if (arg.startsWith("--host=")) {
13702
+ host = arg.slice("--host=".length);
13703
+ } else if (arg === "--port" && argv[i + 1]) {
13704
+ port = Number(argv[++i]);
13705
+ } else if (arg.startsWith("--port=")) {
13706
+ port = Number(arg.slice("--port=".length));
13707
+ }
13708
+ }
13709
+ if (!Number.isFinite(port) || port <= 0) port = DEFAULT_SERVE_PORT;
13710
+ if (host === "0.0.0.0" || host === "::" || host === "") host = DEFAULT_SERVE_HOST;
13711
+ return { dataDir, host, port };
13712
+ }
13713
+ function buildHotCandidates(explicitDataDir, discovered, fallback) {
13714
+ const out = [];
13715
+ const push = (inst) => {
13716
+ if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
13477
13717
  };
13718
+ if (explicitDataDir) push({ dataDir: path27.resolve(explicitDataDir), ...fallback });
13719
+ for (const inst of discovered) if (inst.managed) push(inst);
13720
+ for (const inst of discovered) if (!inst.managed) push(inst);
13721
+ if (!explicitDataDir) push({ dataDir: path27.resolve(DEFAULT_DATA_DIR), ...fallback });
13722
+ return out;
13478
13723
  }
13479
- function providerViews(tenant) {
13724
+ function isWindowsAbsolute(p) {
13725
+ return /^[A-Za-z]:[\\/]/.test(p) || /^\\\\/.test(p);
13726
+ }
13727
+ function isAbsolutePath(p) {
13728
+ return path27.isAbsolute(p) || isWindowsAbsolute(p);
13729
+ }
13730
+ function normalizePath(p) {
13731
+ return isWindowsAbsolute(p) ? path27.win32.normalize(p) : path27.normalize(p);
13732
+ }
13733
+ function parseServeCommandLine(line) {
13734
+ if (!/(?:^|\s)["']?serve["']?(?=\s|$)/.test(line)) return null;
13735
+ const m = /--data-dir(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(.+?))(?=\s+--\w|\s*$)/.exec(line);
13736
+ const rawDataDir = (m?.[1] ?? m?.[2] ?? m?.[3] ?? "").trim();
13737
+ if (!rawDataDir || !isAbsolutePath(rawDataDir)) return null;
13738
+ const host = /--host(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(\S+))/.exec(line);
13739
+ const rawHost = (host?.[1] ?? host?.[2] ?? host?.[3] ?? DEFAULT_SERVE_HOST).trim();
13740
+ const portMatch = /--port(?:=|\s+)(\d+)/.exec(line);
13741
+ const port = Number(portMatch?.[1] ?? DEFAULT_SERVE_PORT);
13480
13742
  return {
13481
- activeProviderId: tenant.activeProviderId ?? null,
13482
- providers: (tenant.llmProviders ?? []).map((p) => ({
13483
- id: p.id,
13484
- name: p.name,
13485
- type: p.type ?? "",
13486
- baseURL: p.baseURL,
13487
- model: p.model,
13488
- models: p.models ?? [],
13489
- modelMeta: p.modelMeta ?? [],
13490
- hasApiKey: Boolean(p.apiKey)
13491
- }))
13743
+ dataDir: normalizePath(rawDataDir),
13744
+ host: rawHost === "0.0.0.0" || rawHost === "::" || rawHost === "" ? DEFAULT_SERVE_HOST : rawHost,
13745
+ port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT
13492
13746
  };
13493
13747
  }
13494
- function applyProvidersPatch(tenant, raw) {
13495
- if (Array.isArray(raw.providers)) {
13496
- const existing = new Map((tenant.llmProviders ?? []).map((p) => [p.id, p]));
13497
- const next = [];
13498
- for (const item of raw.providers) {
13499
- if (!item || typeof item !== "object") continue;
13500
- const o = item;
13501
- const id = String(o.id ?? "").trim() || newId();
13502
- const prev = existing.get(id);
13503
- const apiKey = typeof o.apiKey === "string" && o.apiKey.trim() ? o.apiKey.trim() : prev?.apiKey;
13504
- const models = normalizeModelList(o.models) ?? prev?.models;
13505
- const modelMeta = normalizeModelMeta(o.modelMeta) ?? prev?.modelMeta;
13506
- next.push({
13507
- id,
13508
- name: String(o.name ?? "").trim() || id,
13509
- type: typeof o.type === "string" ? o.type : prev?.type,
13510
- baseURL: String(o.baseURL ?? "").trim(),
13511
- model: String(o.model ?? "").trim(),
13512
- ...apiKey ? { apiKey } : {},
13513
- ...models ? { models } : {},
13514
- ...modelMeta ? { modelMeta } : {}
13515
- });
13748
+ function processCwd(pid) {
13749
+ try {
13750
+ return fs21.readlinkSync(`/proc/${pid}/cwd`);
13751
+ } catch {
13752
+ return null;
13753
+ }
13754
+ }
13755
+ function processEnv(pid) {
13756
+ const out = {};
13757
+ try {
13758
+ const raw = fs21.readFileSync(`/proc/${pid}/environ`, "utf-8");
13759
+ for (const entry of raw.split("\0")) {
13760
+ const eq = entry.indexOf("=");
13761
+ if (eq > 0) out[entry.slice(0, eq)] = entry.slice(eq + 1);
13516
13762
  }
13517
- tenant.llmProviders = next;
13763
+ } catch {
13518
13764
  }
13519
- if (typeof raw.activeProviderId === "string") {
13520
- const activeId = raw.activeProviderId.trim();
13521
- tenant.activeProviderId = activeId || void 0;
13522
- const entry = (tenant.llmProviders ?? []).find((p) => p.id === activeId);
13523
- if (entry) {
13524
- tenant.provider = tenant.provider ?? {};
13525
- tenant.provider.baseURL = entry.baseURL || void 0;
13526
- if (entry.apiKey) tenant.provider.apiKey = entry.apiKey;
13527
- if (entry.model) tenant.model = entry.model;
13765
+ return out;
13766
+ }
13767
+ function resolveDataDir2(rawDataDir, cwd, env3) {
13768
+ const pick = rawDataDir ?? env3.DATA_DIR ?? null;
13769
+ if (pick) return path27.isAbsolute(pick) ? path27.normalize(pick) : cwd ? path27.resolve(cwd, pick) : null;
13770
+ return cwd ? path27.resolve(cwd, DEFAULT_DATA_DIR) : null;
13771
+ }
13772
+ function isManagedBySystemd(pid, env3) {
13773
+ if (env3.ZQAGENT_SERVICE_MANAGER?.trim()) return true;
13774
+ try {
13775
+ const cgroup = fs21.readFileSync(`/proc/${pid}/cgroup`, "utf-8");
13776
+ const serviceName = env3.ZQAGENT_SERVICE_NAME?.trim() || "zqagent";
13777
+ return serviceManagerFromCgroup(cgroup, serviceName) !== null;
13778
+ } catch {
13779
+ return false;
13780
+ }
13781
+ }
13782
+ function discoverFromProc() {
13783
+ const out = [];
13784
+ let pids;
13785
+ try {
13786
+ pids = fs21.readdirSync("/proc");
13787
+ } catch {
13788
+ return out;
13789
+ }
13790
+ for (const name of pids) {
13791
+ if (!/^\d+$/.test(name)) continue;
13792
+ const pid = Number(name);
13793
+ if (pid === process.pid) continue;
13794
+ let argv;
13795
+ let env3;
13796
+ try {
13797
+ argv = fs21.readFileSync(`/proc/${pid}/cmdline`, "utf-8").split("\0").filter(Boolean);
13798
+ env3 = processEnv(pid);
13799
+ } catch {
13800
+ continue;
13528
13801
  }
13802
+ const parsed = parseServeArgs(argv);
13803
+ if (!parsed) continue;
13804
+ const dataDir = resolveDataDir2(parsed.dataDir, processCwd(pid), env3);
13805
+ if (!dataDir) continue;
13806
+ out.push({
13807
+ dataDir,
13808
+ host: parsed.host,
13809
+ port: parsed.port,
13810
+ managed: isManagedBySystemd(pid, env3)
13811
+ });
13529
13812
  }
13813
+ return out;
13530
13814
  }
13531
- function allProjectSummaries(store) {
13532
- return store.listTenants().map((id) => store.loadTenant(id)).filter((t) => t !== null).map((t) => toProjectSummary(store, t));
13815
+ function discoverFromPs() {
13816
+ const out = [];
13817
+ const res = spawnSync3("ps", ["-axo", "command="], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
13818
+ if (res.status !== 0 || !res.stdout) return out;
13819
+ for (const line of res.stdout.split("\n")) {
13820
+ const inst = parseServeCommandLine(line);
13821
+ if (inst) out.push(inst);
13822
+ }
13823
+ return out;
13533
13824
  }
13534
- function lastUserMessage(rec) {
13535
- const sent = (rec.lastInput ?? "").trim();
13536
- if (sent) {
13537
- return sent.length > LAST_USER_MESSAGE_MAX ? `${sent.slice(0, LAST_USER_MESSAGE_MAX)}\u2026` : sent;
13825
+ function discoverFromWindows() {
13826
+ const out = [];
13827
+ const script = `[Console]::OutputEncoding=[Text.Encoding]::UTF8; Get-CimInstance Win32_Process -Filter "Name='zqagent.exe' OR Name='node.exe'" | ForEach-Object { $_.CommandLine }`;
13828
+ const res = spawnSync3(
13829
+ "powershell.exe",
13830
+ ["-NoProfile", "-NonInteractive", "-Command", script],
13831
+ { encoding: "utf-8", timeout: 15e3, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
13832
+ );
13833
+ if (res.status !== 0 || !res.stdout) return out;
13834
+ for (const line of res.stdout.replace(/^\uFEFF/, "").split(/\r?\n/)) {
13835
+ const inst = parseServeCommandLine(line);
13836
+ if (inst) out.push(inst);
13538
13837
  }
13539
- const msgs = rec.messages;
13540
- for (let i = msgs.length - 1; i >= 0; i--) {
13541
- const m = msgs[i];
13542
- if (!m || m.role !== "user" || typeof m.content !== "string") continue;
13543
- const text = m.content.trim();
13544
- if (!text) continue;
13545
- return text.length > LAST_USER_MESSAGE_MAX ? `${text.slice(0, LAST_USER_MESSAGE_MAX)}\u2026` : text;
13838
+ return out;
13839
+ }
13840
+ function decodeXmlEntities(s) {
13841
+ return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
13842
+ }
13843
+ function parseLaunchdProgramArguments(xml) {
13844
+ const marker = xml.indexOf("<key>ProgramArguments</key>");
13845
+ if (marker < 0) return [];
13846
+ const open = xml.indexOf("<array>", marker);
13847
+ if (open < 0) return [];
13848
+ const close = xml.indexOf("</array>", open);
13849
+ if (close < 0) return [];
13850
+ return [...xml.slice(open + "<array>".length, close).matchAll(/<string>([^<]*)<\/string>/g)].map(
13851
+ (m) => decodeXmlEntities(m[1])
13852
+ );
13853
+ }
13854
+ function launchdServiceInstances() {
13855
+ if (process.platform !== "darwin") return [];
13856
+ const label = process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL2;
13857
+ const plists = [
13858
+ `/Library/LaunchDaemons/${label}.plist`,
13859
+ path27.join(os5.homedir(), "Library", "LaunchAgents", `${label}.plist`)
13860
+ ];
13861
+ const out = [];
13862
+ for (const plist of plists) {
13863
+ let xml;
13864
+ try {
13865
+ xml = fs21.readFileSync(plist, "utf-8");
13866
+ } catch {
13867
+ continue;
13868
+ }
13869
+ const parsed = parseServeArgs(parseLaunchdProgramArguments(xml));
13870
+ if (!parsed?.dataDir || !isAbsolutePath(parsed.dataDir)) continue;
13871
+ out.push({
13872
+ dataDir: normalizePath(parsed.dataDir),
13873
+ host: parsed.host,
13874
+ port: parsed.port,
13875
+ managed: true
13876
+ });
13546
13877
  }
13547
- return null;
13878
+ return out;
13548
13879
  }
13549
- function lastCompletedAt(rec) {
13550
- if (rec.lastCompletedAt) return rec.lastCompletedAt;
13551
- if (rec.status !== "completed") return null;
13552
- const msgs = rec.messages;
13553
- for (let i = msgs.length - 1; i >= 0; i--) {
13554
- const m = msgs[i];
13555
- if (!m || m.role !== "assistant") continue;
13556
- if (typeof m.ts === "number" && Number.isFinite(m.ts)) {
13557
- return new Date(m.ts).toISOString();
13880
+ function parseSystemdUnit(text) {
13881
+ let dataDir = null;
13882
+ let host = DEFAULT_SERVE_HOST;
13883
+ let port = DEFAULT_SERVE_PORT;
13884
+ for (const line of text.split("\n")) {
13885
+ const trimmed = line.trim();
13886
+ if (trimmed.startsWith("WorkingDirectory=")) {
13887
+ const v = trimmed.slice("WorkingDirectory=".length).trim();
13888
+ if (v && isAbsolutePath(v)) dataDir = v;
13889
+ } else if (trimmed.startsWith("ExecStart=")) {
13890
+ const tokens = trimmed.slice("ExecStart=".length).trim().split(/\s+/).map((t) => t.replace(/^["']|["']$/g, ""));
13891
+ const parsed = parseServeArgs(tokens);
13892
+ if (!parsed) continue;
13893
+ if (parsed.dataDir && isAbsolutePath(parsed.dataDir)) dataDir = parsed.dataDir;
13894
+ host = parsed.host;
13895
+ port = parsed.port;
13896
+ }
13897
+ }
13898
+ if (!dataDir) return null;
13899
+ return { dataDir: normalizePath(dataDir), host, port, managed: true };
13900
+ }
13901
+ function systemdServiceInstances() {
13902
+ if (process.platform !== "linux") return [];
13903
+ const unit = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME2;
13904
+ const configHome = process.env.XDG_CONFIG_HOME?.trim() || path27.join(os5.homedir(), ".config");
13905
+ const units = [
13906
+ `/etc/systemd/system/${unit}.service`,
13907
+ path27.join(configHome, "systemd", "user", `${unit}.service`)
13908
+ ];
13909
+ const out = [];
13910
+ for (const file of units) {
13911
+ let text;
13912
+ try {
13913
+ text = fs21.readFileSync(file, "utf-8");
13914
+ } catch {
13915
+ continue;
13558
13916
  }
13559
- return null;
13917
+ const inst = parseSystemdUnit(text);
13918
+ if (inst) out.push(inst);
13919
+ }
13920
+ return out;
13921
+ }
13922
+ function windowsServiceInstances() {
13923
+ if (process.platform !== "win32") return [];
13924
+ const appdata = process.env.APPDATA?.trim();
13925
+ if (!appdata) return [];
13926
+ const base = path27.join(appdata, "zqagent");
13927
+ try {
13928
+ const raw = fs21.readFileSync(path27.join(base, "runtime", "service.json"), "utf-8");
13929
+ const v = JSON.parse(raw);
13930
+ const dataDir = v.dataDir && isAbsolutePath(v.dataDir) ? v.dataDir : base;
13931
+ const port = Number(v.port);
13932
+ return [
13933
+ {
13934
+ dataDir: normalizePath(dataDir),
13935
+ host: DEFAULT_SERVE_HOST,
13936
+ port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT,
13937
+ managed: true
13938
+ }
13939
+ ];
13940
+ } catch {
13941
+ return [];
13942
+ }
13943
+ }
13944
+ function mergeServeInstances(discovered, managed) {
13945
+ const out = [];
13946
+ const push = (inst) => {
13947
+ if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
13948
+ };
13949
+ for (const inst of discovered) {
13950
+ const hit = managed.find((m) => m.dataDir === inst.dataDir);
13951
+ push(hit ? { ...inst, managed: true } : inst);
13560
13952
  }
13561
- return null;
13953
+ for (const inst of managed) push(inst);
13954
+ return out;
13562
13955
  }
13563
- function allSessionStates(store, manager) {
13564
- const out = [];
13565
- for (const id of store.listTenants()) {
13566
- const t = store.loadTenant(id);
13567
- if (!t) continue;
13568
- try {
13569
- for (const s of manager.listSessions(t.id)) {
13570
- out.push({
13571
- projectId: t.id,
13572
- projectName: t.name ?? t.id,
13573
- sessionId: s.sessionId,
13574
- name: s.name ?? null,
13575
- status: s.status,
13576
- lastUserMessage: lastUserMessage(s),
13577
- lastCompletedAt: lastCompletedAt(s)
13578
- });
13579
- }
13580
- } catch {
13956
+ function discoverRunningServeInstances() {
13957
+ const found = process.platform === "linux" ? discoverFromProc() : process.platform === "win32" ? discoverFromWindows() : discoverFromPs();
13958
+ const declared = process.platform === "linux" ? systemdServiceInstances() : process.platform === "win32" ? windowsServiceInstances() : launchdServiceInstances();
13959
+ return mergeServeInstances(found, declared);
13960
+ }
13961
+
13962
+ // src/cli/link.ts
13963
+ function expandHome(p) {
13964
+ const home = process.env.HOME || process.env.USERPROFILE;
13965
+ if (!home) return p;
13966
+ if (p === "~") return home;
13967
+ if (p.startsWith("~/") || p.startsWith("~\\")) return path28.join(home, p.slice(2));
13968
+ return p;
13969
+ }
13970
+ function installDataDirHint() {
13971
+ if (process.platform === "darwin") return "$HOME/Library/Application Support/zqagent";
13972
+ if (process.platform === "win32") return "%APPDATA%\\zqagent";
13973
+ return "/var/lib/zqagent\uFF08\u9700 root\uFF1B\u666E\u901A\u7528\u6237\u7528 ~/.zqagent_data\uFF09";
13974
+ }
13975
+ function buildAccountAuth(flags, env3 = process.env) {
13976
+ const username = (flags.username ?? env3.ZQAGENT_HUB_USERNAME ?? "").trim();
13977
+ let email = (flags.email ?? "").trim();
13978
+ let phone = (flags.phone ?? "").trim();
13979
+ if (username && !email && !phone) {
13980
+ if (username.includes("@")) email = username;
13981
+ else phone = username;
13982
+ }
13983
+ if (!email && !phone) return null;
13984
+ const password = flags.password ?? env3.ZQAGENT_HUB_PASSWORD ?? "";
13985
+ const smsCode = (flags.smsCode ?? env3.ZQAGENT_HUB_SMS_CODE ?? "").trim();
13986
+ return {
13987
+ email: email ? email.toLowerCase() : void 0,
13988
+ phone: phone || void 0,
13989
+ password: password || void 0,
13990
+ smsCode: smsCode || void 0
13991
+ };
13992
+ }
13993
+ function isCompleteAccount(account) {
13994
+ return Boolean(account && (account.password || account.smsCode));
13995
+ }
13996
+ function parseLinkArgs(argv) {
13997
+ const out = {};
13998
+ for (let i = 0; i < argv.length; i++) {
13999
+ const a = argv[i];
14000
+ const val = argv[i + 1];
14001
+ if (a === "--hub") {
14002
+ out.hubUrl = val;
14003
+ i++;
14004
+ } else if (a === "--code") {
14005
+ out.code = val;
14006
+ i++;
14007
+ } else if (a === "--username" || a === "--user") {
14008
+ out.username = val;
14009
+ i++;
14010
+ } else if (a === "--email") {
14011
+ out.email = val;
14012
+ i++;
14013
+ } else if (a === "--phone") {
14014
+ out.phone = val;
14015
+ i++;
14016
+ } else if (a === "--password") {
14017
+ out.password = val;
14018
+ i++;
14019
+ } else if (a === "--sms-code") {
14020
+ out.smsCode = val;
14021
+ i++;
14022
+ } else if (a === "--data-dir") {
14023
+ out.dataDir = val;
14024
+ i++;
14025
+ } else if (a === "--label") {
14026
+ out.label = val;
14027
+ i++;
14028
+ } else if (a === "--host") {
14029
+ out.host = val;
14030
+ i++;
14031
+ } else if (a === "--port") {
14032
+ out.port = Number(val);
14033
+ i++;
14034
+ } else if (a === "-h" || a === "--help") {
14035
+ out.help = true;
13581
14036
  }
13582
14037
  }
13583
14038
  return out;
@@ -13590,50 +14045,285 @@ function ensureNodeId2(store) {
13590
14045
  }
13591
14046
  return id;
13592
14047
  }
13593
- function createProject(store, input) {
13594
- const raw = input ?? {};
13595
- const id = String(raw.id ?? "").trim() || newId();
13596
- if (store.loadTenant(id)) {
13597
- throw new ApiError(409, "project_exists", `\u9879\u76EE ${id} \u5DF2\u5B58\u5728`);
14048
+ async function probeServer(host, port, adminKey) {
14049
+ const ctrl = new AbortController();
14050
+ const timer = setTimeout(() => ctrl.abort(), 1500);
14051
+ try {
14052
+ const res = await fetch(`http://${host}:${port}/api/v1/fleet/status`, {
14053
+ headers: { Authorization: `Bearer ${adminKey}` },
14054
+ signal: ctrl.signal
14055
+ });
14056
+ return res.ok;
14057
+ } catch {
14058
+ return false;
14059
+ } finally {
14060
+ clearTimeout(timer);
13598
14061
  }
13599
- const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path26.resolve(raw.workspaceRoot.trim()) : void 0;
13600
- const explicitName = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : "";
13601
- const defaultName = workspaceRoot ? path26.basename(workspaceRoot) : id;
13602
- const tenant = {
13603
- id,
13604
- name: explicitName || defaultName,
13605
- apiKey: generateApiKey(),
13606
- model: typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : DEFAULT_MODEL(),
13607
- provider: {
13608
- apiKey: typeof raw.providerApiKey === "string" && raw.providerApiKey.trim() ? raw.providerApiKey.trim() : void 0,
13609
- baseURL: typeof raw.baseURL === "string" && raw.baseURL.trim() ? raw.baseURL.trim() : void 0
14062
+ }
14063
+ async function enrollViaServer(host, port, adminKey, opts) {
14064
+ const res = await fetch(`http://${host}:${port}/api/v1/fleet/enroll`, {
14065
+ method: "POST",
14066
+ headers: {
14067
+ "Content-Type": "application/json",
14068
+ Authorization: `Bearer ${adminKey}`
13610
14069
  },
13611
- workspaceRoot,
13612
- allowedCommands: Array.isArray(raw.allowedCommands) ? raw.allowedCommands.map((c) => String(c)) : []
13613
- };
13614
- const def = nodeDefaultProvider(store);
13615
- if (def) {
13616
- if (!tenant.provider?.apiKey && def.apiKey) tenant.provider = { ...tenant.provider, apiKey: def.apiKey };
13617
- if (!tenant.provider?.baseURL && def.baseURL) tenant.provider = { ...tenant.provider, baseURL: def.baseURL };
13618
- if (!(typeof raw.model === "string" && raw.model.trim()) && def.model) tenant.model = def.model;
13619
- const nodeCfg = store.loadNodeLlm();
13620
- tenant.llmProviders = nodeCfg.providers.map((p) => ({ ...p }));
13621
- tenant.activeProviderId = nodeCfg.defaultProviderId ?? nodeCfg.providers[0]?.id;
14070
+ body: JSON.stringify({
14071
+ hubUrl: opts.hubUrl,
14072
+ code: opts.code,
14073
+ email: opts.account?.email,
14074
+ phone: opts.account?.phone,
14075
+ password: opts.account?.password,
14076
+ smsCode: opts.account?.smsCode,
14077
+ label: opts.label
14078
+ })
14079
+ });
14080
+ const text = await res.text();
14081
+ let payload = null;
14082
+ try {
14083
+ payload = text ? JSON.parse(text) : null;
14084
+ } catch {
13622
14085
  }
13623
- store.saveTenant(tenant);
13624
- return tenant;
14086
+ if (!res.ok) {
14087
+ throw new Error(payload?.error?.message ?? `HTTP ${res.status}`);
14088
+ }
14089
+ return payload?.data;
13625
14090
  }
13626
- function buildNodeInfo(store, opts) {
14091
+ function hotWarnings(inst, running) {
14092
+ const warnings = [];
14093
+ const managed = running.filter((r) => r.managed && r.dataDir !== inst.dataDir);
14094
+ if (!inst.managed && managed.length > 0) {
14095
+ warnings.push(
14096
+ `\u51ED\u636E\u5199\u5165\u4E86\u975E\u6258\u7BA1\u5B9E\u4F8B ${inst.dataDir}\uFF0C\u4F46\u672C\u673A\u6258\u7BA1\u670D\u52A1\u4F7F\u7528\u7684\u662F ${managed.map((m) => m.dataDir).join("\u3001")}\u3002`
14097
+ );
14098
+ warnings.push(
14099
+ "\u6258\u7BA1\u670D\u52A1\u8BFB\u4E0D\u5230\u8FD9\u4EFD\u51ED\u636E\uFF08\u91CD\u542F\u540E\u4E5F\u4E0D\u4F1A\u81EA\u52A8\u4E0A\u7EBF\uFF09\uFF0C\u8BF7\u7528 --data-dir \u6307\u5411\u5B83\u7684\u6570\u636E\u76EE\u5F55\u91CD\u65B0\u63A5\u5165\u3002"
14100
+ );
14101
+ }
14102
+ const others = [...new Set(running.map((r) => r.dataDir).filter((d) => d !== inst.dataDir))];
14103
+ if (others.length > 0) {
14104
+ warnings.push(`\u672C\u673A\u8FD8\u6709\u5176\u5B83\u8FD0\u884C\u4E2D\u7684\u8282\u70B9\u5B9E\u4F8B\uFF08\u672A\u5199\u5165\u51ED\u636E\uFF09\uFF1A${others.join("\u3001")}\u3002`);
14105
+ }
14106
+ return warnings;
14107
+ }
14108
+ async function linkNode(opts) {
14109
+ if (!opts.code && !isCompleteAccount(opts.account ?? null)) {
14110
+ throw new Error("\u7F3A\u5C11\u63A5\u5165\u7801\uFF0C\u6216 Hub \u8D26\u53F7\u5BC6\u7801/\u77ED\u4FE1\u9A8C\u8BC1\u7801");
14111
+ }
14112
+ const hubUrl = opts.hubUrl.trim().replace(/\/$/, "");
14113
+ if (!hubUrl) throw new Error("\u7F3A\u5C11 Hub \u5730\u5740");
14114
+ const rawDataDir = opts.dataDir?.trim() || process.env.DATA_DIR?.trim() || void 0;
14115
+ const explicitDataDir = rawDataDir ? expandHome(rawDataDir) : void 0;
14116
+ const host = opts.host?.trim() || process.env.HOST?.trim() || DEFAULT_SERVE_HOST;
14117
+ const port = opts.port ?? Number(process.env.PORT ?? DEFAULT_SERVE_PORT);
14118
+ const running = discoverRunningServeInstances();
14119
+ const candidates = buildHotCandidates(explicitDataDir, running, { host, port });
14120
+ for (const inst of candidates) {
14121
+ const store2 = new Store(inst.dataDir);
14122
+ const adminKey = process.env.ADMIN_API_KEY ?? store2.loadAdminKey();
14123
+ if (!adminKey) continue;
14124
+ if (!await probeServer(inst.host, inst.port, adminKey)) continue;
14125
+ const status = await enrollViaServer(inst.host, inst.port, adminKey, { ...opts, hubUrl });
14126
+ return {
14127
+ mode: "hot",
14128
+ ...status,
14129
+ dataDir: inst.dataDir,
14130
+ managed: inst.managed,
14131
+ warnings: hotWarnings(inst, running)
14132
+ };
14133
+ }
14134
+ const managedDir = running.find((r) => r.managed)?.dataDir;
14135
+ const dataDir = path28.resolve(explicitDataDir ?? managedDir ?? DEFAULT_DATA_DIR);
14136
+ const targetedService = !explicitDataDir && Boolean(managedDir) && dataDir === managedDir;
14137
+ const store = new Store(dataDir);
14138
+ const nodeId = ensureNodeId2(store);
14139
+ const account = opts.account;
14140
+ const cred = account && isCompleteAccount(account) ? await hubEnrollByAccount(hubUrl, {
14141
+ email: account.email,
14142
+ phone: account.phone,
14143
+ password: account.password,
14144
+ code: account.smsCode,
14145
+ nodeId,
14146
+ label: opts.label
14147
+ }) : await hubEnrollByCode(hubUrl, {
14148
+ code: opts.code,
14149
+ nodeId,
14150
+ label: opts.label
14151
+ });
14152
+ try {
14153
+ store.saveFleetCredential({
14154
+ ...cred,
14155
+ label: opts.label ?? cred.label,
14156
+ enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
14157
+ });
14158
+ } catch (e) {
14159
+ const err = e;
14160
+ if (err.code === "EACCES" || err.code === "EPERM") {
14161
+ throw new Error(
14162
+ `\u65E0\u6CD5\u5199\u5165\u6570\u636E\u76EE\u5F55 ${dataDir}\uFF08${err.code}: ${err.message}\uFF09
14163
+ \u8BE5\u76EE\u5F55\u5F53\u524D\u7528\u6237\u6CA1\u6709\u5199\u6743\u9650\u3002\u4E24\u79CD\u6539\u6CD5\uFF1A
14164
+ 1) \u53BB\u6389 --data-dir\uFF0C\u8BA9 link \u81EA\u52A8\u53D1\u73B0\u672C\u673A\u8FD0\u884C\u7684\u8282\u70B9\uFF08\u63A8\u8350\uFF09\uFF1B
14165
+ 2) \u7528\u5F53\u524D\u7528\u6237\u53EF\u5199\u7684\u76EE\u5F55\uFF0C\u672C\u5E73\u53F0\u5B89\u88C5\u811A\u672C\u9ED8\u8BA4\uFF1A${installDataDirHint()}
14166
+ \uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF1B\u8D26\u53F7\u65B9\u5F0F\u53EF\u91CD\u590D\u4F7F\u7528\uFF09`
14167
+ );
14168
+ }
14169
+ throw e;
14170
+ }
14171
+ const otherRunningDirs = [
14172
+ ...new Set(running.map((r) => r.dataDir).filter((d) => d !== dataDir))
14173
+ ];
13627
14174
  return {
13628
- nodeId: opts.nodeId ?? ensureNodeId2(store),
13629
- platform: process.platform,
13630
- mode: process.env.NODE_MODE ?? "standalone",
13631
- version: "0.2.10",
13632
- admin: opts.admin,
13633
- projects: opts.projects
14175
+ mode: "saved",
14176
+ nodeId,
14177
+ gatewayUrl: cred.gatewayUrl,
14178
+ label: opts.label,
14179
+ dataDir,
14180
+ explicitDataDir: Boolean(explicitDataDir),
14181
+ runningServiceDirs: otherRunningDirs,
14182
+ targetedService
14183
+ };
14184
+ }
14185
+ function printLinkHelp() {
14186
+ cliLog("\u7528\u6CD5: zqagent link [--hub <url>] [--username <\u90AE\u7BB1/\u624B\u673A\u53F7> --password <\u5BC6\u7801>] [--label \u540D\u79F0]");
14187
+ cliLog(" zqagent link [--hub <url>] --code <\u4E00\u6B21\u6027\u63A5\u5165\u7801> [--label \u540D\u79F0]");
14188
+ cliLog(" \u65B9\u5F0F\u4E00\uFF08\u63A8\u8350\uFF09\uFF1A\u7528 Hub \u5DF2\u6CE8\u518C\u7684\u8D26\u53F7\u76F4\u63A5\u63A5\u5165\uFF1A");
14189
+ cliLog(" zqagent link --hub https://hub.cd-zq.com --username you@example.com --password '<\u5BC6\u7801>'");
14190
+ cliLog(" zqagent link --hub https://hub.cd-zq.com --username 13800000000 --password '<\u5BC6\u7801>'");
14191
+ cliLog(" zqagent link --hub https://hub.cd-zq.com --username 13800000000 --sms-code <\u9A8C\u8BC1\u7801>");
14192
+ cliLog(" \u65B9\u5F0F\u4E8C\uFF1A\u5728 Hub\u300C\u8282\u70B9\u7BA1\u7406\u300D\u751F\u6210\u4E00\u6B21\u6027\u63A5\u5165\u7801\uFF0815 \u5206\u949F\u6709\u6548\uFF09\uFF1A");
14193
+ cliLog(" zqagent link --hub https://hub.cd-zq.com --code <\u63A5\u5165\u7801>");
14194
+ cliLog(" \u4E0D\u5E26\u51ED\u636E\u53C2\u6570\u65F6\u8FDB\u5165\u4EA4\u4E92\u5411\u5BFC\uFF08\u9700\u7EC8\u7AEF\uFF09\uFF1B\u5BC6\u7801\u4E5F\u53EF\u7528 ZQAGENT_HUB_PASSWORD \u63D0\u4F9B\u3002");
14195
+ cliLog(" \u672C\u673A\u5DF2\u88C5\u8282\u70B9\u670D\u52A1\uFF08install.sh / \u684C\u9762\u7AEF\uFF09\u65F6\u4F1A\u81EA\u52A8\u53D1\u73B0\u5B83\u7684\u6570\u636E\u76EE\u5F55\u5E76\u70ED\u63A5\u5165\uFF0C");
14196
+ cliLog(" \u4E0D\u9700\u8981\u624B\u52A8\u6307\u5B9A --data-dir\uFF1B\u670D\u52A1\u6CA1\u5728\u8FD0\u884C\u65F6\u4F1A\u5199\u5165\u5B83\u7684\u76EE\u5F55\uFF0C\u4E0B\u6B21\u542F\u52A8\u5373\u63A5\u5165\u3002");
14197
+ cliLog(" \u4EC5\u5F53\u672C\u673A\u5B8C\u5168\u6CA1\u6709\u670D\u52A1\u65F6\u624D\u9700\u8981 --data-dir \u6307\u5B9A\u76EE\u5F55\uFF08\u4E0B\u6B21\u542F\u52A8 serve \u65F6\u751F\u6548\uFF09\u3002");
14198
+ }
14199
+ async function runLinkCommand(argv, ctx = {}) {
14200
+ const args = parseLinkArgs(argv);
14201
+ if (args.help) {
14202
+ printLinkHelp();
14203
+ return true;
14204
+ }
14205
+ const explicitHub = Boolean((args.hubUrl ?? process.env.HUB_URL ?? "").trim());
14206
+ let hubUrl = (args.hubUrl ?? process.env.HUB_URL ?? DEFAULT_HUB_URL).trim().replace(/\/$/, "");
14207
+ const code = (args.code ?? "").trim();
14208
+ let account = buildAccountAuth(args);
14209
+ const interactive = ctx.interactive ?? (process.stdin.isTTY && process.stdout.isTTY);
14210
+ if (code && account) {
14211
+ cliErr("--code \u4E0E Hub \u8D26\u53F7\uFF08--username/--email/--phone/--password\uFF09\u53EA\u80FD\u4E8C\u9009\u4E00\u3002");
14212
+ return false;
14213
+ }
14214
+ if (!account && (args.password || args.smsCode)) {
14215
+ cliErr("\u63D0\u4F9B\u4E86\u5BC6\u7801/\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF0C\u4F46\u7F3A\u5C11\u8D26\u53F7\uFF1A\u8BF7\u52A0 --username\u3001--email \u6216 --phone\u3002");
14216
+ return false;
14217
+ }
14218
+ const asPrompter = (rl2) => ({
14219
+ question: (q) => new Promise((resolve24) => rl2.question(q, resolve24)),
14220
+ pause: () => rl2.pause(),
14221
+ resume: () => rl2.resume()
14222
+ });
14223
+ let ownedRl = null;
14224
+ const getRl = () => {
14225
+ if (ctx.rl) return asPrompter(ctx.rl);
14226
+ if (!interactive) return null;
14227
+ ownedRl = readline2.createInterface({ input: process.stdin, output: process.stdout });
14228
+ return asPrompter(ownedRl);
13634
14229
  };
14230
+ const closeOwnedRl = () => {
14231
+ if (ownedRl) ownedRl.close();
14232
+ };
14233
+ try {
14234
+ if (!code && !isCompleteAccount(account)) {
14235
+ const rl2 = getRl();
14236
+ if (!rl2) {
14237
+ cliErr("\u7F3A\u5C11\u63A5\u5165\u51ED\u636E\uFF1A\u8BF7\u7528 --username/--password\uFF0C\u6216 --code <\u63A5\u5165\u7801>\u3002");
14238
+ printLinkHelp();
14239
+ return false;
14240
+ }
14241
+ if (account) {
14242
+ const password = await promptPassword(rl2);
14243
+ if (!password) {
14244
+ cliErr("\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u53D6\u6D88\u63A5\u5165\u3002");
14245
+ return false;
14246
+ }
14247
+ account = { ...account, password };
14248
+ } else {
14249
+ const prompted = await promptLinkAccount(rl2, hubUrl, { askHub: !explicitHub });
14250
+ if (!prompted) return true;
14251
+ hubUrl = prompted.hubUrl;
14252
+ account = {
14253
+ email: prompted.auth.email,
14254
+ phone: prompted.auth.phone,
14255
+ password: prompted.auth.password,
14256
+ smsCode: prompted.auth.code
14257
+ };
14258
+ }
14259
+ }
14260
+ const r = await linkNode({
14261
+ hubUrl,
14262
+ code: code || void 0,
14263
+ account: account ?? void 0,
14264
+ dataDir: args.dataDir,
14265
+ label: args.label,
14266
+ host: args.host,
14267
+ port: args.port
14268
+ });
14269
+ if (r.mode === "hot") {
14270
+ cliLog("\u2705 \u5DF2\u63A5\u5165 Hub\uFF08\u70ED\u63A5\u5165\uFF0C\u514D\u91CD\u542F\uFF09");
14271
+ cliLog(` \u8282\u70B9: ${r.nodeId}${r.label ? ` (${r.label})` : ""}`);
14272
+ cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"} \u5728\u7EBF: ${r.online ? "\u662F" : "\u8FDE\u63A5\u4E2D\u2026"}`);
14273
+ cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}${r.managed ? "\uFF08\u6258\u7BA1\u670D\u52A1\uFF0C\u5F00\u673A\u81EA\u542F\uFF09" : ""}`);
14274
+ if (r.warnings.length > 0) {
14275
+ cliLog("");
14276
+ for (const w of r.warnings) cliLog(`\u26A0\uFE0F ${w}`);
14277
+ }
14278
+ } else {
14279
+ cliLog("\u2705 \u63A5\u5165\u51ED\u636E\u5DF2\u4FDD\u5B58\uFF08\u672C\u673A\u6682\u65E0\u8FD0\u884C\u4E2D\u7684 server\uFF09");
14280
+ cliLog(` \u8282\u70B9: ${r.nodeId}`);
14281
+ cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"}`);
14282
+ cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}`);
14283
+ cliLog(" \u4E0B\u6B21\u542F\u52A8 server \u5373\u81EA\u52A8\u63A5\u5165\uFF1B\u6216\u8FD0\u884C zqagent serve \u5E38\u9A7B\u4E3A\u8282\u70B9\u3002");
14284
+ if (r.targetedService) {
14285
+ cliLog("");
14286
+ cliLog("\u2705 \u51ED\u636E\u5DF2\u5199\u5165\u672C\u673A\u6258\u7BA1\u670D\u52A1\u7684\u6570\u636E\u76EE\u5F55\uFF0C\u670D\u52A1\u542F\u52A8\u65F6\u81EA\u52A8\u63A5\u5165\uFF0C\u65E0\u9700 --data-dir\u3002");
14287
+ cliLog(" \u7ACB\u5373\u751F\u6548\uFF1A\u91CD\u542F\u8BE5\u670D\u52A1\uFF08Linux: systemctl restart zqagent\uFF1B");
14288
+ cliLog(" macOS: launchctl kickstart -k gui/$(id -u)/com.zqagent.node\uFF09\u3002");
14289
+ } else if (r.runningServiceDirs.length > 0) {
14290
+ cliLog("");
14291
+ cliLog("\u26A0\uFE0F \u672C\u673A\u6709\u6B63\u5728\u8FD0\u884C\u7684\u8282\u70B9\uFF0C\u4F46\u6570\u636E\u76EE\u5F55\u4E0E\u672C\u6B21\u5199\u5165\u7684\u4E0D\u4E00\u81F4\uFF1A");
14292
+ cliLog(` \u672C\u6B21\u5199\u5165: ${r.dataDir}`);
14293
+ for (const d of r.runningServiceDirs) cliLog(` \u670D\u52A1\u4F7F\u7528: ${d}`);
14294
+ cliLog(" \u6B63\u5728\u8FD0\u884C\u7684\u670D\u52A1\u8BFB\u4E0D\u5230\u8FD9\u4EFD\u51ED\u636E\uFF0CHub \u4F1A\u4E00\u76F4\u663E\u793A\u672A\u63A5\u5165\u3002");
14295
+ cliLog(" \u8BF7\u786E\u8BA4\u670D\u52A1\u7684\u6570\u636E\u76EE\u5F55\u540E\u91CD\u65B0\u6267\u884C\uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF1B\u8D26\u53F7\u65B9\u5F0F\u53EF\u91CD\u590D\u4F7F\u7528\uFF09\uFF1A");
14296
+ cliLog(` zqagent link --hub <Hub\u5730\u5740> --username <\u90AE\u7BB1/\u624B\u673A\u53F7> --password '<\u5BC6\u7801>' --data-dir "${r.runningServiceDirs[0]}"`);
14297
+ } else if (!r.explicitDataDir) {
14298
+ cliLog("");
14299
+ cliLog("\u26A0\uFE0F \u672A\u53D1\u73B0\u672C\u673A\u8282\u70B9\u670D\u52A1\uFF0C\u51ED\u636E\u5199\u5165\u4E86\u5F53\u524D\u76EE\u5F55\u4E0B\u7684 .zqagent_data\u3002");
14300
+ cliLog(" \u5982\u679C\u8FD9\u53F0\u673A\u5668\u6253\u7B97\u957F\u671F\u8FD0\u884C\u8282\u70B9\uFF0C\u8BF7\u5148\u5B89\u88C5\u5F00\u673A\u670D\u52A1\uFF0C\u4E4B\u540E link \u4F1A\u81EA\u52A8\u5199\u5165\u5B83\u7684\u76EE\u5F55\uFF1A");
14301
+ cliLog(" curl -fsSL https://agent.cd-zq.com/install.sh | sh");
14302
+ cliLog(` \uFF08\u670D\u52A1\u9ED8\u8BA4\u6570\u636E\u76EE\u5F55\uFF1A${installDataDirHint()}\uFF09`);
14303
+ }
14304
+ }
14305
+ return true;
14306
+ } catch (e) {
14307
+ cliErr(`\u63A5\u5165\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
14308
+ return false;
14309
+ } finally {
14310
+ closeOwnedRl();
14311
+ }
13635
14312
  }
13636
14313
 
14314
+ // src/cli/serve.ts
14315
+ init_esm_shims();
14316
+
14317
+ // src/server/runtime.ts
14318
+ init_esm_shims();
14319
+ import * as fs31 from "fs";
14320
+ import * as path39 from "path";
14321
+
14322
+ // src/server/routes.ts
14323
+ init_esm_shims();
14324
+ import { createServer } from "http";
14325
+ import * as path32 from "path";
14326
+
13637
14327
  // src/server/auth.ts
13638
14328
  init_esm_shims();
13639
14329
  import * as crypto4 from "crypto";
@@ -13807,34 +14497,34 @@ async function handleHubProxy(req2, res) {
13807
14497
 
13808
14498
  // src/server/fsBrowser.ts
13809
14499
  init_esm_shims();
13810
- import * as fs21 from "fs";
13811
- import * as os4 from "os";
13812
- import * as path27 from "path";
14500
+ import * as fs22 from "fs";
14501
+ import * as os6 from "os";
14502
+ import * as path29 from "path";
13813
14503
  var MAX_ENTRIES = 2e3;
13814
14504
  var DEFAULT_SEARCH_LIMIT = 200;
13815
14505
  var MAX_SEARCH_LIMIT = 1e3;
13816
14506
  function listDirs(input) {
13817
- const base = (input ?? "").trim() || os4.homedir();
13818
- const target = path27.resolve(base);
14507
+ const base = (input ?? "").trim() || os6.homedir();
14508
+ const target = path29.resolve(base);
13819
14509
  let st;
13820
14510
  try {
13821
- st = fs21.statSync(target);
14511
+ st = fs22.statSync(target);
13822
14512
  } catch {
13823
14513
  throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
13824
14514
  }
13825
14515
  if (!st.isDirectory()) throw new Error(`\u4E0D\u662F\u76EE\u5F55: ${target}`);
13826
14516
  let dirents;
13827
14517
  try {
13828
- dirents = fs21.readdirSync(target, { withFileTypes: true });
14518
+ dirents = fs22.readdirSync(target, { withFileTypes: true });
13829
14519
  } catch {
13830
14520
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
13831
14521
  }
13832
14522
  const entries = dirents.map((d) => {
13833
- const full = path27.join(target, d.name);
14523
+ const full = path29.join(target, d.name);
13834
14524
  let isDir = d.isDirectory();
13835
14525
  if (d.isSymbolicLink()) {
13836
14526
  try {
13837
- isDir = fs21.statSync(full).isDirectory();
14527
+ isDir = fs22.statSync(full).isDirectory();
13838
14528
  } catch {
13839
14529
  }
13840
14530
  }
@@ -13843,7 +14533,7 @@ function listDirs(input) {
13843
14533
  entries.sort(
13844
14534
  (a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
13845
14535
  );
13846
- const parentDir = path27.dirname(target);
14536
+ const parentDir = path29.dirname(target);
13847
14537
  return {
13848
14538
  path: target,
13849
14539
  parent: parentDir !== target ? parentDir : null,
@@ -13874,7 +14564,7 @@ var NAME_CACHE_MAX_DIRS = 4;
13874
14564
  var NAME_CACHE_MAX_ENTRIES = 5e4;
13875
14565
  var nameCache = /* @__PURE__ */ new Map();
13876
14566
  function invalidateDirNames(target) {
13877
- nameCache.delete(path27.resolve(target));
14567
+ nameCache.delete(path29.resolve(target));
13878
14568
  }
13879
14569
  async function loadDirNames(target) {
13880
14570
  const now = Date.now();
@@ -13886,7 +14576,7 @@ async function loadDirNames(target) {
13886
14576
  }
13887
14577
  let dirents;
13888
14578
  try {
13889
- dirents = await fs21.promises.readdir(target, { withFileTypes: true });
14579
+ dirents = await fs22.promises.readdir(target, { withFileTypes: true });
13890
14580
  } catch {
13891
14581
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
13892
14582
  }
@@ -13909,22 +14599,22 @@ async function loadDirNames(target) {
13909
14599
  return entries;
13910
14600
  }
13911
14601
  async function toDirEntry(dir, e) {
13912
- const full = path27.join(dir, e.name);
14602
+ const full = path29.join(dir, e.name);
13913
14603
  let isDir = e.isDir;
13914
14604
  if (e.isLink) {
13915
14605
  try {
13916
- isDir = (await fs21.promises.stat(full)).isDirectory();
14606
+ isDir = (await fs22.promises.stat(full)).isDirectory();
13917
14607
  } catch {
13918
14608
  }
13919
14609
  }
13920
14610
  return { name: e.name, path: full, type: isDir ? "dir" : "file" };
13921
14611
  }
13922
14612
  async function searchDirs(input, query, limit) {
13923
- const base = (input ?? "").trim() || os4.homedir();
13924
- const target = path27.resolve(base);
14613
+ const base = (input ?? "").trim() || os6.homedir();
14614
+ const target = path29.resolve(base);
13925
14615
  let st;
13926
14616
  try {
13927
- st = await fs21.promises.stat(target);
14617
+ st = await fs22.promises.stat(target);
13928
14618
  } catch {
13929
14619
  throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
13930
14620
  }
@@ -13954,7 +14644,7 @@ async function searchDirs(input, query, limit) {
13954
14644
  );
13955
14645
  const hit = scored.slice(0, max);
13956
14646
  const entries = await Promise.all(hit.map(({ entry }) => toDirEntry(target, entry)));
13957
- const parentDir = path27.dirname(target);
14647
+ const parentDir = path29.dirname(target);
13958
14648
  return {
13959
14649
  path: target,
13960
14650
  parent: parentDir !== target ? parentDir : null,
@@ -13965,52 +14655,52 @@ async function searchDirs(input, query, limit) {
13965
14655
  };
13966
14656
  }
13967
14657
  function makeDirIn(parentInput, name) {
13968
- const parent = path27.resolve((parentInput ?? "").trim() || os4.homedir());
14658
+ const parent = path29.resolve((parentInput ?? "").trim() || os6.homedir());
13969
14659
  const clean = (name ?? "").trim();
13970
14660
  if (!clean) throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u4E3A\u7A7A");
13971
14661
  if (clean === "." || clean === "..") throw new Error("\u76EE\u5F55\u540D\u975E\u6CD5");
13972
- const target = path27.resolve(parent, clean);
13973
- const rel = path27.relative(parent, target);
13974
- if (!rel || rel.startsWith("..") || path27.isAbsolute(rel)) {
14662
+ const target = path29.resolve(parent, clean);
14663
+ const rel = path29.relative(parent, target);
14664
+ if (!rel || rel.startsWith("..") || path29.isAbsolute(rel)) {
13975
14665
  throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u8D8A\u51FA\u5F53\u524D\u76EE\u5F55");
13976
14666
  }
13977
14667
  let st = null;
13978
14668
  try {
13979
- st = fs21.statSync(target);
14669
+ st = fs22.statSync(target);
13980
14670
  } catch {
13981
14671
  st = null;
13982
14672
  }
13983
14673
  if (st && !st.isDirectory()) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u6587\u4EF6: ${target}`);
13984
14674
  try {
13985
- fs21.mkdirSync(target, { recursive: true });
14675
+ fs22.mkdirSync(target, { recursive: true });
13986
14676
  } catch (e) {
13987
14677
  throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
13988
14678
  }
13989
14679
  invalidateDirNames(parent);
13990
- return { name: path27.basename(target), path: target, type: "dir" };
14680
+ return { name: path29.basename(target), path: target, type: "dir" };
13991
14681
  }
13992
14682
 
13993
14683
  // src/server/projectFiles.ts
13994
14684
  init_esm_shims();
13995
- import * as fs22 from "fs";
13996
- import * as path28 from "path";
14685
+ import * as fs23 from "fs";
14686
+ import * as path30 from "path";
13997
14687
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
13998
14688
  var MAX_DOWNLOAD_BYTES = 16 * 1024 * 1024;
13999
14689
  var MAX_ENTRIES2 = 2e3;
14000
14690
  function toPosix(p) {
14001
- return p.split(path28.sep).join("/");
14691
+ return p.split(path30.sep).join("/");
14002
14692
  }
14003
14693
  function projectRoot(workspaceRoot) {
14004
14694
  const r = (workspaceRoot ?? "").trim();
14005
14695
  if (!r) {
14006
14696
  throw new Error("\u9879\u76EE\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF08workspaceRoot\uFF09\uFF0C\u65E0\u6CD5\u6D4F\u89C8\u6587\u4EF6");
14007
14697
  }
14008
- const abs = path28.resolve(r);
14009
- if (!fs22.existsSync(abs)) {
14698
+ const abs = path30.resolve(r);
14699
+ if (!fs23.existsSync(abs)) {
14010
14700
  throw new Error(`\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728: ${abs}`);
14011
14701
  }
14012
14702
  try {
14013
- return fs22.realpathSync(abs);
14703
+ return fs23.realpathSync(abs);
14014
14704
  } catch {
14015
14705
  return abs;
14016
14706
  }
@@ -14028,37 +14718,37 @@ function listProjectFiles(workspaceRoot, relPath) {
14028
14718
  const dir = resolveWithin(base, relPath ?? "");
14029
14719
  let st;
14030
14720
  try {
14031
- st = fs22.statSync(dir);
14721
+ st = fs23.statSync(dir);
14032
14722
  } catch {
14033
- throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path28.relative(base, dir)) || "/"}`);
14723
+ throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path30.relative(base, dir)) || "/"}`);
14034
14724
  }
14035
14725
  if (!st.isDirectory()) throw new Error("\u76EE\u6807\u4E0D\u662F\u76EE\u5F55");
14036
14726
  let dirents;
14037
14727
  try {
14038
- dirents = fs22.readdirSync(dir, { withFileTypes: true });
14728
+ dirents = fs23.readdirSync(dir, { withFileTypes: true });
14039
14729
  } catch {
14040
14730
  throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09");
14041
14731
  }
14042
14732
  const entries = dirents.map((d) => {
14043
- const full = path28.join(dir, d.name);
14733
+ const full = path30.join(dir, d.name);
14044
14734
  let isDir = d.isDirectory();
14045
14735
  if (d.isSymbolicLink()) {
14046
14736
  try {
14047
- isDir = fs22.statSync(full).isDirectory();
14737
+ isDir = fs23.statSync(full).isDirectory();
14048
14738
  } catch {
14049
14739
  }
14050
14740
  }
14051
14741
  let size = 0;
14052
14742
  let mtime = "";
14053
14743
  try {
14054
- const s = fs22.statSync(full);
14744
+ const s = fs23.statSync(full);
14055
14745
  size = s.size;
14056
14746
  mtime = s.mtime.toISOString();
14057
14747
  } catch {
14058
14748
  }
14059
14749
  return {
14060
14750
  name: d.name,
14061
- path: toPosix(path28.relative(base, full)),
14751
+ path: toPosix(path30.relative(base, full)),
14062
14752
  type: isDir ? "dir" : "file",
14063
14753
  size,
14064
14754
  mtime
@@ -14067,12 +14757,12 @@ function listProjectFiles(workspaceRoot, relPath) {
14067
14757
  entries.sort(
14068
14758
  (a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
14069
14759
  );
14070
- const relDir = toPosix(path28.relative(base, dir));
14071
- const parentAbs = path28.dirname(dir);
14760
+ const relDir = toPosix(path30.relative(base, dir));
14761
+ const parentAbs = path30.dirname(dir);
14072
14762
  return {
14073
14763
  root: base,
14074
14764
  path: relDir,
14075
- parent: isWithinRoot(base, parentAbs) ? toPosix(path28.relative(base, parentAbs)) : null,
14765
+ parent: isWithinRoot(base, parentAbs) ? toPosix(path30.relative(base, parentAbs)) : null,
14076
14766
  entries: entries.slice(0, MAX_ENTRIES2)
14077
14767
  };
14078
14768
  }
@@ -14081,17 +14771,17 @@ function readProjectFile(workspaceRoot, relPath) {
14081
14771
  const file = resolveWithin(base, relPath ?? "");
14082
14772
  let st;
14083
14773
  try {
14084
- st = fs22.statSync(file);
14774
+ st = fs23.statSync(file);
14085
14775
  } catch {
14086
14776
  throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
14087
14777
  }
14088
14778
  if (!st.isFile()) throw new Error("\u76EE\u6807\u4E0D\u662F\u6587\u4EF6");
14089
- const buf = fs22.readFileSync(file);
14779
+ const buf = fs23.readFileSync(file);
14090
14780
  const binary = isBinary(buf);
14091
14781
  const truncated = buf.length > MAX_FILE_BYTES;
14092
14782
  const slice = truncated ? buf.subarray(0, MAX_FILE_BYTES) : buf;
14093
14783
  return {
14094
- path: toPosix(path28.relative(base, file)),
14784
+ path: toPosix(path30.relative(base, file)),
14095
14785
  size: st.size,
14096
14786
  binary,
14097
14787
  truncated,
@@ -14103,7 +14793,7 @@ function downloadProjectFile(workspaceRoot, relPath) {
14103
14793
  const file = resolveWithin(base, relPath ?? "");
14104
14794
  let st;
14105
14795
  try {
14106
- st = fs22.statSync(file);
14796
+ st = fs23.statSync(file);
14107
14797
  } catch {
14108
14798
  throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
14109
14799
  }
@@ -14111,10 +14801,10 @@ function downloadProjectFile(workspaceRoot, relPath) {
14111
14801
  if (st.size > MAX_DOWNLOAD_BYTES) {
14112
14802
  throw new Error(`\u6587\u4EF6\u8D85\u8FC7\u4E0B\u8F7D\u4E0A\u9650\uFF08${MAX_DOWNLOAD_BYTES / (1024 * 1024)}MB\uFF09`);
14113
14803
  }
14114
- const buf = fs22.readFileSync(file);
14804
+ const buf = fs23.readFileSync(file);
14115
14805
  return {
14116
- path: toPosix(path28.relative(base, file)),
14117
- name: path28.basename(file),
14806
+ path: toPosix(path30.relative(base, file)),
14807
+ name: path30.basename(file),
14118
14808
  size: buf.length,
14119
14809
  base64: buf.toString("base64")
14120
14810
  };
@@ -14136,12 +14826,12 @@ function writeProjectFile(workspaceRoot, relPath, content) {
14136
14826
  throw new Error("\u5185\u5BB9\u8D85\u8FC7 2MB \u4E0A\u9650\uFF0C\u65E0\u6CD5\u4FDD\u5B58");
14137
14827
  }
14138
14828
  try {
14139
- fs22.mkdirSync(path28.dirname(file), { recursive: true });
14140
- fs22.writeFileSync(file, buf);
14829
+ fs23.mkdirSync(path30.dirname(file), { recursive: true });
14830
+ fs23.writeFileSync(file, buf);
14141
14831
  } catch (e) {
14142
14832
  throw new Error(`\u5199\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
14143
14833
  }
14144
- return { path: toPosix(path28.relative(base, file)), size: buf.length };
14834
+ return { path: toPosix(path30.relative(base, file)), size: buf.length };
14145
14835
  }
14146
14836
  function createProjectDir(workspaceRoot, relPath) {
14147
14837
  const base = projectRoot(workspaceRoot);
@@ -14149,26 +14839,26 @@ function createProjectDir(workspaceRoot, relPath) {
14149
14839
  if (!rel) throw new Error("\u76EE\u5F55\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
14150
14840
  const dir = resolveWithin(base, rel);
14151
14841
  try {
14152
- fs22.mkdirSync(dir, { recursive: true });
14842
+ fs23.mkdirSync(dir, { recursive: true });
14153
14843
  } catch (e) {
14154
14844
  throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
14155
14845
  }
14156
- return { path: toPosix(path28.relative(base, dir)) };
14846
+ return { path: toPosix(path30.relative(base, dir)) };
14157
14847
  }
14158
14848
  function createProjectFile(workspaceRoot, relPath, content = "") {
14159
14849
  const base = projectRoot(workspaceRoot);
14160
14850
  const rel = (relPath ?? "").trim();
14161
14851
  if (!rel) throw new Error("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
14162
14852
  const file = resolveWithin(base, rel);
14163
- if (fs22.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
14853
+ if (fs23.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
14164
14854
  const buf = Buffer.from(String(content ?? ""), "utf-8");
14165
14855
  try {
14166
- fs22.mkdirSync(path28.dirname(file), { recursive: true });
14167
- fs22.writeFileSync(file, buf);
14856
+ fs23.mkdirSync(path30.dirname(file), { recursive: true });
14857
+ fs23.writeFileSync(file, buf);
14168
14858
  } catch (e) {
14169
14859
  throw new Error(`\u521B\u5EFA\u6587\u4EF6\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
14170
14860
  }
14171
- return { path: toPosix(path28.relative(base, file)), size: buf.length };
14861
+ return { path: toPosix(path30.relative(base, file)), size: buf.length };
14172
14862
  }
14173
14863
  function searchProjectFiles(workspaceRoot, query) {
14174
14864
  const base = projectRoot(workspaceRoot);
@@ -14191,7 +14881,7 @@ function searchProjectFiles(workspaceRoot, query) {
14191
14881
  if (out.length >= LIMIT) return;
14192
14882
  let dirents;
14193
14883
  try {
14194
- dirents = fs22.readdirSync(dir, { withFileTypes: true });
14884
+ dirents = fs23.readdirSync(dir, { withFileTypes: true });
14195
14885
  } catch {
14196
14886
  return;
14197
14887
  }
@@ -14202,7 +14892,7 @@ function searchProjectFiles(workspaceRoot, query) {
14202
14892
  let isDir = d.isDirectory();
14203
14893
  if (d.isSymbolicLink()) {
14204
14894
  try {
14205
- isDir = fs22.statSync(path28.join(dir, d.name)).isDirectory();
14895
+ isDir = fs23.statSync(path30.join(dir, d.name)).isDirectory();
14206
14896
  } catch {
14207
14897
  isDir = false;
14208
14898
  }
@@ -14210,7 +14900,7 @@ function searchProjectFiles(workspaceRoot, query) {
14210
14900
  if (d.name.toLowerCase().includes(q)) {
14211
14901
  out.push({ path: rel, type: isDir ? "dir" : "file" });
14212
14902
  }
14213
- if (isDir) walk(path28.join(dir, d.name), rel);
14903
+ if (isDir) walk(path30.join(dir, d.name), rel);
14214
14904
  }
14215
14905
  };
14216
14906
  walk(base, "");
@@ -14219,8 +14909,8 @@ function searchProjectFiles(workspaceRoot, query) {
14219
14909
 
14220
14910
  // src/server/projectOps.ts
14221
14911
  init_esm_shims();
14222
- import * as os5 from "os";
14223
- import * as path29 from "path";
14912
+ import * as os7 from "os";
14913
+ import * as path31 from "path";
14224
14914
  var MAX_STDOUT2 = 2e4;
14225
14915
  var MAX_STDERR2 = 1e4;
14226
14916
  var MAX_BUFFER3 = 1024 * 1024 * 5;
@@ -14234,7 +14924,7 @@ function resolveInvocation2(spec, prepared) {
14234
14924
  }
14235
14925
  function workRoot(workspaceRoot) {
14236
14926
  const r = (workspaceRoot ?? "").trim();
14237
- return r ? path29.resolve(r) : os5.homedir();
14927
+ return r ? path31.resolve(r) : os7.homedir();
14238
14928
  }
14239
14929
  async function runProjectCommand(workspaceRoot, command, timeoutSeconds) {
14240
14930
  const cmd = String(command ?? "");
@@ -14545,11 +15235,11 @@ function projectStats(manager, tenantId) {
14545
15235
 
14546
15236
  // src/server/nodeHost.ts
14547
15237
  init_esm_shims();
14548
- import * as fs23 from "fs";
14549
- import * as os6 from "os";
15238
+ import * as fs24 from "fs";
15239
+ import * as os8 from "os";
14550
15240
  function diskInfo(pathStr) {
14551
15241
  try {
14552
- const st = fs23.statfsSync(pathStr);
15242
+ const st = fs24.statfsSync(pathStr);
14553
15243
  return {
14554
15244
  path: pathStr,
14555
15245
  total: Number(st.blocks) * Number(st.bsize),
@@ -14561,7 +15251,7 @@ function diskInfo(pathStr) {
14561
15251
  }
14562
15252
  function networkInfo() {
14563
15253
  const out = [];
14564
- const ifaces = os6.networkInterfaces();
15254
+ const ifaces = os8.networkInterfaces();
14565
15255
  for (const [name, list] of Object.entries(ifaces)) {
14566
15256
  for (const info of list ?? []) {
14567
15257
  out.push({
@@ -14612,23 +15302,23 @@ function overview(store, manager) {
14612
15302
  return { projects, sessions: { total, running, waiting, items }, usage: { tokensIn, tokensOut } };
14613
15303
  }
14614
15304
  function buildNodeHost(store, manager, diskPath) {
14615
- const cpus2 = os6.cpus();
15305
+ const cpus2 = os8.cpus();
14616
15306
  const ov = overview(store, manager);
14617
15307
  return {
14618
15308
  nodeId: store.loadNodeId() ?? "",
14619
- hostname: os6.hostname(),
15309
+ hostname: os8.hostname(),
14620
15310
  platform: process.platform,
14621
15311
  arch: process.arch,
14622
- release: os6.release(),
14623
- uptimeSec: Math.round(os6.uptime()),
14624
- version: "0.2.10",
15312
+ release: os8.release(),
15313
+ uptimeSec: Math.round(os8.uptime()),
15314
+ version: "0.2.12",
14625
15315
  mode: process.env.NODE_MODE ?? "standalone",
14626
15316
  cpu: {
14627
15317
  count: cpus2.length,
14628
15318
  model: cpus2[0]?.model ?? "",
14629
- loadAvg: os6.loadavg().map((n) => Math.round(n * 100) / 100)
15319
+ loadAvg: os8.loadavg().map((n) => Math.round(n * 100) / 100)
14630
15320
  },
14631
- memory: { total: os6.totalmem(), free: os6.freemem() },
15321
+ memory: { total: os8.totalmem(), free: os8.freemem() },
14632
15322
  network: networkInfo(),
14633
15323
  disk: diskInfo(diskPath || process.cwd()),
14634
15324
  ...ov
@@ -14991,7 +15681,7 @@ function createApp(store, manager, opts = {}) {
14991
15681
  ok(res, opts.fleet.status());
14992
15682
  }
14993
15683
  },
14994
- // 运行时热接入:凭一次性注册码接入 Hub(落盘 fleet.json + 立即连接,免重启)
15684
+ // 运行时热接入:凭一次性注册码或 Hub 账号接入(落盘 fleet.json + 立即连接,免重启)
14995
15685
  {
14996
15686
  method: "POST",
14997
15687
  pattern: ["fleet", "enroll"],
@@ -15003,15 +15693,42 @@ function createApp(store, manager, opts = {}) {
15003
15693
  const raw = body ?? {};
15004
15694
  const hubUrl = String(raw.hubUrl ?? "").trim().replace(/\/$/, "");
15005
15695
  const code = String(raw.code ?? "").trim();
15006
- if (!hubUrl || !code) {
15007
- throw new ApiError(400, "bad_request", "hubUrl \u4E0E code \u5FC5\u586B");
15696
+ const email = String(raw.email ?? "").trim();
15697
+ const phone = String(raw.phone ?? "").trim();
15698
+ const password = String(raw.password ?? "");
15699
+ const smsCode = String(raw.smsCode ?? "").trim();
15700
+ const hasAccount = Boolean(email || phone);
15701
+ if (!hubUrl) {
15702
+ throw new ApiError(400, "bad_request", "hubUrl \u5FC5\u586B");
15703
+ }
15704
+ if (code && hasAccount) {
15705
+ throw new ApiError(400, "bad_request", "code \u4E0E\u8D26\u53F7\u51ED\u636E\u53EA\u80FD\u4E8C\u9009\u4E00");
15706
+ }
15707
+ if (!code && !hasAccount) {
15708
+ throw new ApiError(400, "bad_request", "\u9700\u8981 code\uFF0C\u6216 email/phone + password/smsCode");
15709
+ }
15710
+ if (hasAccount && !password && !smsCode) {
15711
+ throw new ApiError(400, "bad_request", "\u8D26\u53F7\u63A5\u5165\u9700\u8981 password \u6216 smsCode");
15008
15712
  }
15009
15713
  const label = typeof raw.label === "string" ? raw.label.trim() : void 0;
15010
15714
  try {
15011
- ok(res, await opts.fleet.enroll({ hubUrl, code, label }));
15715
+ ok(
15716
+ res,
15717
+ await opts.fleet.enroll({
15718
+ hubUrl,
15719
+ code: code || void 0,
15720
+ account: hasAccount ? {
15721
+ email: email || void 0,
15722
+ phone: phone || void 0,
15723
+ password: password || void 0,
15724
+ smsCode: smsCode || void 0
15725
+ } : void 0,
15726
+ label
15727
+ })
15728
+ );
15012
15729
  } catch (e) {
15013
15730
  const code2 = e.code;
15014
- const status = code2 === "code_expired" ? 410 : code2 === "code_used" || code2 === "node_taken" ? 409 : code2 === "not_found" ? 404 : code2 === "plan_limit" || code2 === "subscription_required" ? 402 : 400;
15731
+ const status = code2 === "code_expired" ? 410 : code2 === "unauthorized" ? 401 : code2 === "account_disabled" ? 403 : code2 === "code_used" || code2 === "node_taken" ? 409 : code2 === "not_found" ? 404 : code2 === "plan_limit" || code2 === "subscription_required" ? 402 : 400;
15015
15732
  const msg = e instanceof Error ? e.message : String(e);
15016
15733
  throw new ApiError(
15017
15734
  status,
@@ -15196,7 +15913,7 @@ function createApp(store, manager, opts = {}) {
15196
15913
  applyProvidersPatch(tenant, raw);
15197
15914
  if (typeof raw.workspaceRoot === "string") {
15198
15915
  const ws = raw.workspaceRoot.trim();
15199
- const next = ws ? path30.resolve(ws) : void 0;
15916
+ const next = ws ? path32.resolve(ws) : void 0;
15200
15917
  if (tenant.workspaceRoot) {
15201
15918
  if (next !== tenant.workspaceRoot) {
15202
15919
  throw new ApiError(
@@ -16183,7 +16900,7 @@ function createApp(store, manager, opts = {}) {
16183
16900
  json(res, 200, {
16184
16901
  status: "ok",
16185
16902
  // 版本让运维/探针能确认线上跑的是哪个构建(更新与回滚据此判断)
16186
- version: "0.2.10",
16903
+ version: "0.2.12",
16187
16904
  features: { mcp: hasServerMcpConfig() }
16188
16905
  });
16189
16906
  return;
@@ -16278,7 +16995,7 @@ function createApp(store, manager, opts = {}) {
16278
16995
 
16279
16996
  // src/server/fleet/FleetNodeBridge.ts
16280
16997
  init_esm_shims();
16281
- import * as os7 from "os";
16998
+ import * as os9 from "os";
16282
16999
  import { monitorEventLoopDelay } from "perf_hooks";
16283
17000
 
16284
17001
  // src/server/fleet/FleetCommands.ts
@@ -17472,7 +18189,7 @@ function withTimeout(p, ms) {
17472
18189
  }
17473
18190
  function primaryIp() {
17474
18191
  let v6;
17475
- for (const infos of Object.values(os7.networkInterfaces())) {
18192
+ for (const infos of Object.values(os9.networkInterfaces())) {
17476
18193
  for (const info of infos ?? []) {
17477
18194
  if (info.internal) continue;
17478
18195
  if (info.family === "IPv4") return info.address;
@@ -17486,7 +18203,7 @@ function primaryIp() {
17486
18203
  var FleetNodeBridge = class {
17487
18204
  constructor(opts) {
17488
18205
  this.opts = opts;
17489
- this.nodeId = opts.nodeId ?? ensureNodeId2(opts.deps.store);
18206
+ this.nodeId = opts.nodeId ?? ensureNodeId(opts.deps.store);
17490
18207
  }
17491
18208
  opts;
17492
18209
  /** 是否成功连接过(区分「一直连不上」与「连上后断线」) */
@@ -17821,7 +18538,7 @@ var FleetNodeBridge = class {
17821
18538
  label: this.opts.deps.store.loadNodeLabel() ?? void 0,
17822
18539
  platform: process.platform,
17823
18540
  mode: process.env.NODE_MODE ?? "standalone",
17824
- version: "0.2.10",
18541
+ version: "0.2.12",
17825
18542
  ip: primaryIp()
17826
18543
  };
17827
18544
  }
@@ -17876,7 +18593,7 @@ var FleetNodeBridge = class {
17876
18593
  label: this.opts.deps.store.loadNodeLabel() ?? void 0,
17877
18594
  platform: process.platform,
17878
18595
  mode: process.env.NODE_MODE ?? "standalone",
17879
- version: "0.2.10",
18596
+ version: "0.2.12",
17880
18597
  ip: primaryIp(),
17881
18598
  projects,
17882
18599
  totals,
@@ -18064,28 +18781,6 @@ var FleetNodeBridge = class {
18064
18781
  }
18065
18782
  };
18066
18783
 
18067
- // src/server/fleet-config.ts
18068
- init_esm_shims();
18069
- function fleetConfigFromEnv(store) {
18070
- const fleetToken = process.env.FLEET_TOKEN;
18071
- const fleetSecret = process.env.FLEET_SECRET;
18072
- if (!fleetToken || !fleetSecret) return null;
18073
- return {
18074
- hubUrl: process.env.HUB_URL,
18075
- fleetToken,
18076
- fleetSecret,
18077
- nodeId: ensureNodeId2(store),
18078
- gatewayUrl: process.env.FLEET_GATEWAY_URL
18079
- };
18080
- }
18081
- function resolveFleetConfig(store) {
18082
- const saved = store.loadFleetCredential();
18083
- if (saved?.fleetToken && saved.fleetSecret) {
18084
- return { ...saved, nodeId: saved.nodeId || ensureNodeId2(store) };
18085
- }
18086
- return fleetConfigFromEnv(store);
18087
- }
18088
-
18089
18784
  // src/server/notify/index.ts
18090
18785
  init_esm_shims();
18091
18786
 
@@ -18645,8 +19340,8 @@ init_esm_shims();
18645
19340
 
18646
19341
  // src/server/qq/bindings.ts
18647
19342
  init_esm_shims();
18648
- import * as fs24 from "fs";
18649
- import * as path31 from "path";
19343
+ import * as fs25 from "fs";
19344
+ import * as path33 from "path";
18650
19345
  function conversationKey(target) {
18651
19346
  switch (target.kind) {
18652
19347
  case "c2c":
@@ -18685,12 +19380,12 @@ var QqBindingsStore = class _QqBindingsStore {
18685
19380
  cache = null;
18686
19381
  /** 基于 DATA_DIR 构造 */
18687
19382
  static in(dataDir) {
18688
- return new _QqBindingsStore(path31.join(dataDir, "qq", "bindings.json"));
19383
+ return new _QqBindingsStore(path33.join(dataDir, "qq", "bindings.json"));
18689
19384
  }
18690
19385
  load() {
18691
19386
  if (this.cache) return this.cache;
18692
19387
  try {
18693
- const raw = JSON.parse(fs24.readFileSync(this.file, "utf-8"));
19388
+ const raw = JSON.parse(fs25.readFileSync(this.file, "utf-8"));
18694
19389
  this.cache = {
18695
19390
  conversations: raw.conversations ?? {}
18696
19391
  };
@@ -18722,8 +19417,8 @@ var QqBindingsStore = class _QqBindingsStore {
18722
19417
  return null;
18723
19418
  }
18724
19419
  persist() {
18725
- fs24.mkdirSync(path31.dirname(this.file), { recursive: true });
18726
- fs24.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
19420
+ fs25.mkdirSync(path33.dirname(this.file), { recursive: true });
19421
+ fs25.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
18727
19422
  }
18728
19423
  };
18729
19424
 
@@ -19135,8 +19830,8 @@ ${lines.join("\n")}`, msgId);
19135
19830
 
19136
19831
  // src/server/qq/QqManager.ts
19137
19832
  init_esm_shims();
19138
- import * as fs25 from "fs";
19139
- import * as path32 from "path";
19833
+ import * as fs26 from "fs";
19834
+ import * as path34 from "path";
19140
19835
  var QqManager = class {
19141
19836
  constructor(opts) {
19142
19837
  this.opts = opts;
@@ -19145,7 +19840,7 @@ var QqManager = class {
19145
19840
  adapter = null;
19146
19841
  config = null;
19147
19842
  get configFile() {
19148
- return path32.join(this.opts.dataDir, "qq", "config.json");
19843
+ return path34.join(this.opts.dataDir, "qq", "config.json");
19149
19844
  }
19150
19845
  /** 启动:读配置(env 优先,其次文件),有则建连接 */
19151
19846
  start() {
@@ -19183,7 +19878,7 @@ var QqManager = class {
19183
19878
  /** 清空配置:删除文件 + 断开 */
19184
19879
  clearConfig() {
19185
19880
  try {
19186
- fs25.rmSync(this.configFile, { force: true });
19881
+ fs26.rmSync(this.configFile, { force: true });
19187
19882
  } catch {
19188
19883
  }
19189
19884
  this.stop();
@@ -19225,7 +19920,7 @@ var QqManager = class {
19225
19920
  }
19226
19921
  _readConfigFile() {
19227
19922
  try {
19228
- const raw = JSON.parse(fs25.readFileSync(this.configFile, "utf-8"));
19923
+ const raw = JSON.parse(fs26.readFileSync(this.configFile, "utf-8"));
19229
19924
  if (raw.appId && raw.appSecret) {
19230
19925
  return {
19231
19926
  appId: raw.appId,
@@ -19238,8 +19933,8 @@ var QqManager = class {
19238
19933
  return null;
19239
19934
  }
19240
19935
  _persistConfig(cfg) {
19241
- fs25.mkdirSync(path32.dirname(this.configFile), { recursive: true });
19242
- fs25.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
19936
+ fs26.mkdirSync(path34.dirname(this.configFile), { recursive: true });
19937
+ fs26.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
19243
19938
  }
19244
19939
  };
19245
19940
 
@@ -19288,16 +19983,16 @@ var PairingCodeStore = class {
19288
19983
  // src/update/controller.ts
19289
19984
  init_esm_shims();
19290
19985
  import * as fs30 from "fs";
19291
- import * as path37 from "path";
19986
+ import * as path38 from "path";
19292
19987
 
19293
19988
  // src/update/downloader.ts
19294
19989
  init_esm_shims();
19295
19990
  import { createHash as createHash3 } from "crypto";
19296
- import * as fs26 from "fs";
19991
+ import * as fs27 from "fs";
19297
19992
  import { Readable as Readable2 } from "stream";
19298
19993
  import { pipeline as pipeline2 } from "stream/promises";
19299
- import { spawnSync as spawnSync3 } from "child_process";
19300
- import * as path33 from "path";
19994
+ import { spawnSync as spawnSync4 } from "child_process";
19995
+ import * as path35 from "path";
19301
19996
 
19302
19997
  // src/update/manifest.ts
19303
19998
  init_esm_shims();
@@ -19463,267 +20158,97 @@ async function fetchManifest(url, publicKey, opts = {}) {
19463
20158
  }
19464
20159
  let parsed;
19465
20160
  try {
19466
- parsed = JSON.parse(raw.toString("utf-8"));
19467
- } catch (err) {
19468
- return { error: `\u6E05\u5355\u4E0D\u662F\u5408\u6CD5 JSON\uFF1A${err.message}` };
19469
- }
19470
- const { manifest, error: error2 } = parseManifest(parsed, opts);
19471
- if (!manifest) return { error: error2 };
19472
- return { manifest, raw };
19473
- }
19474
- async function downloadBinary(bin, destPath, log = () => {
19475
- }) {
19476
- const partPath = `${destPath}.part`;
19477
- fs26.mkdirSync(path33.dirname(destPath), { recursive: true });
19478
- fs26.rmSync(partPath, { force: true });
19479
- let res;
19480
- try {
19481
- res = await fetch(bin.url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS2) });
19482
- } catch (err) {
19483
- return { error: `\u4E0B\u8F7D\u5931\u8D25\uFF1A${err.message}` };
19484
- }
19485
- if (!res.ok) return { error: `\u4E0B\u8F7D\u5931\u8D25 HTTP ${res.status}` };
19486
- if (!res.body) return { error: "\u4E0B\u8F7D\u54CD\u5E94\u6CA1\u6709 body" };
19487
- const expected = bin.size && bin.size > 0 ? bin.size : void 0;
19488
- const limit = expected ? Math.max(expected * 2, 1024 * 1024) : MAX_BINARY_BYTES;
19489
- const hash = createHash3("sha256");
19490
- let bytes = 0;
19491
- try {
19492
- const source = Readable2.fromWeb(res.body);
19493
- await pipeline2(
19494
- source,
19495
- async function* (chunks) {
19496
- for await (const chunk of chunks) {
19497
- bytes += chunk.length;
19498
- if (bytes > limit) throw new Error(`\u4EA7\u7269\u8D85\u8FC7\u5927\u5C0F\u4E0A\u9650\uFF08>${limit} \u5B57\u8282\uFF09`);
19499
- hash.update(chunk);
19500
- yield chunk;
19501
- }
19502
- },
19503
- fs26.createWriteStream(partPath, { mode: 493 })
19504
- );
19505
- } catch (err) {
19506
- fs26.rmSync(partPath, { force: true });
19507
- return { error: `\u4E0B\u8F7D\u4E2D\u65AD\uFF1A${err.message}` };
19508
- }
19509
- const actual = hash.digest("hex");
19510
- if (actual !== bin.sha256) {
19511
- fs26.rmSync(partPath, { force: true });
19512
- return { error: `SHA-256 \u4E0D\u5339\u914D\uFF08\u671F\u671B ${bin.sha256.slice(0, 12)}\u2026\uFF0C\u5B9E\u9645 ${actual.slice(0, 12)}\u2026\uFF09` };
19513
- }
19514
- if (expected && bytes !== expected) {
19515
- fs26.rmSync(partPath, { force: true });
19516
- return { error: `\u4EA7\u7269\u5927\u5C0F\u4E0D\u5339\u914D\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${bytes}\uFF09` };
19517
- }
19518
- fs26.rmSync(destPath, { force: true });
19519
- fs26.renameSync(partPath, destPath);
19520
- fs26.chmodSync(destPath, 493);
19521
- log(`\u5DF2\u4E0B\u8F7D ${path33.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
19522
- return { bytes };
19523
- }
19524
- function selfCheck(binPath, expectedVersion) {
19525
- try {
19526
- const r = spawnSync3(binPath, ["--version"], {
19527
- timeout: 2e4,
19528
- encoding: "utf-8",
19529
- env: { ...process.env, ZQAGENT_AUTO_UPDATE: "0" }
19530
- });
19531
- if (r.error) return { ok: false, error: `\u6267\u884C\u81EA\u68C0\u5931\u8D25\uFF1A${r.error.message}` };
19532
- if (r.status !== 0) return { ok: false, error: `\u81EA\u68C0\u9000\u51FA\u7801 ${r.status}\uFF1A${(r.stderr || "").trim()}` };
19533
- const out = `${r.stdout ?? ""}${r.stderr ?? ""}`;
19534
- if (!out.includes(expectedVersion)) {
19535
- return { ok: false, error: `\u81EA\u68C0\u7248\u672C\u4E0D\u7B26\uFF08\u671F\u671B ${expectedVersion}\uFF0C\u8F93\u51FA\uFF1A${out.trim().slice(0, 120)}\uFF09` };
19536
- }
19537
- return { ok: true };
19538
- } catch (err) {
19539
- return { ok: false, error: `\u81EA\u68C0\u5F02\u5E38\uFF1A${err.message}` };
19540
- }
19541
- }
19542
-
19543
- // src/update/platform.ts
19544
- init_esm_shims();
19545
- import * as fs27 from "fs";
19546
- import * as os8 from "os";
19547
- import * as path34 from "path";
19548
- import { spawnSync as spawnSync4 } from "child_process";
19549
- import { createRequire as createRequire3 } from "module";
19550
- function currentVersion() {
19551
- const injected = "0.2.10"?.trim();
19552
- if (injected) return injected;
19553
- try {
19554
- const req2 = createRequire3(import.meta.url);
19555
- const pkg = req2("../../package.json");
19556
- if (pkg?.version) return pkg.version;
19557
- } catch {
19558
- }
19559
- return "dev";
19560
- }
19561
- function isSeaRuntime() {
19562
- if (process.env.ZQAGENT_SEA === "1") return true;
19563
- const base = path34.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
19564
- return base !== "node" && base !== "nodejs";
19565
- }
19566
- var DEFAULT_SERVICE_NAME = "zqagent";
19567
- var DEFAULT_SERVICE_LABEL = "com.zqagent.node";
19568
- function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
19569
- const unitName = `${serviceName}.service`;
19570
- for (const line of cgroup.split("\n")) {
19571
- const idx = line.lastIndexOf(":");
19572
- if (idx < 0) continue;
19573
- const unitPath = line.slice(idx + 1).trim();
19574
- if (unitPath.split("/").includes(unitName)) return "systemd";
19575
- }
19576
- return null;
19577
- }
19578
- function launchdDomain() {
19579
- const uid = typeof process.getuid === "function" ? process.getuid() : -1;
19580
- return uid === 0 ? "system" : `gui/${uid}`;
19581
- }
19582
- function launchdLabel() {
19583
- return process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL;
19584
- }
19585
- function launchdPlistPath() {
19586
- const label = launchdLabel();
19587
- const uid = typeof process.getuid === "function" ? process.getuid() : -1;
19588
- if (uid === 0) return `/Library/LaunchDaemons/${label}.plist`;
19589
- return path34.join(os8.homedir(), "Library", "LaunchAgents", `${label}.plist`);
19590
- }
19591
- function serviceManagerFromLaunchctl(output, pid, execPath, sea = true) {
19592
- const pidMatch = /^\s*pid = (\d+)\s*$/m.exec(output);
19593
- if (pidMatch && Number(pidMatch[1]) === pid) return "launchd";
19594
- const program = /^\s*program = (.+?)\s*$/m.exec(output);
19595
- if (program) {
19596
- const shown = program[1].replace(/^"|"$/g, "");
19597
- if (path34.resolve(shown) === path34.resolve(execPath)) return "launchd";
19598
- }
19599
- if (!sea && /^\s*state = /m.test(output)) return "launchd";
19600
- return null;
19601
- }
19602
- function parseLaunchdEnvironment(xml) {
19603
- const out = {};
19604
- const marker = xml.indexOf("<key>EnvironmentVariables</key>");
19605
- if (marker < 0) return out;
19606
- const open = xml.indexOf("<dict>", marker);
19607
- if (open < 0) return out;
19608
- const close = xml.indexOf("</dict>", open);
19609
- if (close < 0) return out;
19610
- const body = xml.slice(open + "<dict>".length, close);
19611
- for (const m of body.matchAll(/<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g)) {
19612
- out[m[1]] = m[2];
19613
- }
19614
- return out;
19615
- }
19616
- function detectServiceManager() {
19617
- if (process.platform === "darwin") return detectLaunchd();
19618
- if (process.platform !== "linux") return null;
19619
- const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
19620
- try {
19621
- return serviceManagerFromCgroup(
19622
- fs27.readFileSync("/proc/self/cgroup", "utf8"),
19623
- serviceName
19624
- );
19625
- } catch {
19626
- return null;
19627
- }
19628
- }
19629
- function detectLaunchd() {
19630
- try {
19631
- const res = spawnSync4(
19632
- "launchctl",
19633
- ["print", `${launchdDomain()}/${launchdLabel()}`],
19634
- { encoding: "utf8", timeout: 3e3 }
19635
- );
19636
- if (res.status !== 0 || !res.stdout) return null;
19637
- return serviceManagerFromLaunchctl(
19638
- res.stdout,
19639
- process.pid,
19640
- process.execPath,
19641
- isSeaRuntime()
19642
- );
19643
- } catch {
19644
- return null;
19645
- }
19646
- }
19647
- function serviceManager() {
19648
- const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
19649
- if (v === "systemd" || v === "launchd") return v;
19650
- return detectServiceManager();
19651
- }
19652
- function parseServiceEnvironment(raw) {
19653
- const out = {};
19654
- for (const token of raw.trim().split(/\s+/)) {
19655
- const idx = token.indexOf("=");
19656
- if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
19657
- }
19658
- return out;
19659
- }
19660
- var cachedServiceEnv = null;
19661
- function readServiceEnvironment() {
19662
- const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
19663
- try {
19664
- const res = spawnSync4(
19665
- "systemctl",
19666
- ["show", serviceName, "--property=Environment", "--value", "--no-pager"],
19667
- { encoding: "utf8", timeout: 3e3 }
19668
- );
19669
- if (res.status !== 0 || !res.stdout) return {};
19670
- return parseServiceEnvironment(res.stdout);
19671
- } catch {
19672
- return {};
20161
+ parsed = JSON.parse(raw.toString("utf-8"));
20162
+ } catch (err) {
20163
+ return { error: `\u6E05\u5355\u4E0D\u662F\u5408\u6CD5 JSON\uFF1A${err.message}` };
19673
20164
  }
20165
+ const { manifest, error: error2 } = parseManifest(parsed, opts);
20166
+ if (!manifest) return { error: error2 };
20167
+ return { manifest, raw };
19674
20168
  }
19675
- function serviceEnvValue(key) {
19676
- if (process.platform === "darwin") {
19677
- if (serviceManager() !== "launchd") return void 0;
19678
- cachedLaunchdEnv ??= readLaunchdEnvironment();
19679
- return cachedLaunchdEnv[key];
20169
+ async function downloadBinary(bin, destPath, log = () => {
20170
+ }) {
20171
+ const partPath = `${destPath}.part`;
20172
+ fs27.mkdirSync(path35.dirname(destPath), { recursive: true });
20173
+ fs27.rmSync(partPath, { force: true });
20174
+ let res;
20175
+ try {
20176
+ res = await fetch(bin.url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS2) });
20177
+ } catch (err) {
20178
+ return { error: `\u4E0B\u8F7D\u5931\u8D25\uFF1A${err.message}` };
19680
20179
  }
19681
- if (process.platform !== "linux") return void 0;
19682
- if (serviceManager() !== "systemd") return void 0;
19683
- cachedServiceEnv ??= readServiceEnvironment();
19684
- return cachedServiceEnv[key];
19685
- }
19686
- var cachedLaunchdEnv = null;
19687
- function readLaunchdEnvironment() {
20180
+ if (!res.ok) return { error: `\u4E0B\u8F7D\u5931\u8D25 HTTP ${res.status}` };
20181
+ if (!res.body) return { error: "\u4E0B\u8F7D\u54CD\u5E94\u6CA1\u6709 body" };
20182
+ const expected = bin.size && bin.size > 0 ? bin.size : void 0;
20183
+ const limit = expected ? Math.max(expected * 2, 1024 * 1024) : MAX_BINARY_BYTES;
20184
+ const hash = createHash3("sha256");
20185
+ let bytes = 0;
19688
20186
  try {
19689
- return parseLaunchdEnvironment(fs27.readFileSync(launchdPlistPath(), "utf8"));
19690
- } catch {
19691
- return {};
20187
+ const source = Readable2.fromWeb(res.body);
20188
+ await pipeline2(
20189
+ source,
20190
+ async function* (chunks) {
20191
+ for await (const chunk of chunks) {
20192
+ bytes += chunk.length;
20193
+ if (bytes > limit) throw new Error(`\u4EA7\u7269\u8D85\u8FC7\u5927\u5C0F\u4E0A\u9650\uFF08>${limit} \u5B57\u8282\uFF09`);
20194
+ hash.update(chunk);
20195
+ yield chunk;
20196
+ }
20197
+ },
20198
+ fs27.createWriteStream(partPath, { mode: 493 })
20199
+ );
20200
+ } catch (err) {
20201
+ fs27.rmSync(partPath, { force: true });
20202
+ return { error: `\u4E0B\u8F7D\u4E2D\u65AD\uFF1A${err.message}` };
19692
20203
  }
19693
- }
19694
- function selfUpdateCapability(hasPublicKey) {
19695
- if (!isSeaRuntime()) {
19696
- return {
19697
- ok: false,
19698
- reason: "\u5F53\u524D\u4E0D\u662F\u5355\u6587\u4EF6\uFF08SEA\uFF09\u5F62\u6001\uFF1A\u8BF7\u7528 npm \u5347\u7EA7\uFF08npm i -g @zqagent/core@latest\uFF09"
19699
- };
20204
+ const actual = hash.digest("hex");
20205
+ if (actual !== bin.sha256) {
20206
+ fs27.rmSync(partPath, { force: true });
20207
+ return { error: `SHA-256 \u4E0D\u5339\u914D\uFF08\u671F\u671B ${bin.sha256.slice(0, 12)}\u2026\uFF0C\u5B9E\u9645 ${actual.slice(0, 12)}\u2026\uFF09` };
19700
20208
  }
19701
- if (!serviceManager()) {
19702
- return {
19703
- ok: false,
19704
- reason: "\u672A\u88AB systemd/launchd \u6258\u7BA1\uFF1A\u8BF7\u7528 deploy/service/install.sh \u5B89\u88C5\uFF0C\u6216\u624B\u52A8\u5347\u7EA7"
19705
- };
20209
+ if (expected && bytes !== expected) {
20210
+ fs27.rmSync(partPath, { force: true });
20211
+ return { error: `\u4EA7\u7269\u5927\u5C0F\u4E0D\u5339\u914D\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${bytes}\uFF09` };
19706
20212
  }
19707
- if (!hasPublicKey) {
19708
- return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
20213
+ fs27.rmSync(destPath, { force: true });
20214
+ fs27.renameSync(partPath, destPath);
20215
+ fs27.chmodSync(destPath, 493);
20216
+ log(`\u5DF2\u4E0B\u8F7D ${path35.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
20217
+ return { bytes };
20218
+ }
20219
+ function selfCheck(binPath, expectedVersion) {
20220
+ try {
20221
+ const r = spawnSync4(binPath, ["--version"], {
20222
+ timeout: 2e4,
20223
+ encoding: "utf-8",
20224
+ env: { ...process.env, ZQAGENT_AUTO_UPDATE: "0" }
20225
+ });
20226
+ if (r.error) return { ok: false, error: `\u6267\u884C\u81EA\u68C0\u5931\u8D25\uFF1A${r.error.message}` };
20227
+ if (r.status !== 0) return { ok: false, error: `\u81EA\u68C0\u9000\u51FA\u7801 ${r.status}\uFF1A${(r.stderr || "").trim()}` };
20228
+ const out = `${r.stdout ?? ""}${r.stderr ?? ""}`;
20229
+ if (!out.includes(expectedVersion)) {
20230
+ return { ok: false, error: `\u81EA\u68C0\u7248\u672C\u4E0D\u7B26\uFF08\u671F\u671B ${expectedVersion}\uFF0C\u8F93\u51FA\uFF1A${out.trim().slice(0, 120)}\uFF09` };
20231
+ }
20232
+ return { ok: true };
20233
+ } catch (err) {
20234
+ return { ok: false, error: `\u81EA\u68C0\u5F02\u5E38\uFF1A${err.message}` };
19709
20235
  }
19710
- return { ok: true };
19711
20236
  }
19712
20237
 
19713
20238
  // src/update/installer.ts
19714
20239
  init_esm_shims();
19715
20240
  import * as fs29 from "fs";
19716
- import * as path36 from "path";
20241
+ import * as path37 from "path";
19717
20242
 
19718
20243
  // src/update/state.ts
19719
20244
  init_esm_shims();
19720
20245
  import * as fs28 from "fs";
19721
- import * as path35 from "path";
20246
+ import * as path36 from "path";
19722
20247
  function updatesDir(dataDir) {
19723
- return path35.join(dataDir, "updates");
20248
+ return path36.join(dataDir, "updates");
19724
20249
  }
19725
20250
  function statePath(dataDir) {
19726
- return path35.join(updatesDir(dataDir), "state.json");
20251
+ return path36.join(updatesDir(dataDir), "state.json");
19727
20252
  }
19728
20253
  function loadState(dataDir) {
19729
20254
  try {
@@ -19750,12 +20275,12 @@ function patchState(dataDir, patch) {
19750
20275
 
19751
20276
  // src/update/installer.ts
19752
20277
  function backupDir(dataDir) {
19753
- return path36.join(updatesDir(dataDir), "backup");
20278
+ return path37.join(updatesDir(dataDir), "backup");
19754
20279
  }
19755
20280
  function backupCurrent(dataDir, execPath, version) {
19756
20281
  const dir = backupDir(dataDir);
19757
20282
  fs29.mkdirSync(dir, { recursive: true });
19758
- const target = path36.join(dir, `zqagent-${version}${process.platform === "win32" ? ".exe" : ""}`);
20283
+ const target = path37.join(dir, `zqagent-${version}${process.platform === "win32" ? ".exe" : ""}`);
19759
20284
  fs29.copyFileSync(execPath, target);
19760
20285
  fs29.chmodSync(target, 493);
19761
20286
  return target;
@@ -19787,7 +20312,7 @@ function installStaged(opts) {
19787
20312
  const from = currentVersion();
19788
20313
  log(`\u6B63\u5728\u5B89\u88C5 v${opts.version}\uFF08\u5F53\u524D v${from}\uFF09`);
19789
20314
  const backupPath = backupCurrent(opts.dataDir, opts.execPath, from);
19790
- log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${path36.basename(backupPath)}`);
20315
+ log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${path37.basename(backupPath)}`);
19791
20316
  replaceExecutable(opts.execPath, opts.stagedPath);
19792
20317
  return patchState(opts.dataDir, {
19793
20318
  stagedVersion: void 0,
@@ -19831,7 +20356,7 @@ function rollback(dataDir, execPath, log = () => {
19831
20356
  }
19832
20357
  function readRollbackTarget(dataDir) {
19833
20358
  try {
19834
- const raw = fs29.readFileSync(path36.join(updatesDir(dataDir), "state.json"), "utf-8");
20359
+ const raw = fs29.readFileSync(path37.join(updatesDir(dataDir), "state.json"), "utf-8");
19835
20360
  const s = JSON.parse(raw);
19836
20361
  if (s.backupPath && s.backupVersion) return s;
19837
20362
  } catch {
@@ -20008,7 +20533,7 @@ var UpdateController = class {
20008
20533
  if (!bin) return `\u6E05\u5355\u6CA1\u6709\u5F53\u524D\u5E73\u53F0\uFF08${platformKey()}\uFF09\u7684\u4EA7\u7269`;
20009
20534
  let stagedPath = state.stagedPath;
20010
20535
  if (!(state.stagedVersion === manifest.version && stagedPath && fs30.existsSync(stagedPath))) {
20011
- stagedPath = path37.join(
20536
+ stagedPath = path38.join(
20012
20537
  updatesDir(this.opts.dataDir),
20013
20538
  "staged",
20014
20539
  `zqagent-${manifest.version}${process.platform === "win32" ? ".exe" : ""}`
@@ -20183,7 +20708,7 @@ function seedDemoTenant(store) {
20183
20708
  apiKey: env2("OPENAI_API_KEY"),
20184
20709
  baseURL: env2("OPENAI_BASE_URL")
20185
20710
  },
20186
- workspaceRoot: workspace ? path38.resolve(workspace) : void 0,
20711
+ workspaceRoot: workspace ? path39.resolve(workspace) : void 0,
20187
20712
  allowedCommands: []
20188
20713
  };
20189
20714
  store.saveTenant(demo);
@@ -20237,9 +20762,9 @@ function syncTenantModels(store) {
20237
20762
  tenant.model = model;
20238
20763
  changed = true;
20239
20764
  }
20240
- if (workspace && tenant.workspaceRoot !== path38.resolve(workspace)) {
20241
- console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${path38.resolve(workspace)}`);
20242
- tenant.workspaceRoot = path38.resolve(workspace);
20765
+ if (workspace && tenant.workspaceRoot !== path39.resolve(workspace)) {
20766
+ console.log(`[sync] \u79DF\u6237 ${id} workspaceRoot \u5DF2\u540C\u6B65: ${tenant.workspaceRoot ?? "(\u6C99\u7BB1)"} \u2192 ${path39.resolve(workspace)}`);
20767
+ tenant.workspaceRoot = path39.resolve(workspace);
20243
20768
  changed = true;
20244
20769
  }
20245
20770
  if (apiKey && tenant.provider.apiKey !== apiKey) {
@@ -20300,7 +20825,7 @@ function listenWithPortFallback(server, startPort, host, dataDir, limit = PORT_F
20300
20825
  }
20301
20826
  try {
20302
20827
  fs31.mkdirSync(dataDir, { recursive: true });
20303
- fs31.writeFileSync(path38.join(dataDir, "server-port"), `${actualPort}
20828
+ fs31.writeFileSync(path39.join(dataDir, "server-port"), `${actualPort}
20304
20829
  `);
20305
20830
  } catch {
20306
20831
  }
@@ -20311,7 +20836,7 @@ function listenWithPortFallback(server, startPort, host, dataDir, limit = PORT_F
20311
20836
  });
20312
20837
  }
20313
20838
  function startServer(opts = {}) {
20314
- const dataDir = path38.resolve(opts.dataDir ?? env2("DATA_DIR") ?? DEFAULT_DATA_DIR);
20839
+ const dataDir = path39.resolve(opts.dataDir ?? env2("DATA_DIR") ?? DEFAULT_DATA_DIR);
20315
20840
  const port = opts.port ?? Number(env2("PORT") ?? 8787);
20316
20841
  const host = opts.host ?? env2("HOST") ?? "127.0.0.1";
20317
20842
  exitWhenOrphaned();
@@ -20341,7 +20866,7 @@ function startServer(opts = {}) {
20341
20866
  }
20342
20867
  const adminKey = ensureAdminKey();
20343
20868
  const secretKey = env2("SECRET_KEY");
20344
- const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(path38.join(dataDir, "secret.key"));
20869
+ const secretBox = secretKey ? SecretBox.fromHex(secretKey) : SecretBox.fromFile(path39.join(dataDir, "secret.key"));
20345
20870
  if (secretKey && !secretBox) {
20346
20871
  console.warn("[crypto] SECRET_KEY \u975E\u6CD5\uFF08\u9700 64 \u4F4D hex\uFF09\uFF0C\u901A\u77E5\u51ED\u636E\u5C06\u9000\u5316\u4E3A\u660E\u6587");
20347
20872
  }
@@ -20350,7 +20875,7 @@ function startServer(opts = {}) {
20350
20875
  let fleet = null;
20351
20876
  let fleetCred = null;
20352
20877
  function connectFleet(cred) {
20353
- const localId = ensureNodeId2(store);
20878
+ const localId = ensureNodeId(store);
20354
20879
  const nodeId = (cred.nodeId ?? "").trim() || localId;
20355
20880
  if (nodeId !== localId) store.saveNodeId(nodeId);
20356
20881
  fleet?.close();
@@ -20378,7 +20903,7 @@ function startServer(opts = {}) {
20378
20903
  function fleetStatus() {
20379
20904
  return {
20380
20905
  linked: fleet !== null,
20381
- nodeId: ensureNodeId2(store),
20906
+ nodeId: ensureNodeId(store),
20382
20907
  hubUrl: fleetCred?.hubUrl,
20383
20908
  gatewayUrl: fleetCred?.gatewayUrl ?? env2("FLEET_GATEWAY_URL"),
20384
20909
  online: fleet?.isConnected() ?? false,
@@ -20390,8 +20915,15 @@ function startServer(opts = {}) {
20390
20915
  const fleetController = {
20391
20916
  status: fleetStatus,
20392
20917
  async enroll(input) {
20393
- const nodeId = ensureNodeId2(store);
20394
- const cred = await hubEnrollByCode(input.hubUrl, {
20918
+ const nodeId = ensureNodeId(store);
20919
+ const cred = input.account ? await hubEnrollByAccount(input.hubUrl, {
20920
+ email: input.account.email,
20921
+ phone: input.account.phone,
20922
+ password: input.account.password,
20923
+ code: input.account.smsCode,
20924
+ nodeId,
20925
+ label: input.label
20926
+ }) : await hubEnrollByCode(input.hubUrl, {
20395
20927
  code: input.code,
20396
20928
  nodeId,
20397
20929
  label: input.label
@@ -20417,7 +20949,7 @@ function startServer(opts = {}) {
20417
20949
  gatewayUrl: fleetCred.gatewayUrl ?? env2("FLEET_GATEWAY_URL"),
20418
20950
  fleetToken: fleetCred.fleetToken,
20419
20951
  fleetSecret: fleetCred.fleetSecret,
20420
- nodeId: ensureNodeId2(store)
20952
+ nodeId: ensureNodeId(store)
20421
20953
  };
20422
20954
  }
20423
20955
  };
@@ -20570,7 +21102,7 @@ function printReady(info, qq) {
20570
21102
  `zqagent server \u76D1\u542C http://${info.host}:${info.port}`,
20571
21103
  `\u6570\u636E\u76EE\u5F55: ${info.dataDir}`,
20572
21104
  `\u5BA1\u6279\u6302\u8D77\u8D85\u65F6: ${env2("APPROVAL_TIMEOUT_SECONDS") ?? 900}s\uFF08\u8D85\u65F6\u540E\u56DE\u6536\u5185\u5B58\u4EFB\u52A1\uFF0C\u53EF\u7ECF resume \u6062\u590D\uFF09`,
20573
- env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${path38.join(info.dataDir, "admin.json")}\uFF09` : `\u7BA1\u7406\u5458\u5BC6\u94A5: ${info.adminKey}\uFF08\u63A7\u5236\u53F0\u591A\u9879\u76EE\u5165\u53E3\uFF1A/api/v1/projects\uFF1B\u9879\u76EE\u7EA7\u63A5\u53E3\u52A0 X-Project-Id \u5934\uFF09`
21105
+ env2("ZQAGENT_LOG_ADMIN_KEY") === "0" ? `\u7BA1\u7406\u5458\u5BC6\u94A5: ${maskKey(info.adminKey)}\uFF08\u5DF2\u8131\u654F\uFF0C\u5B8C\u6574\u503C\u89C1 ${path39.join(info.dataDir, "admin.json")}\uFF09` : `\u7BA1\u7406\u5458\u5BC6\u94A5: ${info.adminKey}\uFF08\u63A7\u5236\u53F0\u591A\u9879\u76EE\u5165\u53E3\uFF1A/api/v1/projects\uFF1B\u9879\u76EE\u7EA7\u63A5\u53E3\u52A0 X-Project-Id \u5934\uFF09`
20574
21106
  ];
20575
21107
  if (info.fleetNodeId) {
20576
21108
  lines.push(
@@ -20589,152 +21121,6 @@ function printReady(info, qq) {
20589
21121
  for (const l of lines) console.log(l);
20590
21122
  }
20591
21123
 
20592
- // src/cli/account-enroll.ts
20593
- init_esm_shims();
20594
- import * as path39 from "path";
20595
- import * as readline from "readline/promises";
20596
- var SKIP_ANSWERS = /* @__PURE__ */ new Set(["-", "skip", "none", "no"]);
20597
- function resolveDefaultHub(hubUrl, env3 = process.env) {
20598
- return (hubUrl ?? env3.HUB_URL ?? DEFAULT_HUB_URL).trim().replace(/\/$/, "");
20599
- }
20600
- function parseHubAnswer(answer, fallback) {
20601
- const a = answer.trim();
20602
- if (SKIP_ANSWERS.has(a.toLowerCase())) return null;
20603
- return (a || fallback).replace(/\/$/, "");
20604
- }
20605
- function resolveDataDir2(dataDir) {
20606
- return path39.resolve(dataDir ?? process.env.DATA_DIR ?? DEFAULT_DATA_DIR);
20607
- }
20608
- async function maybeAccountEnroll(opts = {}) {
20609
- if (!process.stdin.isTTY || !process.stdout.isTTY) return;
20610
- let store;
20611
- try {
20612
- store = new Store(resolveDataDir2(opts.dataDir));
20613
- if (resolveFleetConfig(store)) return;
20614
- } catch (e) {
20615
- cliErr(`\u63A5\u5165\u68C0\u67E5\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
20616
- return;
20617
- }
20618
- const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
20619
- try {
20620
- await runWizard(rl2, store, opts);
20621
- } catch (e) {
20622
- cliErr(`\u63A5\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
20623
- cliLog(" \u53EF\u7A0D\u540E\u5728\u63A7\u5236\u53F0\u751F\u6210\u63A5\u5165\u7801\uFF0C\u518D\u6267\u884C\uFF1Azqagent link --hub <Hub\u5730\u5740> --code <\u63A5\u5165\u7801>");
20624
- } finally {
20625
- rl2.close();
20626
- }
20627
- }
20628
- async function runWizard(rl2, store, opts) {
20629
- cliLog("");
20630
- cliLog("\u672C\u673A\u5C1A\u672A\u63A5\u5165 Hub\u3002\u53EF\u7528 Hub \u8D26\u53F7\u63A5\u5165\uFF08\u8282\u70B9\u9ED8\u8BA4\u8FDB\u5165\u300C\u672A\u5206\u7EC4\u300D\uFF09\uFF1B\u8F93\u5165 - \u8DF3\u8FC7\u4FDD\u6301\u7EAF\u672C\u5730\u3002");
20631
- const fallback = resolveDefaultHub(opts.hubUrl);
20632
- const answer = await rl2.question(`Hub \u5730\u5740 [${fallback}]\uFF1A`);
20633
- const hubUrl = parseHubAnswer(answer, fallback);
20634
- if (!hubUrl) {
20635
- cliLog("\u5DF2\u8DF3\u8FC7\u63A5\u5165\uFF1A\u8282\u70B9\u4FDD\u6301\u7EAF\u672C\u5730\u8FD0\u884C\u3002");
20636
- return;
20637
- }
20638
- cliLog("\u767B\u5F55\u65B9\u5F0F\uFF1A1) \u90AE\u7BB1 + \u5BC6\u7801 2) \u624B\u673A\u53F7 + \u5BC6\u7801 3) \u624B\u673A\u53F7 + \u77ED\u4FE1\u9A8C\u8BC1\u7801");
20639
- const mode = (await rl2.question("\u8BF7\u9009\u62E9 [1]\uFF1A")).trim() || "1";
20640
- const input = {};
20641
- if (mode === "1") {
20642
- input.email = (await rl2.question("\u90AE\u7BB1\uFF1A")).trim();
20643
- input.password = await askHidden(rl2, "\u5BC6\u7801\uFF1A");
20644
- if (!input.email || !input.password) {
20645
- cliErr("\u90AE\u7BB1\u4E0E\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
20646
- return;
20647
- }
20648
- } else if (mode === "2") {
20649
- input.phone = (await rl2.question("\u624B\u673A\u53F7\uFF1A")).trim();
20650
- input.password = await askHidden(rl2, "\u5BC6\u7801\uFF1A");
20651
- if (!input.phone || !input.password) {
20652
- cliErr("\u624B\u673A\u53F7\u4E0E\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
20653
- return;
20654
- }
20655
- } else if (mode === "3") {
20656
- input.phone = (await rl2.question("\u624B\u673A\u53F7\uFF1A")).trim();
20657
- if (!input.phone) {
20658
- cliErr("\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
20659
- return;
20660
- }
20661
- try {
20662
- const sent = await hubSendSms(hubUrl, { phone: input.phone, purpose: "login" });
20663
- if (sent.debugCode) cliLog(`\uFF08\u8C03\u8BD5\u6A21\u5F0F\u9A8C\u8BC1\u7801\uFF1A${sent.debugCode}\uFF09`);
20664
- else cliLog("\u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0C5 \u5206\u949F\u5185\u6709\u6548\u3002");
20665
- } catch (e) {
20666
- cliErr(`\u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
20667
- cliLog(" \u53EF\u6539\u7528\u65B9\u5F0F 1/2\uFF08\u5BC6\u7801\u767B\u5F55\uFF09\uFF0C\u6216\u5728\u7F51\u9875\u7AEF\u751F\u6210\u63A5\u5165\u7801\u540E\u6267\u884C\uFF1A");
20668
- cliLog(" zqagent link --hub <Hub\u5730\u5740> --code <\u63A5\u5165\u7801>");
20669
- return;
20670
- }
20671
- input.code = (await rl2.question("\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF1A")).trim();
20672
- if (!input.code) {
20673
- cliErr("\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
20674
- return;
20675
- }
20676
- } else {
20677
- cliErr("\u672A\u77E5\u7684\u767B\u5F55\u65B9\u5F0F\uFF0C\u5DF2\u8DF3\u8FC7\u63A5\u5165\u3002");
20678
- return;
20679
- }
20680
- const nodeId = ensureNodeId2(store);
20681
- const cred = await hubEnrollByAccount(hubUrl, { ...input, nodeId, label: opts.label });
20682
- store.saveFleetCredential({
20683
- ...cred,
20684
- label: opts.label ?? cred.label,
20685
- enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
20686
- });
20687
- cliLog(`\u2705 \u5DF2\u63A5\u5165 Hub\uFF1A\u8282\u70B9 ${cred.nodeId}\uFF08\u7F51\u5173 ${cred.gatewayUrl ?? "\u672A\u4E0B\u53D1"}\uFF09`);
20688
- cliLog(" \u51ED\u636E\u5DF2\u4FDD\u5B58\u5230 fleet.json\uFF0C\u672C\u6B21\u542F\u52A8\u5373\u8FDE\u63A5 Hub\u3002");
20689
- }
20690
- async function askHidden(rl2, prompt) {
20691
- if (!process.stdin.isTTY) return (await rl2.question(prompt)).trim();
20692
- return new Promise((resolve24) => {
20693
- const saved = process.stdin.listeners("data");
20694
- process.stdin.removeAllListeners("data");
20695
- cliWrite(prompt);
20696
- rl2.pause();
20697
- process.stdin.setRawMode(true);
20698
- process.stdin.resume();
20699
- let buf = "";
20700
- const cleanup = () => {
20701
- process.stdin.removeListener("data", onData);
20702
- for (const listener of saved) {
20703
- process.stdin.on("data", listener);
20704
- }
20705
- process.stdin.setRawMode(true);
20706
- rl2.resume();
20707
- };
20708
- const onData = (chunk) => {
20709
- const s = chunk.toString().replace(/\x1b\[[0-9;?]*[A-Za-z~]/g, "").replace(/\x1bO[A-Za-z]/g, "");
20710
- for (const ch of s) {
20711
- if (ch === "\r" || ch === "\n") {
20712
- cleanup();
20713
- cliWrite("\n");
20714
- resolve24(buf);
20715
- return;
20716
- }
20717
- if (ch === "") {
20718
- cleanup();
20719
- cliWrite("\n");
20720
- resolve24("");
20721
- return;
20722
- }
20723
- if (ch === "\x7F" || ch === "\b") {
20724
- if (buf.length > 0) {
20725
- buf = buf.slice(0, -1);
20726
- cliWrite("\b \b");
20727
- }
20728
- } else if (ch >= " ") {
20729
- buf += ch;
20730
- cliWrite("*");
20731
- }
20732
- }
20733
- };
20734
- process.stdin.on("data", onData);
20735
- });
20736
- }
20737
-
20738
21124
  // src/cli/serve.ts
20739
21125
  function parse2(argv) {
20740
21126
  const out = {};
@@ -20747,6 +21133,21 @@ function parse2(argv) {
20747
21133
  } else if (a === "--enroll-code" || a === "--code") {
20748
21134
  out.code = v;
20749
21135
  i++;
21136
+ } else if (a === "--username" || a === "--user") {
21137
+ out.username = v;
21138
+ i++;
21139
+ } else if (a === "--email") {
21140
+ out.email = v;
21141
+ i++;
21142
+ } else if (a === "--phone") {
21143
+ out.phone = v;
21144
+ i++;
21145
+ } else if (a === "--password") {
21146
+ out.password = v;
21147
+ i++;
21148
+ } else if (a === "--sms-code") {
21149
+ out.smsCode = v;
21150
+ i++;
20750
21151
  } else if (a === "--label") {
20751
21152
  out.label = v;
20752
21153
  i++;
@@ -20767,7 +21168,32 @@ async function runServeCommand(argv) {
20767
21168
  const args = parse2(argv);
20768
21169
  const hubUrl = (args.hubUrl ?? process.env.HUB_URL ?? "").trim();
20769
21170
  const code = (args.code ?? process.env.HUB_ENROLL_CODE ?? "").trim();
20770
- if (!(hubUrl && code)) {
21171
+ const account = buildAccountAuth(args);
21172
+ if (code && account) {
21173
+ cliErr("--enroll-code \u4E0E Hub \u8D26\u53F7\u53C2\u6570\uFF08--username/--password \u7B49\uFF09\u53EA\u80FD\u4E8C\u9009\u4E00\u3002");
21174
+ return;
21175
+ }
21176
+ if (account && !isCompleteAccount(account) && !process.stdin.isTTY) {
21177
+ cliErr("\u8D26\u53F7\u53C2\u6570\u7F3A\u5C11\u5BC6\u7801\u6216\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF1A\u975E\u4EA4\u4E92\u73AF\u5883\u8BF7\u8865 --password \u6216 --sms-code\u3002");
21178
+ }
21179
+ if (!code && isCompleteAccount(account)) {
21180
+ try {
21181
+ const r = await linkNode({
21182
+ hubUrl: resolveDefaultHub(args.hubUrl || void 0),
21183
+ account,
21184
+ dataDir: args.dataDir,
21185
+ label: args.label,
21186
+ host: args.host,
21187
+ port: args.port
21188
+ });
21189
+ cliLog(
21190
+ r.mode === "hot" ? `\u2705 \u5DF2\u901A\u8FC7 Hub \u8D26\u53F7\u63A5\u5165\uFF08\u8282\u70B9 ${r.nodeId}\uFF09` : `\u2705 \u5DF2\u4FDD\u5B58 Hub \u63A5\u5165\u51ED\u636E\uFF08\u8282\u70B9 ${r.nodeId}\uFF09`
21191
+ );
21192
+ } catch (e) {
21193
+ cliErr(`Hub \u8D26\u53F7\u63A5\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
21194
+ }
21195
+ }
21196
+ if (!(hubUrl && code) && !isCompleteAccount(account)) {
20771
21197
  await maybeAccountEnroll({ dataDir: args.dataDir, hubUrl: args.hubUrl, label: args.label });
20772
21198
  }
20773
21199
  const handle = startServer({
@@ -20906,7 +21332,7 @@ function updateSessionMetaFile(stateDir2, sessionId, patch) {
20906
21332
  // src/cli/debug.ts
20907
21333
  init_esm_shims();
20908
21334
  import * as fs33 from "fs";
20909
- import * as os9 from "os";
21335
+ import * as os10 from "os";
20910
21336
  import * as path42 from "path";
20911
21337
  var LOG_NAME = "zqagent-debug.log";
20912
21338
  var TRUTHY = /^(1|true|yes|on)$/i;
@@ -20920,7 +21346,7 @@ function resolveLogFile() {
20920
21346
  const explicit = process.env.ZQAGENT_DEBUG_LOG;
20921
21347
  if (explicit) return path42.resolve(explicit);
20922
21348
  const dir = process.env.ZQAGENT_DEBUG_DIR;
20923
- return path42.join(dir ? path42.resolve(dir) : os9.tmpdir(), LOG_NAME);
21349
+ return path42.join(dir ? path42.resolve(dir) : os10.tmpdir(), LOG_NAME);
20924
21350
  }
20925
21351
  function enableDebug(argv = process.argv) {
20926
21352
  if (enabled) return true;
@@ -21342,7 +21768,7 @@ function cycleMode(engine) {
21342
21768
  engine.permissions.mode = next;
21343
21769
  return next;
21344
21770
  }
21345
- var rl = readline2.createInterface({
21771
+ var rl = readline3.createInterface({
21346
21772
  input: process.stdin,
21347
21773
  output: process.stdout
21348
21774
  });
@@ -21358,20 +21784,7 @@ var _exitHandler = null;
21358
21784
  rl.on("SIGINT", () => {
21359
21785
  _exitHandler?.();
21360
21786
  });
21361
- function patchReadlineHistoryNav() {
21362
- const iface = rl;
21363
- const orig = iface._ttyWrite;
21364
- iface._ttyWrite = function(s, key) {
21365
- const k = key;
21366
- if (k && (k.name === "up" || k.name === "down") && !k.ctrl && !k.meta && !k.shift) {
21367
- if (iface.cursor > 0) {
21368
- iface._moveCursor(-iface.cursor);
21369
- }
21370
- }
21371
- orig.call(rl, s, key);
21372
- };
21373
- }
21374
- patchReadlineHistoryNav();
21787
+ patchReadlineHistoryNav(rl);
21375
21788
  dlog("init", "\u6A21\u5757\u521D\u59CB\u5316\u5B8C\u6210\uFF08readline / \u8F93\u5165\u8DEF\u7531 / \u5386\u53F2\u5BFC\u822A\u5C31\u7EEA\uFF09");
21376
21789
  function question(prompt) {
21377
21790
  return inputRouter.ask(prompt);
@@ -21835,7 +22248,7 @@ async function offerTerminalSetup(engine, canUseAgent) {
21835
22248
  }
21836
22249
  var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set(["link", "serve", "update"]);
21837
22250
  function cliVersion() {
21838
- const env3 = "0.2.10"?.trim();
22251
+ const env3 = "0.2.12"?.trim();
21839
22252
  if (env3) return env3;
21840
22253
  try {
21841
22254
  const req2 = createRequire4(import.meta.url);
@@ -21853,11 +22266,13 @@ function printCliUsage() {
21853
22266
  cliLog(" zqagent serve [\u9009\u9879] \u4EE5 headless \u65B9\u5F0F\u5E38\u9A7B\u4E3A\u53EF\u88AB Hub \u7BA1\u7406\u7684\u8282\u70B9");
21854
22267
  cliLog(" zqagent update [--status|--check|--now|--rollback]");
21855
22268
  cliLog(" \u67E5\u770B/\u624B\u52A8\u89E6\u53D1\u8282\u70B9\u81EA\u52A8\u66F4\u65B0");
21856
- cliLog(" zqagent link --hub <url> --code <\u63A5\u5165\u7801> [--label \u540D\u79F0]");
21857
- cliLog(" \u628A\u672C\u673A\u8282\u70B9\u63A5\u5165 Hub\uFF08\u672C\u673A\u6709 server \u65F6\u70ED\u63A5\u5165\uFF09");
22269
+ cliLog(" zqagent link [--hub <url>] [--username <\u90AE\u7BB1/\u624B\u673A\u53F7> --password <\u5BC6\u7801>]");
22270
+ cliLog(" [--code <\u63A5\u5165\u7801>] [--label \u540D\u79F0]");
22271
+ cliLog(" \u628A\u672C\u673A\u8282\u70B9\u63A5\u5165 Hub\uFF08\u8D26\u53F7\u76F4\u8FDE\u6216\u63A5\u5165\u7801\uFF1B\u672C\u673A\u6709 server \u65F6\u70ED\u63A5\u5165\uFF09");
21858
22272
  cliLog("");
21859
22273
  cliLog("serve \u9009\u9879\uFF1A--port <n> --host <addr> --data-dir <dir>");
21860
22274
  cliLog(" --hub <url> --enroll-code <\u7801> --label <\u540D\u79F0> \uFF08\u542F\u52A8\u5373\u63A5\u5165\uFF09");
22275
+ cliLog(" --hub <url> --username <\u90AE\u7BB1/\u624B\u673A\u53F7> --password <\u5BC6\u7801>\uFF08\u8D26\u53F7\u63A5\u5165\uFF09");
21861
22276
  cliLog("\u5176\u5B83\u9009\u9879\uFF1A--resume <\u4F1A\u8BDDID> --debug --help/-h --version/-v");
21862
22277
  }
21863
22278
  async function main() {
@@ -21874,7 +22289,10 @@ async function main() {
21874
22289
  process.exit(0);
21875
22290
  }
21876
22291
  if (subcommand === "link") {
21877
- const ok2 = await runLinkCommand(process.argv.slice(3));
22292
+ const ok2 = await runLinkCommand(process.argv.slice(3), {
22293
+ rl,
22294
+ interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY)
22295
+ });
21878
22296
  rl.close();
21879
22297
  process.exit(ok2 ? 0 : 1);
21880
22298
  }
@@ -22775,7 +23193,11 @@ async function main() {
22775
23193
  case "/link": {
22776
23194
  const code = parts[1];
22777
23195
  const hub = parts[2] ?? process.env.HUB_URL;
22778
- if (!code || !hub) {
23196
+ if (!code) {
23197
+ await runLinkCommand([], { rl, interactive: true });
23198
+ break;
23199
+ }
23200
+ if (!hub) {
22779
23201
  cliLog("\u7528\u6CD5: /link <\u6CE8\u518C\u7801> [Hub\u5730\u5740]\uFF08Hub\u5730\u5740\u4E5F\u53EF\u7531 HUB_URL \u73AF\u5883\u53D8\u91CF\u63D0\u4F9B\uFF09");
22780
23202
  break;
22781
23203
  }