@zqagent/core 0.2.8 → 0.2.11

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.
@@ -3741,10 +3741,10 @@ var OpenAIProvider = class {
3741
3741
  let timer;
3742
3742
  try {
3743
3743
  for (; ; ) {
3744
- const idle = new Promise((resolve23) => {
3744
+ const idle = new Promise((resolve24) => {
3745
3745
  timer = setTimeout(() => {
3746
3746
  onIdle();
3747
- resolve23({ timedOut: true });
3747
+ resolve24({ timedOut: true });
3748
3748
  }, idleMs);
3749
3749
  });
3750
3750
  const next = iter.next();
@@ -6065,12 +6065,12 @@ function makeSink(maxBuffer) {
6065
6065
  };
6066
6066
  }
6067
6067
  function execAsync(file, argv, opts = {}) {
6068
- return new Promise((resolve23) => {
6068
+ return new Promise((resolve24) => {
6069
6069
  const maxBuffer = opts.maxBuffer ?? DEFAULT_MAX_BUFFER;
6070
6070
  const out = makeSink(maxBuffer);
6071
6071
  const err = makeSink(maxBuffer);
6072
6072
  if (opts.signal?.aborted) {
6073
- resolve23({
6073
+ resolve24({
6074
6074
  status: null,
6075
6075
  stdout: Buffer.alloc(0),
6076
6076
  stderr: Buffer.alloc(0),
@@ -6091,7 +6091,7 @@ function execAsync(file, argv, opts = {}) {
6091
6091
  windowsHide: true
6092
6092
  });
6093
6093
  } catch (e) {
6094
- resolve23({
6094
+ resolve24({
6095
6095
  status: null,
6096
6096
  stdout: Buffer.alloc(0),
6097
6097
  stderr: Buffer.alloc(0),
@@ -6128,7 +6128,7 @@ function execAsync(file, argv, opts = {}) {
6128
6128
  };
6129
6129
  if (aborted) result.aborted = true;
6130
6130
  if (spawnError) result.error = spawnError;
6131
- resolve23(result);
6131
+ resolve24(result);
6132
6132
  };
6133
6133
  const killAndSettle = () => {
6134
6134
  killTree(child.pid);
@@ -10094,6 +10094,38 @@ function countRawEscapes(chunk) {
10094
10094
  for (const ch of cleaned) if (ch === "\x1B") escCount++;
10095
10095
  return escCount;
10096
10096
  }
10097
+ function patchReadlineHistoryNav(rl2) {
10098
+ const iface = rl2;
10099
+ const orig = iface._ttyWrite;
10100
+ let draft = null;
10101
+ iface._ttyWrite = function(s, key) {
10102
+ const k = key;
10103
+ const name = k?.name;
10104
+ const plainArrow = (name === "up" || name === "down") && !k?.ctrl && !k?.meta && !k?.shift;
10105
+ if (plainArrow) {
10106
+ if (name === "up" && iface.historyIndex === -1) {
10107
+ draft = { line: iface.line, cursor: iface.cursor };
10108
+ }
10109
+ if (iface.cursor > 0) {
10110
+ iface._moveCursor(-iface.cursor);
10111
+ }
10112
+ }
10113
+ const prevHistoryIndex = iface.historyIndex;
10114
+ orig.call(rl2, s, key);
10115
+ if (plainArrow && name === "down" && prevHistoryIndex >= 0 && iface.historyIndex === -1) {
10116
+ const saved = draft;
10117
+ draft = null;
10118
+ if (saved) {
10119
+ iface.line = saved.line;
10120
+ iface.cursor = Math.min(saved.cursor, saved.line.length);
10121
+ iface._refreshLine?.();
10122
+ }
10123
+ }
10124
+ };
10125
+ rl2.on("line", () => {
10126
+ draft = null;
10127
+ });
10128
+ }
10097
10129
  var PASTE_PLACEHOLDER_PREFIX = "\u7C98\u8D34\u6587\u672C #";
10098
10130
  var InputRouter = class {
10099
10131
  rl;
@@ -10218,8 +10250,8 @@ var InputRouter = class {
10218
10250
  }
10219
10251
  /** 询问一行:显示 prompt,返回该行内容 */
10220
10252
  ask(prompt) {
10221
- return new Promise((resolve23) => {
10222
- this.pendingAsks.push({ prompt, resolve: resolve23 });
10253
+ return new Promise((resolve24) => {
10254
+ this.pendingAsks.push({ prompt, resolve: resolve24 });
10223
10255
  this.pumpAsks();
10224
10256
  });
10225
10257
  }
@@ -10316,16 +10348,16 @@ var import_chalk2 = __toESM(require("chalk"), 1);
10316
10348
  var BRAND_TEXT = "ZQAGENT";
10317
10349
  var BRAND_TAGLINE = "ZQAgent \xB7 AI Agent CLI";
10318
10350
  var GLYPHS = {
10319
- Z: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2588\u2588\u2588\u2554\u255D", " \u2588\u2588\u2588\u2554\u255D ", " \u2588\u2588\u2588\u2554\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
10320
- Q: [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551\u2584\u2584 \u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2580\u2580\u2550\u255D "],
10321
- A: [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2550\u255D \u255A\u2550\u255D"],
10322
- G: [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2551 \u2588\u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
10323
- E: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
10324
- N: ["\u2588\u2588\u2588\u2557 \u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551", "\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551", "\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551", "\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D"],
10325
- T: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D", " \u2588\u2588\u2551 ", " \u2588\u2588\u2551 ", " \u2588\u2588\u2551 ", " \u255A\u2550\u255D "]
10351
+ Z: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ", " \u2588\u2588\u2588 ", " \u2588\u2588\u2588 ", " \u2588\u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ", " "],
10352
+ Q: [" \u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2584\u2584 \u2588\u2588", " \u2588\u2588\u2588\u2588\u2588\u2588 ", " \u2580\u2580 "],
10353
+ A: [" \u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", " "],
10354
+ G: [" \u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588 \u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588\u2588\u2588 ", " "],
10355
+ E: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ", " "],
10356
+ N: ["\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588\u2588\u2588", " "],
10357
+ T: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", " "]
10326
10358
  };
10327
10359
  var GLYPH_ROWS = 6;
10328
- var GLYPH_COLS = 8;
10360
+ var GLYPH_COLS = 9;
10329
10361
  var GRADIENT = [
10330
10362
  [34, 211, 238],
10331
10363
  [99, 102, 241],
@@ -10481,7 +10513,7 @@ function renderTerminalTutorial() {
10481
10513
 
10482
10514
  // src/cli/link.ts
10483
10515
  init_cjs_shims();
10484
- var path21 = __toESM(require("path"), 1);
10516
+ var path22 = __toESM(require("path"), 1);
10485
10517
 
10486
10518
  // src/server/store.ts
10487
10519
  init_cjs_shims();
@@ -10891,11 +10923,186 @@ async function hubEnrollByAccount(baseUrl, input) {
10891
10923
 
10892
10924
  // src/cli/serve-instances.ts
10893
10925
  init_cjs_shims();
10894
- var import_node_child_process4 = require("child_process");
10926
+ var import_node_child_process5 = require("child_process");
10927
+ var fs19 = __toESM(require("fs"), 1);
10928
+ var os5 = __toESM(require("os"), 1);
10929
+ var path21 = __toESM(require("path"), 1);
10930
+
10931
+ // src/update/platform.ts
10932
+ init_cjs_shims();
10895
10933
  var fs18 = __toESM(require("fs"), 1);
10934
+ var os4 = __toESM(require("os"), 1);
10896
10935
  var path20 = __toESM(require("path"), 1);
10936
+ var import_node_child_process4 = require("child_process");
10937
+ var import_node_module3 = require("module");
10938
+ function currentVersion() {
10939
+ const injected = "0.2.11"?.trim();
10940
+ if (injected) return injected;
10941
+ try {
10942
+ const req2 = (0, import_node_module3.createRequire)(importMetaUrl);
10943
+ const pkg = req2("../../package.json");
10944
+ if (pkg?.version) return pkg.version;
10945
+ } catch {
10946
+ }
10947
+ return "dev";
10948
+ }
10949
+ function isSeaRuntime() {
10950
+ if (process.env.ZQAGENT_SEA === "1") return true;
10951
+ const base = path20.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
10952
+ return base !== "node" && base !== "nodejs";
10953
+ }
10954
+ var DEFAULT_SERVICE_NAME = "zqagent";
10955
+ var DEFAULT_SERVICE_LABEL = "com.zqagent.node";
10956
+ function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
10957
+ const unitName = `${serviceName}.service`;
10958
+ for (const line of cgroup.split("\n")) {
10959
+ const idx = line.lastIndexOf(":");
10960
+ if (idx < 0) continue;
10961
+ const unitPath = line.slice(idx + 1).trim();
10962
+ if (unitPath.split("/").includes(unitName)) return "systemd";
10963
+ }
10964
+ return null;
10965
+ }
10966
+ function launchdDomain() {
10967
+ const uid = typeof process.getuid === "function" ? process.getuid() : -1;
10968
+ return uid === 0 ? "system" : `gui/${uid}`;
10969
+ }
10970
+ function launchdLabel() {
10971
+ return process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL;
10972
+ }
10973
+ function launchdPlistPath() {
10974
+ const label = launchdLabel();
10975
+ const uid = typeof process.getuid === "function" ? process.getuid() : -1;
10976
+ if (uid === 0) return `/Library/LaunchDaemons/${label}.plist`;
10977
+ return path20.join(os4.homedir(), "Library", "LaunchAgents", `${label}.plist`);
10978
+ }
10979
+ function serviceManagerFromLaunchctl(output, pid, execPath, sea = true) {
10980
+ const pidMatch = /^\s*pid = (\d+)\s*$/m.exec(output);
10981
+ if (pidMatch && Number(pidMatch[1]) === pid) return "launchd";
10982
+ const program = /^\s*program = (.+?)\s*$/m.exec(output);
10983
+ if (program) {
10984
+ const shown = program[1].replace(/^"|"$/g, "");
10985
+ if (path20.resolve(shown) === path20.resolve(execPath)) return "launchd";
10986
+ }
10987
+ if (!sea && /^\s*state = /m.test(output)) return "launchd";
10988
+ return null;
10989
+ }
10990
+ function parseLaunchdEnvironment(xml) {
10991
+ const out = {};
10992
+ const marker = xml.indexOf("<key>EnvironmentVariables</key>");
10993
+ if (marker < 0) return out;
10994
+ const open = xml.indexOf("<dict>", marker);
10995
+ if (open < 0) return out;
10996
+ const close = xml.indexOf("</dict>", open);
10997
+ if (close < 0) return out;
10998
+ const body = xml.slice(open + "<dict>".length, close);
10999
+ for (const m of body.matchAll(/<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g)) {
11000
+ out[m[1]] = m[2];
11001
+ }
11002
+ return out;
11003
+ }
11004
+ function detectServiceManager() {
11005
+ if (process.platform === "darwin") return detectLaunchd();
11006
+ if (process.platform !== "linux") return null;
11007
+ const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
11008
+ try {
11009
+ return serviceManagerFromCgroup(
11010
+ fs18.readFileSync("/proc/self/cgroup", "utf8"),
11011
+ serviceName
11012
+ );
11013
+ } catch {
11014
+ return null;
11015
+ }
11016
+ }
11017
+ function detectLaunchd() {
11018
+ try {
11019
+ const res = (0, import_node_child_process4.spawnSync)(
11020
+ "launchctl",
11021
+ ["print", `${launchdDomain()}/${launchdLabel()}`],
11022
+ { encoding: "utf8", timeout: 3e3 }
11023
+ );
11024
+ if (res.status !== 0 || !res.stdout) return null;
11025
+ return serviceManagerFromLaunchctl(
11026
+ res.stdout,
11027
+ process.pid,
11028
+ process.execPath,
11029
+ isSeaRuntime()
11030
+ );
11031
+ } catch {
11032
+ return null;
11033
+ }
11034
+ }
11035
+ function serviceManager() {
11036
+ const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
11037
+ if (v === "systemd" || v === "launchd") return v;
11038
+ return detectServiceManager();
11039
+ }
11040
+ function parseServiceEnvironment(raw) {
11041
+ const out = {};
11042
+ for (const token of raw.trim().split(/\s+/)) {
11043
+ const idx = token.indexOf("=");
11044
+ if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
11045
+ }
11046
+ return out;
11047
+ }
11048
+ var cachedServiceEnv = null;
11049
+ function readServiceEnvironment() {
11050
+ const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
11051
+ try {
11052
+ const res = (0, import_node_child_process4.spawnSync)(
11053
+ "systemctl",
11054
+ ["show", serviceName, "--property=Environment", "--value", "--no-pager"],
11055
+ { encoding: "utf8", timeout: 3e3 }
11056
+ );
11057
+ if (res.status !== 0 || !res.stdout) return {};
11058
+ return parseServiceEnvironment(res.stdout);
11059
+ } catch {
11060
+ return {};
11061
+ }
11062
+ }
11063
+ function serviceEnvValue(key) {
11064
+ if (process.platform === "darwin") {
11065
+ if (serviceManager() !== "launchd") return void 0;
11066
+ cachedLaunchdEnv ??= readLaunchdEnvironment();
11067
+ return cachedLaunchdEnv[key];
11068
+ }
11069
+ if (process.platform !== "linux") return void 0;
11070
+ if (serviceManager() !== "systemd") return void 0;
11071
+ cachedServiceEnv ??= readServiceEnvironment();
11072
+ return cachedServiceEnv[key];
11073
+ }
11074
+ var cachedLaunchdEnv = null;
11075
+ function readLaunchdEnvironment() {
11076
+ try {
11077
+ return parseLaunchdEnvironment(fs18.readFileSync(launchdPlistPath(), "utf8"));
11078
+ } catch {
11079
+ return {};
11080
+ }
11081
+ }
11082
+ function selfUpdateCapability(hasPublicKey) {
11083
+ if (!isSeaRuntime()) {
11084
+ return {
11085
+ ok: false,
11086
+ 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"
11087
+ };
11088
+ }
11089
+ if (!serviceManager()) {
11090
+ return {
11091
+ ok: false,
11092
+ reason: "\u672A\u88AB systemd/launchd \u6258\u7BA1\uFF1A\u8BF7\u7528 deploy/service/install.sh \u5B89\u88C5\uFF0C\u6216\u624B\u52A8\u5347\u7EA7"
11093
+ };
11094
+ }
11095
+ if (!hasPublicKey) {
11096
+ return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
11097
+ }
11098
+ return { ok: true };
11099
+ }
11100
+
11101
+ // src/cli/serve-instances.ts
10897
11102
  var DEFAULT_SERVE_HOST = "127.0.0.1";
10898
11103
  var DEFAULT_SERVE_PORT = 8787;
11104
+ var DEFAULT_SERVICE_NAME2 = "zqagent";
11105
+ var DEFAULT_SERVICE_LABEL2 = "com.zqagent.node";
10899
11106
  function serveSubcommandIndex(argv) {
10900
11107
  for (let i = 1; i < argv.length; i++) {
10901
11108
  if (argv[i].startsWith("-")) continue;
@@ -10930,18 +11137,43 @@ function parseServeArgs(argv) {
10930
11137
  return { dataDir, host, port };
10931
11138
  }
10932
11139
  function buildHotCandidates(explicitDataDir, discovered, fallback) {
10933
- if (explicitDataDir) {
10934
- return [{ dataDir: path20.resolve(explicitDataDir), ...fallback }];
10935
- }
10936
- const out = [{ dataDir: path20.resolve(DEFAULT_DATA_DIR), ...fallback }];
10937
- for (const inst of discovered) {
11140
+ const out = [];
11141
+ const push = (inst) => {
10938
11142
  if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
10939
- }
11143
+ };
11144
+ if (explicitDataDir) push({ dataDir: path21.resolve(explicitDataDir), ...fallback });
11145
+ for (const inst of discovered) if (inst.managed) push(inst);
11146
+ for (const inst of discovered) if (!inst.managed) push(inst);
11147
+ if (!explicitDataDir) push({ dataDir: path21.resolve(DEFAULT_DATA_DIR), ...fallback });
10940
11148
  return out;
10941
11149
  }
11150
+ function isWindowsAbsolute(p) {
11151
+ return /^[A-Za-z]:[\\/]/.test(p) || /^\\\\/.test(p);
11152
+ }
11153
+ function isAbsolutePath(p) {
11154
+ return path21.isAbsolute(p) || isWindowsAbsolute(p);
11155
+ }
11156
+ function normalizePath(p) {
11157
+ return isWindowsAbsolute(p) ? path21.win32.normalize(p) : path21.normalize(p);
11158
+ }
11159
+ function parseServeCommandLine(line) {
11160
+ if (!/(?:^|\s)["']?serve["']?(?=\s|$)/.test(line)) return null;
11161
+ const m = /--data-dir(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(.+?))(?=\s+--\w|\s*$)/.exec(line);
11162
+ const rawDataDir = (m?.[1] ?? m?.[2] ?? m?.[3] ?? "").trim();
11163
+ if (!rawDataDir || !isAbsolutePath(rawDataDir)) return null;
11164
+ const host = /--host(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(\S+))/.exec(line);
11165
+ const rawHost = (host?.[1] ?? host?.[2] ?? host?.[3] ?? DEFAULT_SERVE_HOST).trim();
11166
+ const portMatch = /--port(?:=|\s+)(\d+)/.exec(line);
11167
+ const port = Number(portMatch?.[1] ?? DEFAULT_SERVE_PORT);
11168
+ return {
11169
+ dataDir: normalizePath(rawDataDir),
11170
+ host: rawHost === "0.0.0.0" || rawHost === "::" || rawHost === "" ? DEFAULT_SERVE_HOST : rawHost,
11171
+ port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT
11172
+ };
11173
+ }
10942
11174
  function processCwd(pid) {
10943
11175
  try {
10944
- return fs18.readlinkSync(`/proc/${pid}/cwd`);
11176
+ return fs19.readlinkSync(`/proc/${pid}/cwd`);
10945
11177
  } catch {
10946
11178
  return null;
10947
11179
  }
@@ -10949,7 +11181,7 @@ function processCwd(pid) {
10949
11181
  function processEnv(pid) {
10950
11182
  const out = {};
10951
11183
  try {
10952
- const raw = fs18.readFileSync(`/proc/${pid}/environ`, "utf-8");
11184
+ const raw = fs19.readFileSync(`/proc/${pid}/environ`, "utf-8");
10953
11185
  for (const entry of raw.split("\0")) {
10954
11186
  const eq = entry.indexOf("=");
10955
11187
  if (eq > 0) out[entry.slice(0, eq)] = entry.slice(eq + 1);
@@ -10960,14 +11192,24 @@ function processEnv(pid) {
10960
11192
  }
10961
11193
  function resolveDataDir(rawDataDir, cwd, env3) {
10962
11194
  const pick = rawDataDir ?? env3.DATA_DIR ?? null;
10963
- if (pick) return path20.isAbsolute(pick) ? path20.normalize(pick) : cwd ? path20.resolve(cwd, pick) : null;
10964
- return cwd ? path20.resolve(cwd, DEFAULT_DATA_DIR) : null;
11195
+ if (pick) return path21.isAbsolute(pick) ? path21.normalize(pick) : cwd ? path21.resolve(cwd, pick) : null;
11196
+ return cwd ? path21.resolve(cwd, DEFAULT_DATA_DIR) : null;
11197
+ }
11198
+ function isManagedBySystemd(pid, env3) {
11199
+ if (env3.ZQAGENT_SERVICE_MANAGER?.trim()) return true;
11200
+ try {
11201
+ const cgroup = fs19.readFileSync(`/proc/${pid}/cgroup`, "utf-8");
11202
+ const serviceName = env3.ZQAGENT_SERVICE_NAME?.trim() || "zqagent";
11203
+ return serviceManagerFromCgroup(cgroup, serviceName) !== null;
11204
+ } catch {
11205
+ return false;
11206
+ }
10965
11207
  }
10966
11208
  function discoverFromProc() {
10967
11209
  const out = [];
10968
11210
  let pids;
10969
11211
  try {
10970
- pids = fs18.readdirSync("/proc");
11212
+ pids = fs19.readdirSync("/proc");
10971
11213
  } catch {
10972
11214
  return out;
10973
11215
  }
@@ -10976,50 +11218,186 @@ function discoverFromProc() {
10976
11218
  const pid = Number(name);
10977
11219
  if (pid === process.pid) continue;
10978
11220
  let argv;
11221
+ let env3;
10979
11222
  try {
10980
- argv = fs18.readFileSync(`/proc/${pid}/cmdline`, "utf-8").split("\0").filter(Boolean);
11223
+ argv = fs19.readFileSync(`/proc/${pid}/cmdline`, "utf-8").split("\0").filter(Boolean);
11224
+ env3 = processEnv(pid);
10981
11225
  } catch {
10982
11226
  continue;
10983
11227
  }
10984
11228
  const parsed = parseServeArgs(argv);
10985
11229
  if (!parsed) continue;
10986
- const dataDir = resolveDataDir(parsed.dataDir, processCwd(pid), processEnv(pid));
11230
+ const dataDir = resolveDataDir(parsed.dataDir, processCwd(pid), env3);
10987
11231
  if (!dataDir) continue;
10988
- out.push({ dataDir, host: parsed.host, port: parsed.port });
11232
+ out.push({
11233
+ dataDir,
11234
+ host: parsed.host,
11235
+ port: parsed.port,
11236
+ managed: isManagedBySystemd(pid, env3)
11237
+ });
10989
11238
  }
10990
11239
  return out;
10991
11240
  }
10992
11241
  function discoverFromPs() {
10993
11242
  const out = [];
10994
- const res = (0, import_node_child_process4.spawnSync)("ps", ["-axo", "command="], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
11243
+ const res = (0, import_node_child_process5.spawnSync)("ps", ["-axo", "command="], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
10995
11244
  if (res.status !== 0 || !res.stdout) return out;
10996
11245
  for (const line of res.stdout.split("\n")) {
10997
- if (!line.includes(" serve")) continue;
10998
- const m = /--data-dir(?:=|\s+)(.+?)(?=\s+--|\s*$)/.exec(line);
10999
- if (!m) continue;
11000
- const raw = m[1].trim();
11001
- if (!raw || !path20.isAbsolute(raw)) continue;
11002
- const host = /--host(?:=|\s+)(\S+)/.exec(line)?.[1] ?? DEFAULT_SERVE_HOST;
11003
- const port = Number(/--port(?:=|\s+)(\d+)/.exec(line)?.[1] ?? DEFAULT_SERVE_PORT);
11246
+ const inst = parseServeCommandLine(line);
11247
+ if (inst) out.push(inst);
11248
+ }
11249
+ return out;
11250
+ }
11251
+ function discoverFromWindows() {
11252
+ const out = [];
11253
+ const script = `[Console]::OutputEncoding=[Text.Encoding]::UTF8; Get-CimInstance Win32_Process -Filter "Name='zqagent.exe' OR Name='node.exe'" | ForEach-Object { $_.CommandLine }`;
11254
+ const res = (0, import_node_child_process5.spawnSync)(
11255
+ "powershell.exe",
11256
+ ["-NoProfile", "-NonInteractive", "-Command", script],
11257
+ { encoding: "utf-8", timeout: 15e3, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
11258
+ );
11259
+ if (res.status !== 0 || !res.stdout) return out;
11260
+ for (const line of res.stdout.replace(/^\uFEFF/, "").split(/\r?\n/)) {
11261
+ const inst = parseServeCommandLine(line);
11262
+ if (inst) out.push(inst);
11263
+ }
11264
+ return out;
11265
+ }
11266
+ function decodeXmlEntities(s) {
11267
+ return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
11268
+ }
11269
+ function parseLaunchdProgramArguments(xml) {
11270
+ const marker = xml.indexOf("<key>ProgramArguments</key>");
11271
+ if (marker < 0) return [];
11272
+ const open = xml.indexOf("<array>", marker);
11273
+ if (open < 0) return [];
11274
+ const close = xml.indexOf("</array>", open);
11275
+ if (close < 0) return [];
11276
+ return [...xml.slice(open + "<array>".length, close).matchAll(/<string>([^<]*)<\/string>/g)].map(
11277
+ (m) => decodeXmlEntities(m[1])
11278
+ );
11279
+ }
11280
+ function launchdServiceInstances() {
11281
+ if (process.platform !== "darwin") return [];
11282
+ const label = process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL2;
11283
+ const plists = [
11284
+ `/Library/LaunchDaemons/${label}.plist`,
11285
+ path21.join(os5.homedir(), "Library", "LaunchAgents", `${label}.plist`)
11286
+ ];
11287
+ const out = [];
11288
+ for (const plist of plists) {
11289
+ let xml;
11290
+ try {
11291
+ xml = fs19.readFileSync(plist, "utf-8");
11292
+ } catch {
11293
+ continue;
11294
+ }
11295
+ const parsed = parseServeArgs(parseLaunchdProgramArguments(xml));
11296
+ if (!parsed?.dataDir || !isAbsolutePath(parsed.dataDir)) continue;
11004
11297
  out.push({
11005
- dataDir: path20.normalize(raw),
11006
- host: host === "0.0.0.0" || host === "::" ? DEFAULT_SERVE_HOST : host,
11007
- port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT
11298
+ dataDir: normalizePath(parsed.dataDir),
11299
+ host: parsed.host,
11300
+ port: parsed.port,
11301
+ managed: true
11008
11302
  });
11009
11303
  }
11010
11304
  return out;
11011
11305
  }
11306
+ function parseSystemdUnit(text) {
11307
+ let dataDir = null;
11308
+ let host = DEFAULT_SERVE_HOST;
11309
+ let port = DEFAULT_SERVE_PORT;
11310
+ for (const line of text.split("\n")) {
11311
+ const trimmed = line.trim();
11312
+ if (trimmed.startsWith("WorkingDirectory=")) {
11313
+ const v = trimmed.slice("WorkingDirectory=".length).trim();
11314
+ if (v && isAbsolutePath(v)) dataDir = v;
11315
+ } else if (trimmed.startsWith("ExecStart=")) {
11316
+ const tokens = trimmed.slice("ExecStart=".length).trim().split(/\s+/).map((t) => t.replace(/^["']|["']$/g, ""));
11317
+ const parsed = parseServeArgs(tokens);
11318
+ if (!parsed) continue;
11319
+ if (parsed.dataDir && isAbsolutePath(parsed.dataDir)) dataDir = parsed.dataDir;
11320
+ host = parsed.host;
11321
+ port = parsed.port;
11322
+ }
11323
+ }
11324
+ if (!dataDir) return null;
11325
+ return { dataDir: normalizePath(dataDir), host, port, managed: true };
11326
+ }
11327
+ function systemdServiceInstances() {
11328
+ if (process.platform !== "linux") return [];
11329
+ const unit = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME2;
11330
+ const configHome = process.env.XDG_CONFIG_HOME?.trim() || path21.join(os5.homedir(), ".config");
11331
+ const units = [
11332
+ `/etc/systemd/system/${unit}.service`,
11333
+ path21.join(configHome, "systemd", "user", `${unit}.service`)
11334
+ ];
11335
+ const out = [];
11336
+ for (const file of units) {
11337
+ let text;
11338
+ try {
11339
+ text = fs19.readFileSync(file, "utf-8");
11340
+ } catch {
11341
+ continue;
11342
+ }
11343
+ const inst = parseSystemdUnit(text);
11344
+ if (inst) out.push(inst);
11345
+ }
11346
+ return out;
11347
+ }
11348
+ function windowsServiceInstances() {
11349
+ if (process.platform !== "win32") return [];
11350
+ const appdata = process.env.APPDATA?.trim();
11351
+ if (!appdata) return [];
11352
+ const base = path21.join(appdata, "zqagent");
11353
+ try {
11354
+ const raw = fs19.readFileSync(path21.join(base, "runtime", "service.json"), "utf-8");
11355
+ const v = JSON.parse(raw);
11356
+ const dataDir = v.dataDir && isAbsolutePath(v.dataDir) ? v.dataDir : base;
11357
+ const port = Number(v.port);
11358
+ return [
11359
+ {
11360
+ dataDir: normalizePath(dataDir),
11361
+ host: DEFAULT_SERVE_HOST,
11362
+ port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT,
11363
+ managed: true
11364
+ }
11365
+ ];
11366
+ } catch {
11367
+ return [];
11368
+ }
11369
+ }
11370
+ function mergeServeInstances(discovered, managed) {
11371
+ const out = [];
11372
+ const push = (inst) => {
11373
+ if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
11374
+ };
11375
+ for (const inst of discovered) {
11376
+ const hit = managed.find((m) => m.dataDir === inst.dataDir);
11377
+ push(hit ? { ...inst, managed: true } : inst);
11378
+ }
11379
+ for (const inst of managed) push(inst);
11380
+ return out;
11381
+ }
11012
11382
  function discoverRunningServeInstances() {
11013
- const found = process.platform === "linux" ? discoverFromProc() : discoverFromPs();
11014
- const seen = /* @__PURE__ */ new Set();
11015
- return found.filter((inst) => {
11016
- if (seen.has(inst.dataDir)) return false;
11017
- seen.add(inst.dataDir);
11018
- return true;
11019
- });
11383
+ const found = process.platform === "linux" ? discoverFromProc() : process.platform === "win32" ? discoverFromWindows() : discoverFromPs();
11384
+ const declared = process.platform === "linux" ? systemdServiceInstances() : process.platform === "win32" ? windowsServiceInstances() : launchdServiceInstances();
11385
+ return mergeServeInstances(found, declared);
11020
11386
  }
11021
11387
 
11022
11388
  // src/cli/link.ts
11389
+ function expandHome(p) {
11390
+ const home = process.env.HOME || process.env.USERPROFILE;
11391
+ if (!home) return p;
11392
+ if (p === "~") return home;
11393
+ if (p.startsWith("~/") || p.startsWith("~\\")) return path22.join(home, p.slice(2));
11394
+ return p;
11395
+ }
11396
+ function installDataDirHint() {
11397
+ if (process.platform === "darwin") return "$HOME/Library/Application Support/zqagent";
11398
+ if (process.platform === "win32") return "%APPDATA%\\zqagent";
11399
+ return "/var/lib/zqagent\uFF08\u9700 root\uFF1B\u666E\u901A\u7528\u6237\u7528 ~/.zqagent_data\uFF09";
11400
+ }
11023
11401
  function parseLinkArgs(argv) {
11024
11402
  const out = {};
11025
11403
  for (let i = 0; i < argv.length; i++) {
@@ -11092,20 +11470,47 @@ async function enrollViaServer(host, port, adminKey, opts) {
11092
11470
  }
11093
11471
  return payload?.data;
11094
11472
  }
11473
+ function hotWarnings(inst, running) {
11474
+ const warnings = [];
11475
+ const managed = running.filter((r) => r.managed && r.dataDir !== inst.dataDir);
11476
+ if (!inst.managed && managed.length > 0) {
11477
+ warnings.push(
11478
+ `\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`
11479
+ );
11480
+ warnings.push(
11481
+ "\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"
11482
+ );
11483
+ }
11484
+ const others = [...new Set(running.map((r) => r.dataDir).filter((d) => d !== inst.dataDir))];
11485
+ if (others.length > 0) {
11486
+ 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`);
11487
+ }
11488
+ return warnings;
11489
+ }
11095
11490
  async function linkNode(opts) {
11096
- const explicitDataDir = opts.dataDir?.trim() || process.env.DATA_DIR?.trim() || void 0;
11491
+ const rawDataDir = opts.dataDir?.trim() || process.env.DATA_DIR?.trim() || void 0;
11492
+ const explicitDataDir = rawDataDir ? expandHome(rawDataDir) : void 0;
11097
11493
  const host = opts.host?.trim() || process.env.HOST?.trim() || DEFAULT_SERVE_HOST;
11098
11494
  const port = opts.port ?? Number(process.env.PORT ?? DEFAULT_SERVE_PORT);
11099
- const candidates = explicitDataDir ? buildHotCandidates(explicitDataDir, [], { host, port }) : buildHotCandidates(void 0, discoverRunningServeInstances(), { host, port });
11495
+ const running = discoverRunningServeInstances();
11496
+ const candidates = buildHotCandidates(explicitDataDir, running, { host, port });
11100
11497
  for (const inst of candidates) {
11101
11498
  const store2 = new Store(inst.dataDir);
11102
11499
  const adminKey = process.env.ADMIN_API_KEY ?? store2.loadAdminKey();
11103
11500
  if (!adminKey) continue;
11104
11501
  if (!await probeServer(inst.host, inst.port, adminKey)) continue;
11105
11502
  const status = await enrollViaServer(inst.host, inst.port, adminKey, opts);
11106
- return { mode: "hot", ...status, dataDir: inst.dataDir };
11503
+ return {
11504
+ mode: "hot",
11505
+ ...status,
11506
+ dataDir: inst.dataDir,
11507
+ managed: inst.managed,
11508
+ warnings: hotWarnings(inst, running)
11509
+ };
11107
11510
  }
11108
- const dataDir = path21.resolve(explicitDataDir ?? DEFAULT_DATA_DIR);
11511
+ const managedDir = running.find((r) => r.managed)?.dataDir;
11512
+ const dataDir = path22.resolve(explicitDataDir ?? managedDir ?? DEFAULT_DATA_DIR);
11513
+ const targetedService = !explicitDataDir && Boolean(managedDir) && dataDir === managedDir;
11109
11514
  const store = new Store(dataDir);
11110
11515
  const nodeId = ensureNodeId(store);
11111
11516
  const cred = await hubEnrollByCode(opts.hubUrl, {
@@ -11113,25 +11518,45 @@ async function linkNode(opts) {
11113
11518
  nodeId,
11114
11519
  label: opts.label
11115
11520
  });
11116
- store.saveFleetCredential({
11117
- ...cred,
11118
- label: opts.label ?? cred.label,
11119
- enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
11120
- });
11521
+ try {
11522
+ store.saveFleetCredential({
11523
+ ...cred,
11524
+ label: opts.label ?? cred.label,
11525
+ enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
11526
+ });
11527
+ } catch (e) {
11528
+ const err = e;
11529
+ if (err.code === "EACCES" || err.code === "EPERM") {
11530
+ throw new Error(
11531
+ `\u65E0\u6CD5\u5199\u5165\u6570\u636E\u76EE\u5F55 ${dataDir}\uFF08${err.code}: ${err.message}\uFF09
11532
+ \u8BE5\u76EE\u5F55\u5F53\u524D\u7528\u6237\u6CA1\u6709\u5199\u6743\u9650\u3002\u4E24\u79CD\u6539\u6CD5\uFF1A
11533
+ 1) \u53BB\u6389 --data-dir\uFF0C\u8BA9 link \u81EA\u52A8\u53D1\u73B0\u672C\u673A\u8FD0\u884C\u7684\u8282\u70B9\uFF08\u63A8\u8350\uFF09\uFF1B
11534
+ 2) \u7528\u5F53\u524D\u7528\u6237\u53EF\u5199\u7684\u76EE\u5F55\uFF0C\u672C\u5E73\u53F0\u5B89\u88C5\u811A\u672C\u9ED8\u8BA4\uFF1A${installDataDirHint()}
11535
+ \uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF09`
11536
+ );
11537
+ }
11538
+ throw e;
11539
+ }
11540
+ const otherRunningDirs = [
11541
+ ...new Set(running.map((r) => r.dataDir).filter((d) => d !== dataDir))
11542
+ ];
11121
11543
  return {
11122
11544
  mode: "saved",
11123
11545
  nodeId,
11124
11546
  gatewayUrl: cred.gatewayUrl,
11125
11547
  label: opts.label,
11126
11548
  dataDir,
11127
- explicitDataDir: Boolean(explicitDataDir)
11549
+ explicitDataDir: Boolean(explicitDataDir),
11550
+ runningServiceDirs: otherRunningDirs,
11551
+ targetedService
11128
11552
  };
11129
11553
  }
11130
11554
  function printLinkHelp() {
11131
- cliLog("\u7528\u6CD5: zqagent link --hub <url> --code <\u6CE8\u518C\u7801> [--label \u540D\u79F0] [--data-dir \u76EE\u5F55]");
11555
+ cliLog("\u7528\u6CD5: zqagent link --hub <url> --code <\u6CE8\u518C\u7801> [--label \u540D\u79F0]");
11132
11556
  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");
11133
- 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");
11134
- cliLog(" \u5426\u5219\u5199\u5165 <\u6570\u636E\u76EE\u5F55>/fleet.json\uFF0C\u4E0B\u6B21\u542F\u52A8\u81EA\u52A8\u63A5\u5165\u3002");
11557
+ 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");
11558
+ 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");
11559
+ 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");
11135
11560
  }
11136
11561
  async function runLinkCommand(argv) {
11137
11562
  const args = parseLinkArgs(argv);
@@ -11159,20 +11584,36 @@ async function runLinkCommand(argv) {
11159
11584
  cliLog("\u2705 \u5DF2\u63A5\u5165 Hub\uFF08\u70ED\u63A5\u5165\uFF0C\u514D\u91CD\u542F\uFF09");
11160
11585
  cliLog(` \u8282\u70B9: ${r.nodeId}${r.label ? ` (${r.label})` : ""}`);
11161
11586
  cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"} \u5728\u7EBF: ${r.online ? "\u662F" : "\u8FDE\u63A5\u4E2D\u2026"}`);
11162
- cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}`);
11587
+ cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}${r.managed ? "\uFF08\u6258\u7BA1\u670D\u52A1\uFF0C\u5F00\u673A\u81EA\u542F\uFF09" : ""}`);
11588
+ if (r.warnings.length > 0) {
11589
+ cliLog("");
11590
+ for (const w of r.warnings) cliLog(`\u26A0\uFE0F ${w}`);
11591
+ }
11163
11592
  } else {
11164
11593
  cliLog("\u2705 \u63A5\u5165\u51ED\u636E\u5DF2\u4FDD\u5B58\uFF08\u672C\u673A\u6682\u65E0\u8FD0\u884C\u4E2D\u7684 server\uFF09");
11165
11594
  cliLog(` \u8282\u70B9: ${r.nodeId}`);
11166
11595
  cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"}`);
11167
11596
  cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}`);
11168
11597
  cliLog(" \u4E0B\u6B21\u542F\u52A8 server \u5373\u81EA\u52A8\u63A5\u5165\uFF1B\u6216\u8FD0\u884C zqagent serve \u5E38\u9A7B\u4E3A\u8282\u70B9\u3002");
11169
- if (!r.explicitDataDir) {
11598
+ if (r.targetedService) {
11170
11599
  cliLog("");
11171
- cliLog("\u26A0\uFE0F \u672A\u6307\u5B9A --data-dir\uFF0C\u51ED\u636E\u5199\u5165\u4E86\u5F53\u524D\u76EE\u5F55\u4E0B\u7684 .zqagent_data\u3002");
11172
- 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");
11173
- 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");
11174
- cliLog(" zqagent link --hub <Hub\u5730\u5740> --code <\u65B0\u63A5\u5165\u7801> --data-dir <\u670D\u52A1\u6570\u636E\u76EE\u5F55>");
11175
- cliLog(" \uFF08Linux \u5B89\u88C5\u811A\u672C\u9ED8\u8BA4 /var/lib/zqagent\uFF1B\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF09");
11600
+ 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");
11601
+ cliLog(" \u7ACB\u5373\u751F\u6548\uFF1A\u91CD\u542F\u8BE5\u670D\u52A1\uFF08Linux: systemctl restart zqagent\uFF1B");
11602
+ cliLog(" macOS: launchctl kickstart -k gui/$(id -u)/com.zqagent.node\uFF09\u3002");
11603
+ } else if (r.runningServiceDirs.length > 0) {
11604
+ cliLog("");
11605
+ 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");
11606
+ cliLog(` \u672C\u6B21\u5199\u5165: ${r.dataDir}`);
11607
+ for (const d of r.runningServiceDirs) cliLog(` \u670D\u52A1\u4F7F\u7528: ${d}`);
11608
+ 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");
11609
+ 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");
11610
+ cliLog(` zqagent link --hub <Hub\u5730\u5740> --code <\u65B0\u63A5\u5165\u7801> --data-dir "${r.runningServiceDirs[0]}"`);
11611
+ } else if (!r.explicitDataDir) {
11612
+ cliLog("");
11613
+ 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");
11614
+ 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");
11615
+ cliLog(" curl -fsSL https://agent.cd-zq.com/install.sh | sh");
11616
+ cliLog(` \uFF08\u670D\u52A1\u9ED8\u8BA4\u6570\u636E\u76EE\u5F55\uFF1A${installDataDirHint()}\uFF09`);
11176
11617
  }
11177
11618
  }
11178
11619
  return true;
@@ -11192,7 +11633,7 @@ var path37 = __toESM(require("path"), 1);
11192
11633
 
11193
11634
  // src/server/session.ts
11194
11635
  init_cjs_shims();
11195
- var path24 = __toESM(require("path"), 1);
11636
+ var path25 = __toESM(require("path"), 1);
11196
11637
  var import_async_hooks = require("async_hooks");
11197
11638
 
11198
11639
  // src/server/mcpConfig.ts
@@ -11373,8 +11814,8 @@ function resolveSessionProvider(store, tenant, providerId) {
11373
11814
 
11374
11815
  // src/server/usage.ts
11375
11816
  init_cjs_shims();
11376
- var fs20 = __toESM(require("fs"), 1);
11377
- var path23 = __toESM(require("path"), 1);
11817
+ var fs21 = __toESM(require("fs"), 1);
11818
+ var path24 = __toESM(require("path"), 1);
11378
11819
  var LEDGER_RETENTION_MONTHS = 12;
11379
11820
  var USAGE_SCHEMA = 1;
11380
11821
  var DEFAULT_PRICING = {
@@ -11455,15 +11896,15 @@ function retainedMonths(now = /* @__PURE__ */ new Date()) {
11455
11896
  return out;
11456
11897
  }
11457
11898
  function atomicWrite(file, content) {
11458
- fs20.mkdirSync(path23.dirname(file), { recursive: true });
11899
+ fs21.mkdirSync(path24.dirname(file), { recursive: true });
11459
11900
  const tmp = `${file}.tmp`;
11460
- fs20.writeFileSync(tmp, content, "utf-8");
11461
- fs20.renameSync(tmp, file);
11901
+ fs21.writeFileSync(tmp, content, "utf-8");
11902
+ fs21.renameSync(tmp, file);
11462
11903
  }
11463
11904
  var UsageLedger = class {
11464
11905
  constructor(root) {
11465
11906
  this.root = root;
11466
- this.dir = path23.join(root, "usage");
11907
+ this.dir = path24.join(root, "usage");
11467
11908
  }
11468
11909
  root;
11469
11910
  dir;
@@ -11476,18 +11917,18 @@ var UsageLedger = class {
11476
11917
  return this.dir;
11477
11918
  }
11478
11919
  ledgerFile(month) {
11479
- return path23.join(this.ledgerDir, `ledger-${month}.jsonl`);
11920
+ return path24.join(this.ledgerDir, `ledger-${month}.jsonl`);
11480
11921
  }
11481
11922
  get rollupFile() {
11482
- return path23.join(this.ledgerDir, "rollup.json");
11923
+ return path24.join(this.ledgerDir, "rollup.json");
11483
11924
  }
11484
11925
  get pricingFile() {
11485
- return path23.join(this.ledgerDir, "pricing.json");
11926
+ return path24.join(this.ledgerDir, "pricing.json");
11486
11927
  }
11487
11928
  // ===================== 初始化 =====================
11488
11929
  init(now = /* @__PURE__ */ new Date()) {
11489
11930
  if (this.inited) return;
11490
- fs20.mkdirSync(this.ledgerDir, { recursive: true });
11931
+ fs21.mkdirSync(this.ledgerDir, { recursive: true });
11491
11932
  this.pricing = this.loadPricing();
11492
11933
  this.rollup = this.loadRollup(now);
11493
11934
  this.pruneLedgers(now);
@@ -11495,7 +11936,7 @@ var UsageLedger = class {
11495
11936
  }
11496
11937
  loadPricing() {
11497
11938
  try {
11498
- const raw = fs20.readFileSync(this.pricingFile, "utf-8");
11939
+ const raw = fs21.readFileSync(this.pricingFile, "utf-8");
11499
11940
  const parsed = JSON.parse(raw);
11500
11941
  const vendors = {};
11501
11942
  for (const v of /* @__PURE__ */ new Set([
@@ -11524,7 +11965,7 @@ var UsageLedger = class {
11524
11965
  }
11525
11966
  loadRollup(now) {
11526
11967
  try {
11527
- const raw = fs20.readFileSync(this.rollupFile, "utf-8");
11968
+ const raw = fs21.readFileSync(this.rollupFile, "utf-8");
11528
11969
  const parsed = JSON.parse(raw);
11529
11970
  return {
11530
11971
  schema: USAGE_SCHEMA,
@@ -11551,10 +11992,10 @@ var UsageLedger = class {
11551
11992
  const keep = retainedMonths(now);
11552
11993
  let changed = false;
11553
11994
  try {
11554
- for (const f of fs20.readdirSync(this.ledgerDir)) {
11995
+ for (const f of fs21.readdirSync(this.ledgerDir)) {
11555
11996
  const m = /^ledger-(\d{4}-\d{2})\.jsonl$/.exec(f);
11556
11997
  if (m && !keep.has(m[1])) {
11557
- fs20.unlinkSync(path23.join(this.ledgerDir, f));
11998
+ fs21.unlinkSync(path24.join(this.ledgerDir, f));
11558
11999
  }
11559
12000
  }
11560
12001
  } catch {
@@ -11631,7 +12072,7 @@ var UsageLedger = class {
11631
12072
  };
11632
12073
  const line = JSON.stringify(rec) + "\n";
11633
12074
  try {
11634
- fs20.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
12075
+ fs21.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
11635
12076
  } catch (e) {
11636
12077
  console.error(`[usage] \u5199\u8D26\u672C\u5931\u8D25: ${String(e)}`);
11637
12078
  }
@@ -11803,10 +12244,10 @@ var UsageLedger = class {
11803
12244
  const rows = [];
11804
12245
  for (const month of months) {
11805
12246
  const file = this.ledgerFile(month);
11806
- if (!fs20.existsSync(file)) continue;
12247
+ if (!fs21.existsSync(file)) continue;
11807
12248
  let content = "";
11808
12249
  try {
11809
- content = fs20.readFileSync(file, "utf-8");
12250
+ content = fs21.readFileSync(file, "utf-8");
11810
12251
  } catch {
11811
12252
  continue;
11812
12253
  }
@@ -11915,10 +12356,10 @@ var UsageLedger = class {
11915
12356
  const seen = /* @__PURE__ */ new Set();
11916
12357
  for (const month of this.monthsInRange("", "")) {
11917
12358
  const file = this.ledgerFile(month);
11918
- if (!fs20.existsSync(file)) continue;
12359
+ if (!fs21.existsSync(file)) continue;
11919
12360
  let content = "";
11920
12361
  try {
11921
- content = fs20.readFileSync(file, "utf-8");
12362
+ content = fs21.readFileSync(file, "utf-8");
11922
12363
  } catch {
11923
12364
  continue;
11924
12365
  }
@@ -12023,11 +12464,11 @@ function isAffirmative(answer) {
12023
12464
  return /^(yes|approve|同意|批准|允许|确定|可以|好的|确认)/.test(a);
12024
12465
  }
12025
12466
  function defer() {
12026
- let resolve23;
12467
+ let resolve24;
12027
12468
  const promise = new Promise((r) => {
12028
- resolve23 = r;
12469
+ resolve24 = r;
12029
12470
  });
12030
- return { promise, resolve: resolve23 };
12471
+ return { promise, resolve: resolve24 };
12031
12472
  }
12032
12473
  var SessionManager = class {
12033
12474
  constructor(store, approvalTimeoutMs = APPROVAL_TIMEOUT_MS, opts = {}) {
@@ -12599,11 +13040,11 @@ data: ${JSON.stringify({ sessionId, seq: entry.seq, ts: entry.ts, data: entry.da
12599
13040
  status: "pending",
12600
13041
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
12601
13042
  };
12602
- const { promise, resolve: resolve23 } = defer();
13043
+ const { promise, resolve: resolve24 } = defer();
12603
13044
  const timer = setTimeout(() => {
12604
13045
  this._recycleApproval(rt, id);
12605
13046
  }, this.approvalTimeoutMs);
12606
- rt.pendingApprovals.set(id, { promise, resolve: resolve23, record, timer });
13047
+ rt.pendingApprovals.set(id, { promise, resolve: resolve24, record, timer });
12607
13048
  rt.approvals.set(id, record);
12608
13049
  this._setStatus(rt, "waiting_approval");
12609
13050
  this._persist(rt);
@@ -12681,11 +13122,11 @@ data: ${JSON.stringify({ sessionId, seq: entry.seq, ts: entry.ts, data: entry.da
12681
13122
  status: "pending",
12682
13123
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
12683
13124
  };
12684
- const { promise, resolve: resolve23 } = defer();
13125
+ const { promise, resolve: resolve24 } = defer();
12685
13126
  const timer = setTimeout(() => {
12686
13127
  this._recycleQuestion(rt, qid);
12687
13128
  }, this.approvalTimeoutMs);
12688
- rt.pendingQuestions.set(qid, { promise, resolve: resolve23, record, timer });
13129
+ rt.pendingQuestions.set(qid, { promise, resolve: resolve24, record, timer });
12689
13130
  rt.questions.set(qid, record);
12690
13131
  this._setStatus(rt, "waiting_question");
12691
13132
  this._persist(rt);
@@ -12994,7 +13435,7 @@ data: ${JSON.stringify(entry.data)}
12994
13435
  if (rt.mcpReady) {
12995
13436
  await Promise.race([
12996
13437
  rt.mcpReady,
12997
- new Promise((resolve23) => setTimeout(resolve23, 15e3))
13438
+ new Promise((resolve24) => setTimeout(resolve24, 15e3))
12998
13439
  ]).catch(() => void 0);
12999
13440
  }
13000
13441
  for await (const event of gen) {
@@ -13229,7 +13670,7 @@ data: ${JSON.stringify(entry.data)}
13229
13670
  softThresholdPct: parseRatio(process.env.COMPACTION_SOFT_THRESHOLD_PCT),
13230
13671
  capTokens: parseOptionalPositiveNumber(process.env.COMPACTION_CAP_TOKENS) ?? null,
13231
13672
  // 超预算工具结果按租户/工作区隔离落盘(多租户共用一个状态目录会混内容)
13232
- toolResultDir: path24.join(workspace, ".agent", "tool-results")
13673
+ toolResultDir: path25.join(workspace, ".agent", "tool-results")
13233
13674
  });
13234
13675
  engine.setApprover(this._makeApprover(rt));
13235
13676
  const mcpConfigs = loadServerMcpConfigs(tenant);
@@ -13410,11 +13851,11 @@ var ApiError = class extends Error {
13410
13851
  // src/server/routes.ts
13411
13852
  init_cjs_shims();
13412
13853
  var import_http = require("http");
13413
- var path29 = __toESM(require("path"), 1);
13854
+ var path30 = __toESM(require("path"), 1);
13414
13855
 
13415
13856
  // src/server/projects.ts
13416
13857
  init_cjs_shims();
13417
- var path25 = __toESM(require("path"), 1);
13858
+ var path26 = __toESM(require("path"), 1);
13418
13859
  var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
13419
13860
  function toProjectSummary(store, tenant) {
13420
13861
  return {
@@ -13547,9 +13988,9 @@ function createProject(store, input) {
13547
13988
  if (store.loadTenant(id)) {
13548
13989
  throw new ApiError(409, "project_exists", `\u9879\u76EE ${id} \u5DF2\u5B58\u5728`);
13549
13990
  }
13550
- const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path25.resolve(raw.workspaceRoot.trim()) : void 0;
13991
+ const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path26.resolve(raw.workspaceRoot.trim()) : void 0;
13551
13992
  const explicitName = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : "";
13552
- const defaultName = workspaceRoot ? path25.basename(workspaceRoot) : id;
13993
+ const defaultName = workspaceRoot ? path26.basename(workspaceRoot) : id;
13553
13994
  const tenant = {
13554
13995
  id,
13555
13996
  name: explicitName || defaultName,
@@ -13579,7 +14020,7 @@ function buildNodeInfo(store, opts) {
13579
14020
  nodeId: opts.nodeId ?? ensureNodeId2(store),
13580
14021
  platform: process.platform,
13581
14022
  mode: process.env.NODE_MODE ?? "standalone",
13582
- version: "0.2.8",
14023
+ version: "0.2.11",
13583
14024
  admin: opts.admin,
13584
14025
  projects: opts.projects
13585
14026
  };
@@ -13758,34 +14199,34 @@ async function handleHubProxy(req2, res) {
13758
14199
 
13759
14200
  // src/server/fsBrowser.ts
13760
14201
  init_cjs_shims();
13761
- var fs21 = __toESM(require("fs"), 1);
13762
- var os4 = __toESM(require("os"), 1);
13763
- var path26 = __toESM(require("path"), 1);
14202
+ var fs22 = __toESM(require("fs"), 1);
14203
+ var os6 = __toESM(require("os"), 1);
14204
+ var path27 = __toESM(require("path"), 1);
13764
14205
  var MAX_ENTRIES = 2e3;
13765
14206
  var DEFAULT_SEARCH_LIMIT = 200;
13766
14207
  var MAX_SEARCH_LIMIT = 1e3;
13767
14208
  function listDirs(input) {
13768
- const base = (input ?? "").trim() || os4.homedir();
13769
- const target = path26.resolve(base);
14209
+ const base = (input ?? "").trim() || os6.homedir();
14210
+ const target = path27.resolve(base);
13770
14211
  let st;
13771
14212
  try {
13772
- st = fs21.statSync(target);
14213
+ st = fs22.statSync(target);
13773
14214
  } catch {
13774
14215
  throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
13775
14216
  }
13776
14217
  if (!st.isDirectory()) throw new Error(`\u4E0D\u662F\u76EE\u5F55: ${target}`);
13777
14218
  let dirents;
13778
14219
  try {
13779
- dirents = fs21.readdirSync(target, { withFileTypes: true });
14220
+ dirents = fs22.readdirSync(target, { withFileTypes: true });
13780
14221
  } catch {
13781
14222
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
13782
14223
  }
13783
14224
  const entries = dirents.map((d) => {
13784
- const full = path26.join(target, d.name);
14225
+ const full = path27.join(target, d.name);
13785
14226
  let isDir = d.isDirectory();
13786
14227
  if (d.isSymbolicLink()) {
13787
14228
  try {
13788
- isDir = fs21.statSync(full).isDirectory();
14229
+ isDir = fs22.statSync(full).isDirectory();
13789
14230
  } catch {
13790
14231
  }
13791
14232
  }
@@ -13794,7 +14235,7 @@ function listDirs(input) {
13794
14235
  entries.sort(
13795
14236
  (a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
13796
14237
  );
13797
- const parentDir = path26.dirname(target);
14238
+ const parentDir = path27.dirname(target);
13798
14239
  return {
13799
14240
  path: target,
13800
14241
  parent: parentDir !== target ? parentDir : null,
@@ -13825,7 +14266,7 @@ var NAME_CACHE_MAX_DIRS = 4;
13825
14266
  var NAME_CACHE_MAX_ENTRIES = 5e4;
13826
14267
  var nameCache = /* @__PURE__ */ new Map();
13827
14268
  function invalidateDirNames(target) {
13828
- nameCache.delete(path26.resolve(target));
14269
+ nameCache.delete(path27.resolve(target));
13829
14270
  }
13830
14271
  async function loadDirNames(target) {
13831
14272
  const now = Date.now();
@@ -13837,7 +14278,7 @@ async function loadDirNames(target) {
13837
14278
  }
13838
14279
  let dirents;
13839
14280
  try {
13840
- dirents = await fs21.promises.readdir(target, { withFileTypes: true });
14281
+ dirents = await fs22.promises.readdir(target, { withFileTypes: true });
13841
14282
  } catch {
13842
14283
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
13843
14284
  }
@@ -13860,22 +14301,22 @@ async function loadDirNames(target) {
13860
14301
  return entries;
13861
14302
  }
13862
14303
  async function toDirEntry(dir, e) {
13863
- const full = path26.join(dir, e.name);
14304
+ const full = path27.join(dir, e.name);
13864
14305
  let isDir = e.isDir;
13865
14306
  if (e.isLink) {
13866
14307
  try {
13867
- isDir = (await fs21.promises.stat(full)).isDirectory();
14308
+ isDir = (await fs22.promises.stat(full)).isDirectory();
13868
14309
  } catch {
13869
14310
  }
13870
14311
  }
13871
14312
  return { name: e.name, path: full, type: isDir ? "dir" : "file" };
13872
14313
  }
13873
14314
  async function searchDirs(input, query, limit) {
13874
- const base = (input ?? "").trim() || os4.homedir();
13875
- const target = path26.resolve(base);
14315
+ const base = (input ?? "").trim() || os6.homedir();
14316
+ const target = path27.resolve(base);
13876
14317
  let st;
13877
14318
  try {
13878
- st = await fs21.promises.stat(target);
14319
+ st = await fs22.promises.stat(target);
13879
14320
  } catch {
13880
14321
  throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
13881
14322
  }
@@ -13905,7 +14346,7 @@ async function searchDirs(input, query, limit) {
13905
14346
  );
13906
14347
  const hit = scored.slice(0, max);
13907
14348
  const entries = await Promise.all(hit.map(({ entry }) => toDirEntry(target, entry)));
13908
- const parentDir = path26.dirname(target);
14349
+ const parentDir = path27.dirname(target);
13909
14350
  return {
13910
14351
  path: target,
13911
14352
  parent: parentDir !== target ? parentDir : null,
@@ -13916,52 +14357,52 @@ async function searchDirs(input, query, limit) {
13916
14357
  };
13917
14358
  }
13918
14359
  function makeDirIn(parentInput, name) {
13919
- const parent = path26.resolve((parentInput ?? "").trim() || os4.homedir());
14360
+ const parent = path27.resolve((parentInput ?? "").trim() || os6.homedir());
13920
14361
  const clean = (name ?? "").trim();
13921
14362
  if (!clean) throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u4E3A\u7A7A");
13922
14363
  if (clean === "." || clean === "..") throw new Error("\u76EE\u5F55\u540D\u975E\u6CD5");
13923
- const target = path26.resolve(parent, clean);
13924
- const rel = path26.relative(parent, target);
13925
- if (!rel || rel.startsWith("..") || path26.isAbsolute(rel)) {
14364
+ const target = path27.resolve(parent, clean);
14365
+ const rel = path27.relative(parent, target);
14366
+ if (!rel || rel.startsWith("..") || path27.isAbsolute(rel)) {
13926
14367
  throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u8D8A\u51FA\u5F53\u524D\u76EE\u5F55");
13927
14368
  }
13928
14369
  let st = null;
13929
14370
  try {
13930
- st = fs21.statSync(target);
14371
+ st = fs22.statSync(target);
13931
14372
  } catch {
13932
14373
  st = null;
13933
14374
  }
13934
14375
  if (st && !st.isDirectory()) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u6587\u4EF6: ${target}`);
13935
14376
  try {
13936
- fs21.mkdirSync(target, { recursive: true });
14377
+ fs22.mkdirSync(target, { recursive: true });
13937
14378
  } catch (e) {
13938
14379
  throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
13939
14380
  }
13940
14381
  invalidateDirNames(parent);
13941
- return { name: path26.basename(target), path: target, type: "dir" };
14382
+ return { name: path27.basename(target), path: target, type: "dir" };
13942
14383
  }
13943
14384
 
13944
14385
  // src/server/projectFiles.ts
13945
14386
  init_cjs_shims();
13946
- var fs22 = __toESM(require("fs"), 1);
13947
- var path27 = __toESM(require("path"), 1);
14387
+ var fs23 = __toESM(require("fs"), 1);
14388
+ var path28 = __toESM(require("path"), 1);
13948
14389
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
13949
14390
  var MAX_DOWNLOAD_BYTES = 16 * 1024 * 1024;
13950
14391
  var MAX_ENTRIES2 = 2e3;
13951
14392
  function toPosix(p) {
13952
- return p.split(path27.sep).join("/");
14393
+ return p.split(path28.sep).join("/");
13953
14394
  }
13954
14395
  function projectRoot(workspaceRoot) {
13955
14396
  const r = (workspaceRoot ?? "").trim();
13956
14397
  if (!r) {
13957
14398
  throw new Error("\u9879\u76EE\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF08workspaceRoot\uFF09\uFF0C\u65E0\u6CD5\u6D4F\u89C8\u6587\u4EF6");
13958
14399
  }
13959
- const abs = path27.resolve(r);
13960
- if (!fs22.existsSync(abs)) {
14400
+ const abs = path28.resolve(r);
14401
+ if (!fs23.existsSync(abs)) {
13961
14402
  throw new Error(`\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728: ${abs}`);
13962
14403
  }
13963
14404
  try {
13964
- return fs22.realpathSync(abs);
14405
+ return fs23.realpathSync(abs);
13965
14406
  } catch {
13966
14407
  return abs;
13967
14408
  }
@@ -13979,37 +14420,37 @@ function listProjectFiles(workspaceRoot, relPath) {
13979
14420
  const dir = resolveWithin(base, relPath ?? "");
13980
14421
  let st;
13981
14422
  try {
13982
- st = fs22.statSync(dir);
14423
+ st = fs23.statSync(dir);
13983
14424
  } catch {
13984
- throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path27.relative(base, dir)) || "/"}`);
14425
+ throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path28.relative(base, dir)) || "/"}`);
13985
14426
  }
13986
14427
  if (!st.isDirectory()) throw new Error("\u76EE\u6807\u4E0D\u662F\u76EE\u5F55");
13987
14428
  let dirents;
13988
14429
  try {
13989
- dirents = fs22.readdirSync(dir, { withFileTypes: true });
14430
+ dirents = fs23.readdirSync(dir, { withFileTypes: true });
13990
14431
  } catch {
13991
14432
  throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09");
13992
14433
  }
13993
14434
  const entries = dirents.map((d) => {
13994
- const full = path27.join(dir, d.name);
14435
+ const full = path28.join(dir, d.name);
13995
14436
  let isDir = d.isDirectory();
13996
14437
  if (d.isSymbolicLink()) {
13997
14438
  try {
13998
- isDir = fs22.statSync(full).isDirectory();
14439
+ isDir = fs23.statSync(full).isDirectory();
13999
14440
  } catch {
14000
14441
  }
14001
14442
  }
14002
14443
  let size = 0;
14003
14444
  let mtime = "";
14004
14445
  try {
14005
- const s = fs22.statSync(full);
14446
+ const s = fs23.statSync(full);
14006
14447
  size = s.size;
14007
14448
  mtime = s.mtime.toISOString();
14008
14449
  } catch {
14009
14450
  }
14010
14451
  return {
14011
14452
  name: d.name,
14012
- path: toPosix(path27.relative(base, full)),
14453
+ path: toPosix(path28.relative(base, full)),
14013
14454
  type: isDir ? "dir" : "file",
14014
14455
  size,
14015
14456
  mtime
@@ -14018,12 +14459,12 @@ function listProjectFiles(workspaceRoot, relPath) {
14018
14459
  entries.sort(
14019
14460
  (a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
14020
14461
  );
14021
- const relDir = toPosix(path27.relative(base, dir));
14022
- const parentAbs = path27.dirname(dir);
14462
+ const relDir = toPosix(path28.relative(base, dir));
14463
+ const parentAbs = path28.dirname(dir);
14023
14464
  return {
14024
14465
  root: base,
14025
14466
  path: relDir,
14026
- parent: isWithinRoot(base, parentAbs) ? toPosix(path27.relative(base, parentAbs)) : null,
14467
+ parent: isWithinRoot(base, parentAbs) ? toPosix(path28.relative(base, parentAbs)) : null,
14027
14468
  entries: entries.slice(0, MAX_ENTRIES2)
14028
14469
  };
14029
14470
  }
@@ -14032,17 +14473,17 @@ function readProjectFile(workspaceRoot, relPath) {
14032
14473
  const file = resolveWithin(base, relPath ?? "");
14033
14474
  let st;
14034
14475
  try {
14035
- st = fs22.statSync(file);
14476
+ st = fs23.statSync(file);
14036
14477
  } catch {
14037
14478
  throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
14038
14479
  }
14039
14480
  if (!st.isFile()) throw new Error("\u76EE\u6807\u4E0D\u662F\u6587\u4EF6");
14040
- const buf = fs22.readFileSync(file);
14481
+ const buf = fs23.readFileSync(file);
14041
14482
  const binary = isBinary(buf);
14042
14483
  const truncated = buf.length > MAX_FILE_BYTES;
14043
14484
  const slice = truncated ? buf.subarray(0, MAX_FILE_BYTES) : buf;
14044
14485
  return {
14045
- path: toPosix(path27.relative(base, file)),
14486
+ path: toPosix(path28.relative(base, file)),
14046
14487
  size: st.size,
14047
14488
  binary,
14048
14489
  truncated,
@@ -14054,7 +14495,7 @@ function downloadProjectFile(workspaceRoot, relPath) {
14054
14495
  const file = resolveWithin(base, relPath ?? "");
14055
14496
  let st;
14056
14497
  try {
14057
- st = fs22.statSync(file);
14498
+ st = fs23.statSync(file);
14058
14499
  } catch {
14059
14500
  throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
14060
14501
  }
@@ -14062,10 +14503,10 @@ function downloadProjectFile(workspaceRoot, relPath) {
14062
14503
  if (st.size > MAX_DOWNLOAD_BYTES) {
14063
14504
  throw new Error(`\u6587\u4EF6\u8D85\u8FC7\u4E0B\u8F7D\u4E0A\u9650\uFF08${MAX_DOWNLOAD_BYTES / (1024 * 1024)}MB\uFF09`);
14064
14505
  }
14065
- const buf = fs22.readFileSync(file);
14506
+ const buf = fs23.readFileSync(file);
14066
14507
  return {
14067
- path: toPosix(path27.relative(base, file)),
14068
- name: path27.basename(file),
14508
+ path: toPosix(path28.relative(base, file)),
14509
+ name: path28.basename(file),
14069
14510
  size: buf.length,
14070
14511
  base64: buf.toString("base64")
14071
14512
  };
@@ -14087,12 +14528,12 @@ function writeProjectFile(workspaceRoot, relPath, content) {
14087
14528
  throw new Error("\u5185\u5BB9\u8D85\u8FC7 2MB \u4E0A\u9650\uFF0C\u65E0\u6CD5\u4FDD\u5B58");
14088
14529
  }
14089
14530
  try {
14090
- fs22.mkdirSync(path27.dirname(file), { recursive: true });
14091
- fs22.writeFileSync(file, buf);
14531
+ fs23.mkdirSync(path28.dirname(file), { recursive: true });
14532
+ fs23.writeFileSync(file, buf);
14092
14533
  } catch (e) {
14093
14534
  throw new Error(`\u5199\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
14094
14535
  }
14095
- return { path: toPosix(path27.relative(base, file)), size: buf.length };
14536
+ return { path: toPosix(path28.relative(base, file)), size: buf.length };
14096
14537
  }
14097
14538
  function createProjectDir(workspaceRoot, relPath) {
14098
14539
  const base = projectRoot(workspaceRoot);
@@ -14100,26 +14541,26 @@ function createProjectDir(workspaceRoot, relPath) {
14100
14541
  if (!rel) throw new Error("\u76EE\u5F55\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
14101
14542
  const dir = resolveWithin(base, rel);
14102
14543
  try {
14103
- fs22.mkdirSync(dir, { recursive: true });
14544
+ fs23.mkdirSync(dir, { recursive: true });
14104
14545
  } catch (e) {
14105
14546
  throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
14106
14547
  }
14107
- return { path: toPosix(path27.relative(base, dir)) };
14548
+ return { path: toPosix(path28.relative(base, dir)) };
14108
14549
  }
14109
14550
  function createProjectFile(workspaceRoot, relPath, content = "") {
14110
14551
  const base = projectRoot(workspaceRoot);
14111
14552
  const rel = (relPath ?? "").trim();
14112
14553
  if (!rel) throw new Error("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
14113
14554
  const file = resolveWithin(base, rel);
14114
- if (fs22.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
14555
+ if (fs23.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
14115
14556
  const buf = Buffer.from(String(content ?? ""), "utf-8");
14116
14557
  try {
14117
- fs22.mkdirSync(path27.dirname(file), { recursive: true });
14118
- fs22.writeFileSync(file, buf);
14558
+ fs23.mkdirSync(path28.dirname(file), { recursive: true });
14559
+ fs23.writeFileSync(file, buf);
14119
14560
  } catch (e) {
14120
14561
  throw new Error(`\u521B\u5EFA\u6587\u4EF6\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
14121
14562
  }
14122
- return { path: toPosix(path27.relative(base, file)), size: buf.length };
14563
+ return { path: toPosix(path28.relative(base, file)), size: buf.length };
14123
14564
  }
14124
14565
  function searchProjectFiles(workspaceRoot, query) {
14125
14566
  const base = projectRoot(workspaceRoot);
@@ -14142,7 +14583,7 @@ function searchProjectFiles(workspaceRoot, query) {
14142
14583
  if (out.length >= LIMIT) return;
14143
14584
  let dirents;
14144
14585
  try {
14145
- dirents = fs22.readdirSync(dir, { withFileTypes: true });
14586
+ dirents = fs23.readdirSync(dir, { withFileTypes: true });
14146
14587
  } catch {
14147
14588
  return;
14148
14589
  }
@@ -14153,7 +14594,7 @@ function searchProjectFiles(workspaceRoot, query) {
14153
14594
  let isDir = d.isDirectory();
14154
14595
  if (d.isSymbolicLink()) {
14155
14596
  try {
14156
- isDir = fs22.statSync(path27.join(dir, d.name)).isDirectory();
14597
+ isDir = fs23.statSync(path28.join(dir, d.name)).isDirectory();
14157
14598
  } catch {
14158
14599
  isDir = false;
14159
14600
  }
@@ -14161,7 +14602,7 @@ function searchProjectFiles(workspaceRoot, query) {
14161
14602
  if (d.name.toLowerCase().includes(q)) {
14162
14603
  out.push({ path: rel, type: isDir ? "dir" : "file" });
14163
14604
  }
14164
- if (isDir) walk(path27.join(dir, d.name), rel);
14605
+ if (isDir) walk(path28.join(dir, d.name), rel);
14165
14606
  }
14166
14607
  };
14167
14608
  walk(base, "");
@@ -14170,8 +14611,8 @@ function searchProjectFiles(workspaceRoot, query) {
14170
14611
 
14171
14612
  // src/server/projectOps.ts
14172
14613
  init_cjs_shims();
14173
- var os5 = __toESM(require("os"), 1);
14174
- var path28 = __toESM(require("path"), 1);
14614
+ var os7 = __toESM(require("os"), 1);
14615
+ var path29 = __toESM(require("path"), 1);
14175
14616
  var MAX_STDOUT2 = 2e4;
14176
14617
  var MAX_STDERR2 = 1e4;
14177
14618
  var MAX_BUFFER3 = 1024 * 1024 * 5;
@@ -14185,7 +14626,7 @@ function resolveInvocation2(spec, prepared) {
14185
14626
  }
14186
14627
  function workRoot(workspaceRoot) {
14187
14628
  const r = (workspaceRoot ?? "").trim();
14188
- return r ? path28.resolve(r) : os5.homedir();
14629
+ return r ? path29.resolve(r) : os7.homedir();
14189
14630
  }
14190
14631
  async function runProjectCommand(workspaceRoot, command, timeoutSeconds) {
14191
14632
  const cmd = String(command ?? "");
@@ -14496,11 +14937,11 @@ function projectStats(manager, tenantId) {
14496
14937
 
14497
14938
  // src/server/nodeHost.ts
14498
14939
  init_cjs_shims();
14499
- var fs23 = __toESM(require("fs"), 1);
14500
- var os6 = __toESM(require("os"), 1);
14940
+ var fs24 = __toESM(require("fs"), 1);
14941
+ var os8 = __toESM(require("os"), 1);
14501
14942
  function diskInfo(pathStr) {
14502
14943
  try {
14503
- const st = fs23.statfsSync(pathStr);
14944
+ const st = fs24.statfsSync(pathStr);
14504
14945
  return {
14505
14946
  path: pathStr,
14506
14947
  total: Number(st.blocks) * Number(st.bsize),
@@ -14512,7 +14953,7 @@ function diskInfo(pathStr) {
14512
14953
  }
14513
14954
  function networkInfo() {
14514
14955
  const out = [];
14515
- const ifaces = os6.networkInterfaces();
14956
+ const ifaces = os8.networkInterfaces();
14516
14957
  for (const [name, list] of Object.entries(ifaces)) {
14517
14958
  for (const info of list ?? []) {
14518
14959
  out.push({
@@ -14563,23 +15004,23 @@ function overview(store, manager) {
14563
15004
  return { projects, sessions: { total, running, waiting, items }, usage: { tokensIn, tokensOut } };
14564
15005
  }
14565
15006
  function buildNodeHost(store, manager, diskPath) {
14566
- const cpus2 = os6.cpus();
15007
+ const cpus2 = os8.cpus();
14567
15008
  const ov = overview(store, manager);
14568
15009
  return {
14569
15010
  nodeId: store.loadNodeId() ?? "",
14570
- hostname: os6.hostname(),
15011
+ hostname: os8.hostname(),
14571
15012
  platform: process.platform,
14572
15013
  arch: process.arch,
14573
- release: os6.release(),
14574
- uptimeSec: Math.round(os6.uptime()),
14575
- version: "0.2.8",
15014
+ release: os8.release(),
15015
+ uptimeSec: Math.round(os8.uptime()),
15016
+ version: "0.2.11",
14576
15017
  mode: process.env.NODE_MODE ?? "standalone",
14577
15018
  cpu: {
14578
15019
  count: cpus2.length,
14579
15020
  model: cpus2[0]?.model ?? "",
14580
- loadAvg: os6.loadavg().map((n) => Math.round(n * 100) / 100)
15021
+ loadAvg: os8.loadavg().map((n) => Math.round(n * 100) / 100)
14581
15022
  },
14582
- memory: { total: os6.totalmem(), free: os6.freemem() },
15023
+ memory: { total: os8.totalmem(), free: os8.freemem() },
14583
15024
  network: networkInfo(),
14584
15025
  disk: diskInfo(diskPath || process.cwd()),
14585
15026
  ...ov
@@ -14616,7 +15057,7 @@ function saveNotifications(tenant, secretBox, notifications) {
14616
15057
  var API_PREFIX = "/api/v1";
14617
15058
  var MAX_BODY_BYTES = 10 * 1024 * 1024;
14618
15059
  function readBody(req2) {
14619
- return new Promise((resolve23, reject) => {
15060
+ return new Promise((resolve24, reject) => {
14620
15061
  const chunks = [];
14621
15062
  let size = 0;
14622
15063
  req2.on("data", (c) => {
@@ -14629,9 +15070,9 @@ function readBody(req2) {
14629
15070
  chunks.push(c);
14630
15071
  });
14631
15072
  req2.on("end", () => {
14632
- if (chunks.length === 0) return resolve23(void 0);
15073
+ if (chunks.length === 0) return resolve24(void 0);
14633
15074
  try {
14634
- resolve23(JSON.parse(Buffer.concat(chunks).toString("utf-8")));
15075
+ resolve24(JSON.parse(Buffer.concat(chunks).toString("utf-8")));
14635
15076
  } catch {
14636
15077
  reject(new ApiError(400, "bad_request", "\u8BF7\u6C42\u4F53\u4E0D\u662F\u5408\u6CD5 JSON"));
14637
15078
  }
@@ -15147,8 +15588,18 @@ function createApp(store, manager, opts = {}) {
15147
15588
  applyProvidersPatch(tenant, raw);
15148
15589
  if (typeof raw.workspaceRoot === "string") {
15149
15590
  const ws = raw.workspaceRoot.trim();
15150
- if (ws) tenant.workspaceRoot = path29.resolve(ws);
15151
- else delete tenant.workspaceRoot;
15591
+ const next = ws ? path30.resolve(ws) : void 0;
15592
+ if (tenant.workspaceRoot) {
15593
+ if (next !== tenant.workspaceRoot) {
15594
+ throw new ApiError(
15595
+ 409,
15596
+ "workspace_locked",
15597
+ "\u8BE5\u9879\u76EE\u5DF2\u8BBE\u7F6E\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u4E0D\u80FD\u518D\u4FEE\u6539"
15598
+ );
15599
+ }
15600
+ } else if (next) {
15601
+ tenant.workspaceRoot = next;
15602
+ }
15152
15603
  }
15153
15604
  if (typeof raw.name === "string" && raw.name.trim()) {
15154
15605
  tenant.name = raw.name.trim();
@@ -16124,7 +16575,7 @@ function createApp(store, manager, opts = {}) {
16124
16575
  json(res, 200, {
16125
16576
  status: "ok",
16126
16577
  // 版本让运维/探针能确认线上跑的是哪个构建(更新与回滚据此判断)
16127
- version: "0.2.8",
16578
+ version: "0.2.11",
16128
16579
  features: { mcp: hasServerMcpConfig() }
16129
16580
  });
16130
16581
  return;
@@ -16219,7 +16670,7 @@ function createApp(store, manager, opts = {}) {
16219
16670
 
16220
16671
  // src/server/fleet/FleetNodeBridge.ts
16221
16672
  init_cjs_shims();
16222
- var os7 = __toESM(require("os"), 1);
16673
+ var os9 = __toESM(require("os"), 1);
16223
16674
  var import_node_perf_hooks = require("perf_hooks");
16224
16675
 
16225
16676
  // src/server/fleet/FleetCommands.ts
@@ -16240,8 +16691,8 @@ async function cachedFetchModels(baseURL, apiKey) {
16240
16691
  try {
16241
16692
  result = await Promise.race([
16242
16693
  fetchModels(baseURL, apiKey),
16243
- new Promise((resolve23) => {
16244
- const t = setTimeout(() => resolve23(null), MODEL_FETCH_TIMEOUT_MS);
16694
+ new Promise((resolve24) => {
16695
+ const t = setTimeout(() => resolve24(null), MODEL_FETCH_TIMEOUT_MS);
16245
16696
  t.unref?.();
16246
16697
  })
16247
16698
  ]);
@@ -16266,8 +16717,8 @@ async function cachedFetchModelsEnriched(baseURL, apiKey, preferred) {
16266
16717
  if (!target) return r;
16267
16718
  const ctx = await Promise.race([
16268
16719
  fetchModelContext(baseURL, apiKey, target),
16269
- new Promise((resolve23) => {
16270
- const t = setTimeout(() => resolve23(void 0), MODEL_FETCH_TIMEOUT_MS);
16720
+ new Promise((resolve24) => {
16721
+ const t = setTimeout(() => resolve24(void 0), MODEL_FETCH_TIMEOUT_MS);
16271
16722
  t.unref?.();
16272
16723
  })
16273
16724
  ]);
@@ -17396,13 +17847,13 @@ var EVENT_FLUSH_MAX_EVENTS = 60;
17396
17847
  var EVENT_FLUSH_MAX_BYTES = 200 * 1024;
17397
17848
  var LOOP_LAG_WARN_MS = 2e3;
17398
17849
  function withTimeout(p, ms) {
17399
- return new Promise((resolve23, reject) => {
17850
+ return new Promise((resolve24, reject) => {
17400
17851
  const timer = setTimeout(() => reject(new Error(`\u5B50\u6307\u4EE4\u8D85\u65F6\uFF08${ms}ms\uFF09`)), ms);
17401
17852
  timer.unref?.();
17402
17853
  p.then(
17403
17854
  (v) => {
17404
17855
  clearTimeout(timer);
17405
- resolve23(v);
17856
+ resolve24(v);
17406
17857
  },
17407
17858
  (e) => {
17408
17859
  clearTimeout(timer);
@@ -17413,7 +17864,7 @@ function withTimeout(p, ms) {
17413
17864
  }
17414
17865
  function primaryIp() {
17415
17866
  let v6;
17416
- for (const infos of Object.values(os7.networkInterfaces())) {
17867
+ for (const infos of Object.values(os9.networkInterfaces())) {
17417
17868
  for (const info of infos ?? []) {
17418
17869
  if (info.internal) continue;
17419
17870
  if (info.family === "IPv4") return info.address;
@@ -17762,7 +18213,7 @@ var FleetNodeBridge = class {
17762
18213
  label: this.opts.deps.store.loadNodeLabel() ?? void 0,
17763
18214
  platform: process.platform,
17764
18215
  mode: process.env.NODE_MODE ?? "standalone",
17765
- version: "0.2.8",
18216
+ version: "0.2.11",
17766
18217
  ip: primaryIp()
17767
18218
  };
17768
18219
  }
@@ -17817,7 +18268,7 @@ var FleetNodeBridge = class {
17817
18268
  label: this.opts.deps.store.loadNodeLabel() ?? void 0,
17818
18269
  platform: process.platform,
17819
18270
  mode: process.env.NODE_MODE ?? "standalone",
17820
- version: "0.2.8",
18271
+ version: "0.2.11",
17821
18272
  ip: primaryIp(),
17822
18273
  projects,
17823
18274
  totals,
@@ -18586,8 +19037,8 @@ init_cjs_shims();
18586
19037
 
18587
19038
  // src/server/qq/bindings.ts
18588
19039
  init_cjs_shims();
18589
- var fs24 = __toESM(require("fs"), 1);
18590
- var path30 = __toESM(require("path"), 1);
19040
+ var fs25 = __toESM(require("fs"), 1);
19041
+ var path31 = __toESM(require("path"), 1);
18591
19042
  function conversationKey(target) {
18592
19043
  switch (target.kind) {
18593
19044
  case "c2c":
@@ -18626,12 +19077,12 @@ var QqBindingsStore = class _QqBindingsStore {
18626
19077
  cache = null;
18627
19078
  /** 基于 DATA_DIR 构造 */
18628
19079
  static in(dataDir) {
18629
- return new _QqBindingsStore(path30.join(dataDir, "qq", "bindings.json"));
19080
+ return new _QqBindingsStore(path31.join(dataDir, "qq", "bindings.json"));
18630
19081
  }
18631
19082
  load() {
18632
19083
  if (this.cache) return this.cache;
18633
19084
  try {
18634
- const raw = JSON.parse(fs24.readFileSync(this.file, "utf-8"));
19085
+ const raw = JSON.parse(fs25.readFileSync(this.file, "utf-8"));
18635
19086
  this.cache = {
18636
19087
  conversations: raw.conversations ?? {}
18637
19088
  };
@@ -18663,8 +19114,8 @@ var QqBindingsStore = class _QqBindingsStore {
18663
19114
  return null;
18664
19115
  }
18665
19116
  persist() {
18666
- fs24.mkdirSync(path30.dirname(this.file), { recursive: true });
18667
- fs24.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
19117
+ fs25.mkdirSync(path31.dirname(this.file), { recursive: true });
19118
+ fs25.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
18668
19119
  }
18669
19120
  };
18670
19121
 
@@ -19076,8 +19527,8 @@ ${lines.join("\n")}`, msgId);
19076
19527
 
19077
19528
  // src/server/qq/QqManager.ts
19078
19529
  init_cjs_shims();
19079
- var fs25 = __toESM(require("fs"), 1);
19080
- var path31 = __toESM(require("path"), 1);
19530
+ var fs26 = __toESM(require("fs"), 1);
19531
+ var path32 = __toESM(require("path"), 1);
19081
19532
  var QqManager = class {
19082
19533
  constructor(opts) {
19083
19534
  this.opts = opts;
@@ -19086,7 +19537,7 @@ var QqManager = class {
19086
19537
  adapter = null;
19087
19538
  config = null;
19088
19539
  get configFile() {
19089
- return path31.join(this.opts.dataDir, "qq", "config.json");
19540
+ return path32.join(this.opts.dataDir, "qq", "config.json");
19090
19541
  }
19091
19542
  /** 启动:读配置(env 优先,其次文件),有则建连接 */
19092
19543
  start() {
@@ -19124,7 +19575,7 @@ var QqManager = class {
19124
19575
  /** 清空配置:删除文件 + 断开 */
19125
19576
  clearConfig() {
19126
19577
  try {
19127
- fs25.rmSync(this.configFile, { force: true });
19578
+ fs26.rmSync(this.configFile, { force: true });
19128
19579
  } catch {
19129
19580
  }
19130
19581
  this.stop();
@@ -19166,7 +19617,7 @@ var QqManager = class {
19166
19617
  }
19167
19618
  _readConfigFile() {
19168
19619
  try {
19169
- const raw = JSON.parse(fs25.readFileSync(this.configFile, "utf-8"));
19620
+ const raw = JSON.parse(fs26.readFileSync(this.configFile, "utf-8"));
19170
19621
  if (raw.appId && raw.appSecret) {
19171
19622
  return {
19172
19623
  appId: raw.appId,
@@ -19179,8 +19630,8 @@ var QqManager = class {
19179
19630
  return null;
19180
19631
  }
19181
19632
  _persistConfig(cfg) {
19182
- fs25.mkdirSync(path31.dirname(this.configFile), { recursive: true });
19183
- fs25.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
19633
+ fs26.mkdirSync(path32.dirname(this.configFile), { recursive: true });
19634
+ fs26.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
19184
19635
  }
19185
19636
  };
19186
19637
 
@@ -19234,11 +19685,11 @@ var path36 = __toESM(require("path"), 1);
19234
19685
  // src/update/downloader.ts
19235
19686
  init_cjs_shims();
19236
19687
  var import_node_crypto5 = require("crypto");
19237
- var fs26 = __toESM(require("fs"), 1);
19688
+ var fs27 = __toESM(require("fs"), 1);
19238
19689
  var import_node_stream2 = require("stream");
19239
19690
  var import_promises2 = require("stream/promises");
19240
- var import_node_child_process5 = require("child_process");
19241
- var path32 = __toESM(require("path"), 1);
19691
+ var import_node_child_process6 = require("child_process");
19692
+ var path33 = __toESM(require("path"), 1);
19242
19693
 
19243
19694
  // src/update/manifest.ts
19244
19695
  init_cjs_shims();
@@ -19294,8 +19745,10 @@ var PLATFORM_NAMES = {
19294
19745
  linux: "linux",
19295
19746
  win32: "windows"
19296
19747
  };
19748
+ var LIBC_VARIANT = "";
19297
19749
  function platformKey(platform = process.platform, arch = process.arch) {
19298
- return `${PLATFORM_NAMES[platform] ?? platform}-${arch}`;
19750
+ const base = `${PLATFORM_NAMES[platform] ?? platform}-${arch}`;
19751
+ return LIBC_VARIANT ? `${base}-${LIBC_VARIANT}` : base;
19299
19752
  }
19300
19753
  var SHA256_HEX = /^[0-9a-f]{64}$/;
19301
19754
  function isRecord(v) {
@@ -19413,8 +19866,8 @@ async function fetchManifest(url, publicKey, opts = {}) {
19413
19866
  async function downloadBinary(bin, destPath, log = () => {
19414
19867
  }) {
19415
19868
  const partPath = `${destPath}.part`;
19416
- fs26.mkdirSync(path32.dirname(destPath), { recursive: true });
19417
- fs26.rmSync(partPath, { force: true });
19869
+ fs27.mkdirSync(path33.dirname(destPath), { recursive: true });
19870
+ fs27.rmSync(partPath, { force: true });
19418
19871
  let res;
19419
19872
  try {
19420
19873
  res = await fetch(bin.url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS2) });
@@ -19439,30 +19892,30 @@ async function downloadBinary(bin, destPath, log = () => {
19439
19892
  yield chunk;
19440
19893
  }
19441
19894
  },
19442
- fs26.createWriteStream(partPath, { mode: 493 })
19895
+ fs27.createWriteStream(partPath, { mode: 493 })
19443
19896
  );
19444
19897
  } catch (err) {
19445
- fs26.rmSync(partPath, { force: true });
19898
+ fs27.rmSync(partPath, { force: true });
19446
19899
  return { error: `\u4E0B\u8F7D\u4E2D\u65AD\uFF1A${err.message}` };
19447
19900
  }
19448
19901
  const actual = hash.digest("hex");
19449
19902
  if (actual !== bin.sha256) {
19450
- fs26.rmSync(partPath, { force: true });
19903
+ fs27.rmSync(partPath, { force: true });
19451
19904
  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` };
19452
19905
  }
19453
19906
  if (expected && bytes !== expected) {
19454
- fs26.rmSync(partPath, { force: true });
19907
+ fs27.rmSync(partPath, { force: true });
19455
19908
  return { error: `\u4EA7\u7269\u5927\u5C0F\u4E0D\u5339\u914D\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${bytes}\uFF09` };
19456
19909
  }
19457
- fs26.rmSync(destPath, { force: true });
19458
- fs26.renameSync(partPath, destPath);
19459
- fs26.chmodSync(destPath, 493);
19460
- log(`\u5DF2\u4E0B\u8F7D ${path32.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
19910
+ fs27.rmSync(destPath, { force: true });
19911
+ fs27.renameSync(partPath, destPath);
19912
+ fs27.chmodSync(destPath, 493);
19913
+ log(`\u5DF2\u4E0B\u8F7D ${path33.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
19461
19914
  return { bytes };
19462
19915
  }
19463
19916
  function selfCheck(binPath, expectedVersion) {
19464
19917
  try {
19465
- const r = (0, import_node_child_process5.spawnSync)(binPath, ["--version"], {
19918
+ const r = (0, import_node_child_process6.spawnSync)(binPath, ["--version"], {
19466
19919
  timeout: 2e4,
19467
19920
  encoding: "utf-8",
19468
19921
  env: { ...process.env, ZQAGENT_AUTO_UPDATE: "0" }
@@ -19479,104 +19932,6 @@ function selfCheck(binPath, expectedVersion) {
19479
19932
  }
19480
19933
  }
19481
19934
 
19482
- // src/update/platform.ts
19483
- init_cjs_shims();
19484
- var fs27 = __toESM(require("fs"), 1);
19485
- var path33 = __toESM(require("path"), 1);
19486
- var import_node_child_process6 = require("child_process");
19487
- var import_node_module3 = require("module");
19488
- function currentVersion() {
19489
- const injected = "0.2.8"?.trim();
19490
- if (injected) return injected;
19491
- try {
19492
- const req2 = (0, import_node_module3.createRequire)(importMetaUrl);
19493
- const pkg = req2("../../package.json");
19494
- if (pkg?.version) return pkg.version;
19495
- } catch {
19496
- }
19497
- return "dev";
19498
- }
19499
- function isSeaRuntime() {
19500
- if (process.env.ZQAGENT_SEA === "1") return true;
19501
- const base = path33.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
19502
- return base !== "node" && base !== "nodejs";
19503
- }
19504
- var DEFAULT_SERVICE_NAME = "zqagent";
19505
- function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
19506
- const unitName = `${serviceName}.service`;
19507
- for (const line of cgroup.split("\n")) {
19508
- const idx = line.lastIndexOf(":");
19509
- if (idx < 0) continue;
19510
- const unitPath = line.slice(idx + 1).trim();
19511
- if (unitPath.split("/").includes(unitName)) return "systemd";
19512
- }
19513
- return null;
19514
- }
19515
- function detectServiceManager() {
19516
- if (process.platform !== "linux") return null;
19517
- const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
19518
- try {
19519
- return serviceManagerFromCgroup(
19520
- fs27.readFileSync("/proc/self/cgroup", "utf8"),
19521
- serviceName
19522
- );
19523
- } catch {
19524
- return null;
19525
- }
19526
- }
19527
- function serviceManager() {
19528
- const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
19529
- if (v === "systemd" || v === "launchd") return v;
19530
- return detectServiceManager();
19531
- }
19532
- function parseServiceEnvironment(raw) {
19533
- const out = {};
19534
- for (const token of raw.trim().split(/\s+/)) {
19535
- const idx = token.indexOf("=");
19536
- if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
19537
- }
19538
- return out;
19539
- }
19540
- var cachedServiceEnv = null;
19541
- function readServiceEnvironment() {
19542
- const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
19543
- try {
19544
- const res = (0, import_node_child_process6.spawnSync)(
19545
- "systemctl",
19546
- ["show", serviceName, "--property=Environment", "--value", "--no-pager"],
19547
- { encoding: "utf8", timeout: 3e3 }
19548
- );
19549
- if (res.status !== 0 || !res.stdout) return {};
19550
- return parseServiceEnvironment(res.stdout);
19551
- } catch {
19552
- return {};
19553
- }
19554
- }
19555
- function serviceEnvValue(key) {
19556
- if (process.platform !== "linux") return void 0;
19557
- if (serviceManager() !== "systemd") return void 0;
19558
- cachedServiceEnv ??= readServiceEnvironment();
19559
- return cachedServiceEnv[key];
19560
- }
19561
- function selfUpdateCapability(hasPublicKey) {
19562
- if (!isSeaRuntime()) {
19563
- return {
19564
- ok: false,
19565
- 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"
19566
- };
19567
- }
19568
- if (!serviceManager()) {
19569
- return {
19570
- ok: false,
19571
- reason: "\u672A\u88AB systemd/launchd \u6258\u7BA1\uFF1A\u8BF7\u7528 deploy/service/install.sh \u5B89\u88C5\uFF0C\u6216\u624B\u52A8\u5347\u7EA7"
19572
- };
19573
- }
19574
- if (!hasPublicKey) {
19575
- return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
19576
- }
19577
- return { ok: true };
19578
- }
19579
-
19580
19935
  // src/update/installer.ts
19581
19936
  init_cjs_shims();
19582
19937
  var fs29 = __toESM(require("fs"), 1);
@@ -19942,11 +20297,11 @@ var UpdateController = class {
19942
20297
  const minUptime = num("ZQAGENT_UPDATE_MIN_UPTIME_MS", 10 * 60 * 1e3);
19943
20298
  const idleMs = num("ZQAGENT_UPDATE_IDLE_MS", 60 * 1e3);
19944
20299
  let idleSince = null;
19945
- return new Promise((resolve23) => {
20300
+ return new Promise((resolve24) => {
19946
20301
  const timer = setInterval(() => {
19947
20302
  if (this.stopped) {
19948
20303
  clearInterval(timer);
19949
- resolve23(false);
20304
+ resolve24(false);
19950
20305
  return;
19951
20306
  }
19952
20307
  if (Date.now() - this.startedAt < minUptime || !this.opts.isIdle()) {
@@ -19956,7 +20311,7 @@ var UpdateController = class {
19956
20311
  idleSince ??= Date.now();
19957
20312
  if (Date.now() - idleSince >= idleMs) {
19958
20313
  clearInterval(timer);
19959
- resolve23(true);
20314
+ resolve24(true);
19960
20315
  }
19961
20316
  }, 1e3);
19962
20317
  timer.unref();
@@ -20137,6 +20492,46 @@ function exitWhenOrphaned() {
20137
20492
  }, 1500);
20138
20493
  timer.unref();
20139
20494
  }
20495
+ var PORT_FALLBACK_LIMIT = 20;
20496
+ function listenWithPortFallback(server, startPort, host, dataDir, limit = PORT_FALLBACK_LIMIT) {
20497
+ return new Promise((resolve24, reject) => {
20498
+ let attempts = 0;
20499
+ const tryListen = (port) => {
20500
+ server.removeAllListeners("listening");
20501
+ const onError = (err) => {
20502
+ if (err.code === "EADDRINUSE" && attempts < limit) {
20503
+ attempts += 1;
20504
+ const next = port + 1;
20505
+ console.warn(`[server] \u7AEF\u53E3 ${port} \u5DF2\u88AB\u5360\u7528\uFF08EADDRINUSE\uFF09\uFF0C\u5C1D\u8BD5 ${next} \u2026`);
20506
+ tryListen(next);
20507
+ return;
20508
+ }
20509
+ server.removeListener("error", onError);
20510
+ console.error(
20511
+ `[server] \u65E0\u6CD5\u76D1\u542C ${host}:${port}\uFF1A${err.message}` + (err.code === "EADDRINUSE" ? `\uFF08\u5DF2\u5C1D\u8BD5 ${attempts + 1} \u4E2A\u7AEF\u53E3\u5747\u88AB\u5360\u7528\uFF09` : "")
20512
+ );
20513
+ reject(err);
20514
+ };
20515
+ server.once("error", onError);
20516
+ server.listen(port, host, () => {
20517
+ server.removeListener("error", onError);
20518
+ const addr = server.address();
20519
+ const actualPort = typeof addr === "object" && addr !== null ? addr.port : port;
20520
+ if (actualPort !== startPort) {
20521
+ console.warn(`[server] \u5DF2\u6539\u7528\u7AEF\u53E3 ${actualPort}\uFF08\u539F\u5B9A ${startPort} \u88AB\u5360\u7528\uFF09`);
20522
+ }
20523
+ try {
20524
+ fs31.mkdirSync(dataDir, { recursive: true });
20525
+ fs31.writeFileSync(path37.join(dataDir, "server-port"), `${actualPort}
20526
+ `);
20527
+ } catch {
20528
+ }
20529
+ resolve24(actualPort);
20530
+ });
20531
+ };
20532
+ tryListen(startPort);
20533
+ });
20534
+ }
20140
20535
  function startServer(opts = {}) {
20141
20536
  const dataDir = path37.resolve(opts.dataDir ?? env2("DATA_DIR") ?? DEFAULT_DATA_DIR);
20142
20537
  const port = opts.port ?? Number(env2("PORT") ?? 8787);
@@ -20362,11 +20757,11 @@ function startServer(opts = {}) {
20362
20757
  server.timeout = 0;
20363
20758
  server.keepAliveTimeout = 65e3;
20364
20759
  server.headersTimeout = 66e3;
20365
- const ready = new Promise((resolve23) => {
20366
- server.listen(port, host, () => {
20760
+ const ready = new Promise((resolve24, reject) => {
20761
+ listenWithPortFallback(server, port, host, dataDir).then((actualPort) => {
20367
20762
  const info = {
20368
20763
  host,
20369
- port,
20764
+ port: actualPort,
20370
20765
  dataDir,
20371
20766
  adminKey,
20372
20767
  fleetNodeId: fleet ? fleet.getNodeId() : void 0,
@@ -20379,8 +20774,8 @@ function startServer(opts = {}) {
20379
20774
  printReady(info, qq);
20380
20775
  }
20381
20776
  updater?.start();
20382
- resolve23(info);
20383
- });
20777
+ resolve24(info);
20778
+ }, reject);
20384
20779
  });
20385
20780
  return {
20386
20781
  store,
@@ -20516,7 +20911,7 @@ async function runWizard(rl2, store, opts) {
20516
20911
  }
20517
20912
  async function askHidden(rl2, prompt) {
20518
20913
  if (!process.stdin.isTTY) return (await rl2.question(prompt)).trim();
20519
- return new Promise((resolve23) => {
20914
+ return new Promise((resolve24) => {
20520
20915
  const saved = process.stdin.listeners("data");
20521
20916
  process.stdin.removeAllListeners("data");
20522
20917
  cliWrite(prompt);
@@ -20538,13 +20933,13 @@ async function askHidden(rl2, prompt) {
20538
20933
  if (ch === "\r" || ch === "\n") {
20539
20934
  cleanup();
20540
20935
  cliWrite("\n");
20541
- resolve23(buf);
20936
+ resolve24(buf);
20542
20937
  return;
20543
20938
  }
20544
20939
  if (ch === "") {
20545
20940
  cleanup();
20546
20941
  cliWrite("\n");
20547
- resolve23("");
20942
+ resolve24("");
20548
20943
  return;
20549
20944
  }
20550
20945
  if (ch === "\x7F" || ch === "\b") {
@@ -20733,7 +21128,7 @@ function updateSessionMetaFile(stateDir2, sessionId, patch) {
20733
21128
  // src/cli/debug.ts
20734
21129
  init_cjs_shims();
20735
21130
  var fs33 = __toESM(require("fs"), 1);
20736
- var os8 = __toESM(require("os"), 1);
21131
+ var os10 = __toESM(require("os"), 1);
20737
21132
  var path41 = __toESM(require("path"), 1);
20738
21133
  var LOG_NAME = "zqagent-debug.log";
20739
21134
  var TRUTHY = /^(1|true|yes|on)$/i;
@@ -20747,7 +21142,7 @@ function resolveLogFile() {
20747
21142
  const explicit = process.env.ZQAGENT_DEBUG_LOG;
20748
21143
  if (explicit) return path41.resolve(explicit);
20749
21144
  const dir = process.env.ZQAGENT_DEBUG_DIR;
20750
- return path41.join(dir ? path41.resolve(dir) : os8.tmpdir(), LOG_NAME);
21145
+ return path41.join(dir ? path41.resolve(dir) : os10.tmpdir(), LOG_NAME);
20751
21146
  }
20752
21147
  function enableDebug(argv = process.argv) {
20753
21148
  if (enabled) return true;
@@ -21185,27 +21580,14 @@ var _exitHandler = null;
21185
21580
  rl.on("SIGINT", () => {
21186
21581
  _exitHandler?.();
21187
21582
  });
21188
- function patchReadlineHistoryNav() {
21189
- const iface = rl;
21190
- const orig = iface._ttyWrite;
21191
- iface._ttyWrite = function(s, key) {
21192
- const k = key;
21193
- if (k && (k.name === "up" || k.name === "down") && !k.ctrl && !k.meta && !k.shift) {
21194
- if (iface.cursor > 0) {
21195
- iface._moveCursor(-iface.cursor);
21196
- }
21197
- }
21198
- orig.call(rl, s, key);
21199
- };
21200
- }
21201
- patchReadlineHistoryNav();
21583
+ patchReadlineHistoryNav(rl);
21202
21584
  dlog("init", "\u6A21\u5757\u521D\u59CB\u5316\u5B8C\u6210\uFF08readline / \u8F93\u5165\u8DEF\u7531 / \u5386\u53F2\u5BFC\u822A\u5C31\u7EEA\uFF09");
21203
21585
  function question(prompt) {
21204
21586
  return inputRouter.ask(prompt);
21205
21587
  }
21206
21588
  function readPassword(prompt) {
21207
21589
  if (!process.stdin.isTTY) return question(prompt);
21208
- return new Promise((resolve23) => {
21590
+ return new Promise((resolve24) => {
21209
21591
  const savedDataListeners = process.stdin.listeners("data");
21210
21592
  process.stdin.removeAllListeners("data");
21211
21593
  cliWrite(prompt);
@@ -21221,13 +21603,13 @@ function readPassword(prompt) {
21221
21603
  if (ch === "\r" || ch === "\n") {
21222
21604
  cleanup();
21223
21605
  cliWrite("\n");
21224
- resolve23(buf);
21606
+ resolve24(buf);
21225
21607
  return;
21226
21608
  }
21227
21609
  if (ch === "") {
21228
21610
  cleanup();
21229
21611
  cliWrite("\n");
21230
- resolve23("");
21612
+ resolve24("");
21231
21613
  return;
21232
21614
  }
21233
21615
  if (ch === "\x7F" || ch === "\b") {
@@ -21662,7 +22044,7 @@ async function offerTerminalSetup(engine, canUseAgent) {
21662
22044
  }
21663
22045
  var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set(["link", "serve", "update"]);
21664
22046
  function cliVersion() {
21665
- const env3 = "0.2.8"?.trim();
22047
+ const env3 = "0.2.11"?.trim();
21666
22048
  if (env3) return env3;
21667
22049
  try {
21668
22050
  const req2 = (0, import_module.createRequire)(importMetaUrl);