@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.
package/dist/cli/index.js CHANGED
@@ -3724,10 +3724,10 @@ var OpenAIProvider = class {
3724
3724
  let timer;
3725
3725
  try {
3726
3726
  for (; ; ) {
3727
- const idle = new Promise((resolve23) => {
3727
+ const idle = new Promise((resolve24) => {
3728
3728
  timer = setTimeout(() => {
3729
3729
  onIdle();
3730
- resolve23({ timedOut: true });
3730
+ resolve24({ timedOut: true });
3731
3731
  }, idleMs);
3732
3732
  });
3733
3733
  const next = iter.next();
@@ -6048,12 +6048,12 @@ function makeSink(maxBuffer) {
6048
6048
  };
6049
6049
  }
6050
6050
  function execAsync(file, argv, opts = {}) {
6051
- return new Promise((resolve23) => {
6051
+ return new Promise((resolve24) => {
6052
6052
  const maxBuffer = opts.maxBuffer ?? DEFAULT_MAX_BUFFER;
6053
6053
  const out = makeSink(maxBuffer);
6054
6054
  const err = makeSink(maxBuffer);
6055
6055
  if (opts.signal?.aborted) {
6056
- resolve23({
6056
+ resolve24({
6057
6057
  status: null,
6058
6058
  stdout: Buffer.alloc(0),
6059
6059
  stderr: Buffer.alloc(0),
@@ -6074,7 +6074,7 @@ function execAsync(file, argv, opts = {}) {
6074
6074
  windowsHide: true
6075
6075
  });
6076
6076
  } catch (e) {
6077
- resolve23({
6077
+ resolve24({
6078
6078
  status: null,
6079
6079
  stdout: Buffer.alloc(0),
6080
6080
  stderr: Buffer.alloc(0),
@@ -6111,7 +6111,7 @@ function execAsync(file, argv, opts = {}) {
6111
6111
  };
6112
6112
  if (aborted) result.aborted = true;
6113
6113
  if (spawnError) result.error = spawnError;
6114
- resolve23(result);
6114
+ resolve24(result);
6115
6115
  };
6116
6116
  const killAndSettle = () => {
6117
6117
  killTree(child.pid);
@@ -10077,6 +10077,38 @@ function countRawEscapes(chunk) {
10077
10077
  for (const ch of cleaned) if (ch === "\x1B") escCount++;
10078
10078
  return escCount;
10079
10079
  }
10080
+ function patchReadlineHistoryNav(rl2) {
10081
+ const iface = rl2;
10082
+ const orig = iface._ttyWrite;
10083
+ let draft = null;
10084
+ iface._ttyWrite = function(s, key) {
10085
+ const k = key;
10086
+ const name = k?.name;
10087
+ const plainArrow = (name === "up" || name === "down") && !k?.ctrl && !k?.meta && !k?.shift;
10088
+ if (plainArrow) {
10089
+ if (name === "up" && iface.historyIndex === -1) {
10090
+ draft = { line: iface.line, cursor: iface.cursor };
10091
+ }
10092
+ if (iface.cursor > 0) {
10093
+ iface._moveCursor(-iface.cursor);
10094
+ }
10095
+ }
10096
+ const prevHistoryIndex = iface.historyIndex;
10097
+ orig.call(rl2, s, key);
10098
+ if (plainArrow && name === "down" && prevHistoryIndex >= 0 && iface.historyIndex === -1) {
10099
+ const saved = draft;
10100
+ draft = null;
10101
+ if (saved) {
10102
+ iface.line = saved.line;
10103
+ iface.cursor = Math.min(saved.cursor, saved.line.length);
10104
+ iface._refreshLine?.();
10105
+ }
10106
+ }
10107
+ };
10108
+ rl2.on("line", () => {
10109
+ draft = null;
10110
+ });
10111
+ }
10080
10112
  var PASTE_PLACEHOLDER_PREFIX = "\u7C98\u8D34\u6587\u672C #";
10081
10113
  var InputRouter = class {
10082
10114
  rl;
@@ -10201,8 +10233,8 @@ var InputRouter = class {
10201
10233
  }
10202
10234
  /** 询问一行:显示 prompt,返回该行内容 */
10203
10235
  ask(prompt) {
10204
- return new Promise((resolve23) => {
10205
- this.pendingAsks.push({ prompt, resolve: resolve23 });
10236
+ return new Promise((resolve24) => {
10237
+ this.pendingAsks.push({ prompt, resolve: resolve24 });
10206
10238
  this.pumpAsks();
10207
10239
  });
10208
10240
  }
@@ -10299,16 +10331,16 @@ import chalk2 from "chalk";
10299
10331
  var BRAND_TEXT = "ZQAGENT";
10300
10332
  var BRAND_TAGLINE = "ZQAgent \xB7 AI Agent CLI";
10301
10333
  var GLYPHS = {
10302
- 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"],
10303
- 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 "],
10304
- 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"],
10305
- 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 "],
10306
- 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"],
10307
- 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"],
10308
- 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 "]
10334
+ Z: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ", " \u2588\u2588\u2588 ", " \u2588\u2588\u2588 ", " \u2588\u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ", " "],
10335
+ 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 "],
10336
+ 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", " "],
10337
+ G: [" \u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588 \u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588\u2588\u2588 ", " "],
10338
+ 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 ", " "],
10339
+ 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", " "],
10340
+ T: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", " "]
10309
10341
  };
10310
10342
  var GLYPH_ROWS = 6;
10311
- var GLYPH_COLS = 8;
10343
+ var GLYPH_COLS = 9;
10312
10344
  var GRADIENT = [
10313
10345
  [34, 211, 238],
10314
10346
  [99, 102, 241],
@@ -10464,7 +10496,7 @@ function renderTerminalTutorial() {
10464
10496
 
10465
10497
  // src/cli/link.ts
10466
10498
  init_esm_shims();
10467
- import * as path22 from "path";
10499
+ import * as path23 from "path";
10468
10500
 
10469
10501
  // src/server/store.ts
10470
10502
  init_esm_shims();
@@ -10874,11 +10906,186 @@ async function hubEnrollByAccount(baseUrl, input) {
10874
10906
 
10875
10907
  // src/cli/serve-instances.ts
10876
10908
  init_esm_shims();
10877
- import { spawnSync as spawnSync2 } from "child_process";
10909
+ import { spawnSync as spawnSync3 } from "child_process";
10910
+ import * as fs19 from "fs";
10911
+ import * as os5 from "os";
10912
+ import * as path22 from "path";
10913
+
10914
+ // src/update/platform.ts
10915
+ init_esm_shims();
10878
10916
  import * as fs18 from "fs";
10917
+ import * as os4 from "os";
10879
10918
  import * as path21 from "path";
10919
+ import { spawnSync as spawnSync2 } from "child_process";
10920
+ import { createRequire as createRequire3 } from "module";
10921
+ function currentVersion() {
10922
+ const injected = "0.2.11"?.trim();
10923
+ if (injected) return injected;
10924
+ try {
10925
+ const req2 = createRequire3(import.meta.url);
10926
+ const pkg = req2("../../package.json");
10927
+ if (pkg?.version) return pkg.version;
10928
+ } catch {
10929
+ }
10930
+ return "dev";
10931
+ }
10932
+ function isSeaRuntime() {
10933
+ if (process.env.ZQAGENT_SEA === "1") return true;
10934
+ const base = path21.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
10935
+ return base !== "node" && base !== "nodejs";
10936
+ }
10937
+ var DEFAULT_SERVICE_NAME = "zqagent";
10938
+ var DEFAULT_SERVICE_LABEL = "com.zqagent.node";
10939
+ function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
10940
+ const unitName = `${serviceName}.service`;
10941
+ for (const line of cgroup.split("\n")) {
10942
+ const idx = line.lastIndexOf(":");
10943
+ if (idx < 0) continue;
10944
+ const unitPath = line.slice(idx + 1).trim();
10945
+ if (unitPath.split("/").includes(unitName)) return "systemd";
10946
+ }
10947
+ return null;
10948
+ }
10949
+ function launchdDomain() {
10950
+ const uid = typeof process.getuid === "function" ? process.getuid() : -1;
10951
+ return uid === 0 ? "system" : `gui/${uid}`;
10952
+ }
10953
+ function launchdLabel() {
10954
+ return process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL;
10955
+ }
10956
+ function launchdPlistPath() {
10957
+ const label = launchdLabel();
10958
+ const uid = typeof process.getuid === "function" ? process.getuid() : -1;
10959
+ if (uid === 0) return `/Library/LaunchDaemons/${label}.plist`;
10960
+ return path21.join(os4.homedir(), "Library", "LaunchAgents", `${label}.plist`);
10961
+ }
10962
+ function serviceManagerFromLaunchctl(output, pid, execPath, sea = true) {
10963
+ const pidMatch = /^\s*pid = (\d+)\s*$/m.exec(output);
10964
+ if (pidMatch && Number(pidMatch[1]) === pid) return "launchd";
10965
+ const program = /^\s*program = (.+?)\s*$/m.exec(output);
10966
+ if (program) {
10967
+ const shown = program[1].replace(/^"|"$/g, "");
10968
+ if (path21.resolve(shown) === path21.resolve(execPath)) return "launchd";
10969
+ }
10970
+ if (!sea && /^\s*state = /m.test(output)) return "launchd";
10971
+ return null;
10972
+ }
10973
+ function parseLaunchdEnvironment(xml) {
10974
+ const out = {};
10975
+ const marker = xml.indexOf("<key>EnvironmentVariables</key>");
10976
+ if (marker < 0) return out;
10977
+ const open = xml.indexOf("<dict>", marker);
10978
+ if (open < 0) return out;
10979
+ const close = xml.indexOf("</dict>", open);
10980
+ if (close < 0) return out;
10981
+ const body = xml.slice(open + "<dict>".length, close);
10982
+ for (const m of body.matchAll(/<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g)) {
10983
+ out[m[1]] = m[2];
10984
+ }
10985
+ return out;
10986
+ }
10987
+ function detectServiceManager() {
10988
+ if (process.platform === "darwin") return detectLaunchd();
10989
+ if (process.platform !== "linux") return null;
10990
+ const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
10991
+ try {
10992
+ return serviceManagerFromCgroup(
10993
+ fs18.readFileSync("/proc/self/cgroup", "utf8"),
10994
+ serviceName
10995
+ );
10996
+ } catch {
10997
+ return null;
10998
+ }
10999
+ }
11000
+ function detectLaunchd() {
11001
+ try {
11002
+ const res = spawnSync2(
11003
+ "launchctl",
11004
+ ["print", `${launchdDomain()}/${launchdLabel()}`],
11005
+ { encoding: "utf8", timeout: 3e3 }
11006
+ );
11007
+ if (res.status !== 0 || !res.stdout) return null;
11008
+ return serviceManagerFromLaunchctl(
11009
+ res.stdout,
11010
+ process.pid,
11011
+ process.execPath,
11012
+ isSeaRuntime()
11013
+ );
11014
+ } catch {
11015
+ return null;
11016
+ }
11017
+ }
11018
+ function serviceManager() {
11019
+ const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
11020
+ if (v === "systemd" || v === "launchd") return v;
11021
+ return detectServiceManager();
11022
+ }
11023
+ function parseServiceEnvironment(raw) {
11024
+ const out = {};
11025
+ for (const token of raw.trim().split(/\s+/)) {
11026
+ const idx = token.indexOf("=");
11027
+ if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
11028
+ }
11029
+ return out;
11030
+ }
11031
+ var cachedServiceEnv = null;
11032
+ function readServiceEnvironment() {
11033
+ const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
11034
+ try {
11035
+ const res = spawnSync2(
11036
+ "systemctl",
11037
+ ["show", serviceName, "--property=Environment", "--value", "--no-pager"],
11038
+ { encoding: "utf8", timeout: 3e3 }
11039
+ );
11040
+ if (res.status !== 0 || !res.stdout) return {};
11041
+ return parseServiceEnvironment(res.stdout);
11042
+ } catch {
11043
+ return {};
11044
+ }
11045
+ }
11046
+ function serviceEnvValue(key) {
11047
+ if (process.platform === "darwin") {
11048
+ if (serviceManager() !== "launchd") return void 0;
11049
+ cachedLaunchdEnv ??= readLaunchdEnvironment();
11050
+ return cachedLaunchdEnv[key];
11051
+ }
11052
+ if (process.platform !== "linux") return void 0;
11053
+ if (serviceManager() !== "systemd") return void 0;
11054
+ cachedServiceEnv ??= readServiceEnvironment();
11055
+ return cachedServiceEnv[key];
11056
+ }
11057
+ var cachedLaunchdEnv = null;
11058
+ function readLaunchdEnvironment() {
11059
+ try {
11060
+ return parseLaunchdEnvironment(fs18.readFileSync(launchdPlistPath(), "utf8"));
11061
+ } catch {
11062
+ return {};
11063
+ }
11064
+ }
11065
+ function selfUpdateCapability(hasPublicKey) {
11066
+ if (!isSeaRuntime()) {
11067
+ return {
11068
+ ok: false,
11069
+ 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"
11070
+ };
11071
+ }
11072
+ if (!serviceManager()) {
11073
+ return {
11074
+ ok: false,
11075
+ reason: "\u672A\u88AB systemd/launchd \u6258\u7BA1\uFF1A\u8BF7\u7528 deploy/service/install.sh \u5B89\u88C5\uFF0C\u6216\u624B\u52A8\u5347\u7EA7"
11076
+ };
11077
+ }
11078
+ if (!hasPublicKey) {
11079
+ return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
11080
+ }
11081
+ return { ok: true };
11082
+ }
11083
+
11084
+ // src/cli/serve-instances.ts
10880
11085
  var DEFAULT_SERVE_HOST = "127.0.0.1";
10881
11086
  var DEFAULT_SERVE_PORT = 8787;
11087
+ var DEFAULT_SERVICE_NAME2 = "zqagent";
11088
+ var DEFAULT_SERVICE_LABEL2 = "com.zqagent.node";
10882
11089
  function serveSubcommandIndex(argv) {
10883
11090
  for (let i = 1; i < argv.length; i++) {
10884
11091
  if (argv[i].startsWith("-")) continue;
@@ -10913,18 +11120,43 @@ function parseServeArgs(argv) {
10913
11120
  return { dataDir, host, port };
10914
11121
  }
10915
11122
  function buildHotCandidates(explicitDataDir, discovered, fallback) {
10916
- if (explicitDataDir) {
10917
- return [{ dataDir: path21.resolve(explicitDataDir), ...fallback }];
10918
- }
10919
- const out = [{ dataDir: path21.resolve(DEFAULT_DATA_DIR), ...fallback }];
10920
- for (const inst of discovered) {
11123
+ const out = [];
11124
+ const push = (inst) => {
10921
11125
  if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
10922
- }
11126
+ };
11127
+ if (explicitDataDir) push({ dataDir: path22.resolve(explicitDataDir), ...fallback });
11128
+ for (const inst of discovered) if (inst.managed) push(inst);
11129
+ for (const inst of discovered) if (!inst.managed) push(inst);
11130
+ if (!explicitDataDir) push({ dataDir: path22.resolve(DEFAULT_DATA_DIR), ...fallback });
10923
11131
  return out;
10924
11132
  }
11133
+ function isWindowsAbsolute(p) {
11134
+ return /^[A-Za-z]:[\\/]/.test(p) || /^\\\\/.test(p);
11135
+ }
11136
+ function isAbsolutePath(p) {
11137
+ return path22.isAbsolute(p) || isWindowsAbsolute(p);
11138
+ }
11139
+ function normalizePath(p) {
11140
+ return isWindowsAbsolute(p) ? path22.win32.normalize(p) : path22.normalize(p);
11141
+ }
11142
+ function parseServeCommandLine(line) {
11143
+ if (!/(?:^|\s)["']?serve["']?(?=\s|$)/.test(line)) return null;
11144
+ const m = /--data-dir(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(.+?))(?=\s+--\w|\s*$)/.exec(line);
11145
+ const rawDataDir = (m?.[1] ?? m?.[2] ?? m?.[3] ?? "").trim();
11146
+ if (!rawDataDir || !isAbsolutePath(rawDataDir)) return null;
11147
+ const host = /--host(?:=|\s+)(?:"([^"]+)"|'([^']+)'|(\S+))/.exec(line);
11148
+ const rawHost = (host?.[1] ?? host?.[2] ?? host?.[3] ?? DEFAULT_SERVE_HOST).trim();
11149
+ const portMatch = /--port(?:=|\s+)(\d+)/.exec(line);
11150
+ const port = Number(portMatch?.[1] ?? DEFAULT_SERVE_PORT);
11151
+ return {
11152
+ dataDir: normalizePath(rawDataDir),
11153
+ host: rawHost === "0.0.0.0" || rawHost === "::" || rawHost === "" ? DEFAULT_SERVE_HOST : rawHost,
11154
+ port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT
11155
+ };
11156
+ }
10925
11157
  function processCwd(pid) {
10926
11158
  try {
10927
- return fs18.readlinkSync(`/proc/${pid}/cwd`);
11159
+ return fs19.readlinkSync(`/proc/${pid}/cwd`);
10928
11160
  } catch {
10929
11161
  return null;
10930
11162
  }
@@ -10932,7 +11164,7 @@ function processCwd(pid) {
10932
11164
  function processEnv(pid) {
10933
11165
  const out = {};
10934
11166
  try {
10935
- const raw = fs18.readFileSync(`/proc/${pid}/environ`, "utf-8");
11167
+ const raw = fs19.readFileSync(`/proc/${pid}/environ`, "utf-8");
10936
11168
  for (const entry of raw.split("\0")) {
10937
11169
  const eq = entry.indexOf("=");
10938
11170
  if (eq > 0) out[entry.slice(0, eq)] = entry.slice(eq + 1);
@@ -10943,14 +11175,24 @@ function processEnv(pid) {
10943
11175
  }
10944
11176
  function resolveDataDir(rawDataDir, cwd, env3) {
10945
11177
  const pick = rawDataDir ?? env3.DATA_DIR ?? null;
10946
- if (pick) return path21.isAbsolute(pick) ? path21.normalize(pick) : cwd ? path21.resolve(cwd, pick) : null;
10947
- return cwd ? path21.resolve(cwd, DEFAULT_DATA_DIR) : null;
11178
+ if (pick) return path22.isAbsolute(pick) ? path22.normalize(pick) : cwd ? path22.resolve(cwd, pick) : null;
11179
+ return cwd ? path22.resolve(cwd, DEFAULT_DATA_DIR) : null;
11180
+ }
11181
+ function isManagedBySystemd(pid, env3) {
11182
+ if (env3.ZQAGENT_SERVICE_MANAGER?.trim()) return true;
11183
+ try {
11184
+ const cgroup = fs19.readFileSync(`/proc/${pid}/cgroup`, "utf-8");
11185
+ const serviceName = env3.ZQAGENT_SERVICE_NAME?.trim() || "zqagent";
11186
+ return serviceManagerFromCgroup(cgroup, serviceName) !== null;
11187
+ } catch {
11188
+ return false;
11189
+ }
10948
11190
  }
10949
11191
  function discoverFromProc() {
10950
11192
  const out = [];
10951
11193
  let pids;
10952
11194
  try {
10953
- pids = fs18.readdirSync("/proc");
11195
+ pids = fs19.readdirSync("/proc");
10954
11196
  } catch {
10955
11197
  return out;
10956
11198
  }
@@ -10959,50 +11201,186 @@ function discoverFromProc() {
10959
11201
  const pid = Number(name);
10960
11202
  if (pid === process.pid) continue;
10961
11203
  let argv;
11204
+ let env3;
10962
11205
  try {
10963
- argv = fs18.readFileSync(`/proc/${pid}/cmdline`, "utf-8").split("\0").filter(Boolean);
11206
+ argv = fs19.readFileSync(`/proc/${pid}/cmdline`, "utf-8").split("\0").filter(Boolean);
11207
+ env3 = processEnv(pid);
10964
11208
  } catch {
10965
11209
  continue;
10966
11210
  }
10967
11211
  const parsed = parseServeArgs(argv);
10968
11212
  if (!parsed) continue;
10969
- const dataDir = resolveDataDir(parsed.dataDir, processCwd(pid), processEnv(pid));
11213
+ const dataDir = resolveDataDir(parsed.dataDir, processCwd(pid), env3);
10970
11214
  if (!dataDir) continue;
10971
- out.push({ dataDir, host: parsed.host, port: parsed.port });
11215
+ out.push({
11216
+ dataDir,
11217
+ host: parsed.host,
11218
+ port: parsed.port,
11219
+ managed: isManagedBySystemd(pid, env3)
11220
+ });
10972
11221
  }
10973
11222
  return out;
10974
11223
  }
10975
11224
  function discoverFromPs() {
10976
11225
  const out = [];
10977
- const res = spawnSync2("ps", ["-axo", "command="], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
11226
+ const res = spawnSync3("ps", ["-axo", "command="], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
10978
11227
  if (res.status !== 0 || !res.stdout) return out;
10979
11228
  for (const line of res.stdout.split("\n")) {
10980
- if (!line.includes(" serve")) continue;
10981
- const m = /--data-dir(?:=|\s+)(.+?)(?=\s+--|\s*$)/.exec(line);
10982
- if (!m) continue;
10983
- const raw = m[1].trim();
10984
- if (!raw || !path21.isAbsolute(raw)) continue;
10985
- const host = /--host(?:=|\s+)(\S+)/.exec(line)?.[1] ?? DEFAULT_SERVE_HOST;
10986
- const port = Number(/--port(?:=|\s+)(\d+)/.exec(line)?.[1] ?? DEFAULT_SERVE_PORT);
11229
+ const inst = parseServeCommandLine(line);
11230
+ if (inst) out.push(inst);
11231
+ }
11232
+ return out;
11233
+ }
11234
+ function discoverFromWindows() {
11235
+ const out = [];
11236
+ const script = `[Console]::OutputEncoding=[Text.Encoding]::UTF8; Get-CimInstance Win32_Process -Filter "Name='zqagent.exe' OR Name='node.exe'" | ForEach-Object { $_.CommandLine }`;
11237
+ const res = spawnSync3(
11238
+ "powershell.exe",
11239
+ ["-NoProfile", "-NonInteractive", "-Command", script],
11240
+ { encoding: "utf-8", timeout: 15e3, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
11241
+ );
11242
+ if (res.status !== 0 || !res.stdout) return out;
11243
+ for (const line of res.stdout.replace(/^\uFEFF/, "").split(/\r?\n/)) {
11244
+ const inst = parseServeCommandLine(line);
11245
+ if (inst) out.push(inst);
11246
+ }
11247
+ return out;
11248
+ }
11249
+ function decodeXmlEntities(s) {
11250
+ return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
11251
+ }
11252
+ function parseLaunchdProgramArguments(xml) {
11253
+ const marker = xml.indexOf("<key>ProgramArguments</key>");
11254
+ if (marker < 0) return [];
11255
+ const open = xml.indexOf("<array>", marker);
11256
+ if (open < 0) return [];
11257
+ const close = xml.indexOf("</array>", open);
11258
+ if (close < 0) return [];
11259
+ return [...xml.slice(open + "<array>".length, close).matchAll(/<string>([^<]*)<\/string>/g)].map(
11260
+ (m) => decodeXmlEntities(m[1])
11261
+ );
11262
+ }
11263
+ function launchdServiceInstances() {
11264
+ if (process.platform !== "darwin") return [];
11265
+ const label = process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL2;
11266
+ const plists = [
11267
+ `/Library/LaunchDaemons/${label}.plist`,
11268
+ path22.join(os5.homedir(), "Library", "LaunchAgents", `${label}.plist`)
11269
+ ];
11270
+ const out = [];
11271
+ for (const plist of plists) {
11272
+ let xml;
11273
+ try {
11274
+ xml = fs19.readFileSync(plist, "utf-8");
11275
+ } catch {
11276
+ continue;
11277
+ }
11278
+ const parsed = parseServeArgs(parseLaunchdProgramArguments(xml));
11279
+ if (!parsed?.dataDir || !isAbsolutePath(parsed.dataDir)) continue;
10987
11280
  out.push({
10988
- dataDir: path21.normalize(raw),
10989
- host: host === "0.0.0.0" || host === "::" ? DEFAULT_SERVE_HOST : host,
10990
- port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT
11281
+ dataDir: normalizePath(parsed.dataDir),
11282
+ host: parsed.host,
11283
+ port: parsed.port,
11284
+ managed: true
10991
11285
  });
10992
11286
  }
10993
11287
  return out;
10994
11288
  }
11289
+ function parseSystemdUnit(text) {
11290
+ let dataDir = null;
11291
+ let host = DEFAULT_SERVE_HOST;
11292
+ let port = DEFAULT_SERVE_PORT;
11293
+ for (const line of text.split("\n")) {
11294
+ const trimmed = line.trim();
11295
+ if (trimmed.startsWith("WorkingDirectory=")) {
11296
+ const v = trimmed.slice("WorkingDirectory=".length).trim();
11297
+ if (v && isAbsolutePath(v)) dataDir = v;
11298
+ } else if (trimmed.startsWith("ExecStart=")) {
11299
+ const tokens = trimmed.slice("ExecStart=".length).trim().split(/\s+/).map((t) => t.replace(/^["']|["']$/g, ""));
11300
+ const parsed = parseServeArgs(tokens);
11301
+ if (!parsed) continue;
11302
+ if (parsed.dataDir && isAbsolutePath(parsed.dataDir)) dataDir = parsed.dataDir;
11303
+ host = parsed.host;
11304
+ port = parsed.port;
11305
+ }
11306
+ }
11307
+ if (!dataDir) return null;
11308
+ return { dataDir: normalizePath(dataDir), host, port, managed: true };
11309
+ }
11310
+ function systemdServiceInstances() {
11311
+ if (process.platform !== "linux") return [];
11312
+ const unit = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME2;
11313
+ const configHome = process.env.XDG_CONFIG_HOME?.trim() || path22.join(os5.homedir(), ".config");
11314
+ const units = [
11315
+ `/etc/systemd/system/${unit}.service`,
11316
+ path22.join(configHome, "systemd", "user", `${unit}.service`)
11317
+ ];
11318
+ const out = [];
11319
+ for (const file of units) {
11320
+ let text;
11321
+ try {
11322
+ text = fs19.readFileSync(file, "utf-8");
11323
+ } catch {
11324
+ continue;
11325
+ }
11326
+ const inst = parseSystemdUnit(text);
11327
+ if (inst) out.push(inst);
11328
+ }
11329
+ return out;
11330
+ }
11331
+ function windowsServiceInstances() {
11332
+ if (process.platform !== "win32") return [];
11333
+ const appdata = process.env.APPDATA?.trim();
11334
+ if (!appdata) return [];
11335
+ const base = path22.join(appdata, "zqagent");
11336
+ try {
11337
+ const raw = fs19.readFileSync(path22.join(base, "runtime", "service.json"), "utf-8");
11338
+ const v = JSON.parse(raw);
11339
+ const dataDir = v.dataDir && isAbsolutePath(v.dataDir) ? v.dataDir : base;
11340
+ const port = Number(v.port);
11341
+ return [
11342
+ {
11343
+ dataDir: normalizePath(dataDir),
11344
+ host: DEFAULT_SERVE_HOST,
11345
+ port: Number.isFinite(port) && port > 0 ? port : DEFAULT_SERVE_PORT,
11346
+ managed: true
11347
+ }
11348
+ ];
11349
+ } catch {
11350
+ return [];
11351
+ }
11352
+ }
11353
+ function mergeServeInstances(discovered, managed) {
11354
+ const out = [];
11355
+ const push = (inst) => {
11356
+ if (!out.some((o) => o.dataDir === inst.dataDir)) out.push(inst);
11357
+ };
11358
+ for (const inst of discovered) {
11359
+ const hit = managed.find((m) => m.dataDir === inst.dataDir);
11360
+ push(hit ? { ...inst, managed: true } : inst);
11361
+ }
11362
+ for (const inst of managed) push(inst);
11363
+ return out;
11364
+ }
10995
11365
  function discoverRunningServeInstances() {
10996
- const found = process.platform === "linux" ? discoverFromProc() : discoverFromPs();
10997
- const seen = /* @__PURE__ */ new Set();
10998
- return found.filter((inst) => {
10999
- if (seen.has(inst.dataDir)) return false;
11000
- seen.add(inst.dataDir);
11001
- return true;
11002
- });
11366
+ const found = process.platform === "linux" ? discoverFromProc() : process.platform === "win32" ? discoverFromWindows() : discoverFromPs();
11367
+ const declared = process.platform === "linux" ? systemdServiceInstances() : process.platform === "win32" ? windowsServiceInstances() : launchdServiceInstances();
11368
+ return mergeServeInstances(found, declared);
11003
11369
  }
11004
11370
 
11005
11371
  // src/cli/link.ts
11372
+ function expandHome(p) {
11373
+ const home = process.env.HOME || process.env.USERPROFILE;
11374
+ if (!home) return p;
11375
+ if (p === "~") return home;
11376
+ if (p.startsWith("~/") || p.startsWith("~\\")) return path23.join(home, p.slice(2));
11377
+ return p;
11378
+ }
11379
+ function installDataDirHint() {
11380
+ if (process.platform === "darwin") return "$HOME/Library/Application Support/zqagent";
11381
+ if (process.platform === "win32") return "%APPDATA%\\zqagent";
11382
+ return "/var/lib/zqagent\uFF08\u9700 root\uFF1B\u666E\u901A\u7528\u6237\u7528 ~/.zqagent_data\uFF09";
11383
+ }
11006
11384
  function parseLinkArgs(argv) {
11007
11385
  const out = {};
11008
11386
  for (let i = 0; i < argv.length; i++) {
@@ -11075,20 +11453,47 @@ async function enrollViaServer(host, port, adminKey, opts) {
11075
11453
  }
11076
11454
  return payload?.data;
11077
11455
  }
11456
+ function hotWarnings(inst, running) {
11457
+ const warnings = [];
11458
+ const managed = running.filter((r) => r.managed && r.dataDir !== inst.dataDir);
11459
+ if (!inst.managed && managed.length > 0) {
11460
+ warnings.push(
11461
+ `\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`
11462
+ );
11463
+ warnings.push(
11464
+ "\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"
11465
+ );
11466
+ }
11467
+ const others = [...new Set(running.map((r) => r.dataDir).filter((d) => d !== inst.dataDir))];
11468
+ if (others.length > 0) {
11469
+ 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`);
11470
+ }
11471
+ return warnings;
11472
+ }
11078
11473
  async function linkNode(opts) {
11079
- const explicitDataDir = opts.dataDir?.trim() || process.env.DATA_DIR?.trim() || void 0;
11474
+ const rawDataDir = opts.dataDir?.trim() || process.env.DATA_DIR?.trim() || void 0;
11475
+ const explicitDataDir = rawDataDir ? expandHome(rawDataDir) : void 0;
11080
11476
  const host = opts.host?.trim() || process.env.HOST?.trim() || DEFAULT_SERVE_HOST;
11081
11477
  const port = opts.port ?? Number(process.env.PORT ?? DEFAULT_SERVE_PORT);
11082
- const candidates = explicitDataDir ? buildHotCandidates(explicitDataDir, [], { host, port }) : buildHotCandidates(void 0, discoverRunningServeInstances(), { host, port });
11478
+ const running = discoverRunningServeInstances();
11479
+ const candidates = buildHotCandidates(explicitDataDir, running, { host, port });
11083
11480
  for (const inst of candidates) {
11084
11481
  const store2 = new Store(inst.dataDir);
11085
11482
  const adminKey = process.env.ADMIN_API_KEY ?? store2.loadAdminKey();
11086
11483
  if (!adminKey) continue;
11087
11484
  if (!await probeServer(inst.host, inst.port, adminKey)) continue;
11088
11485
  const status = await enrollViaServer(inst.host, inst.port, adminKey, opts);
11089
- return { mode: "hot", ...status, dataDir: inst.dataDir };
11486
+ return {
11487
+ mode: "hot",
11488
+ ...status,
11489
+ dataDir: inst.dataDir,
11490
+ managed: inst.managed,
11491
+ warnings: hotWarnings(inst, running)
11492
+ };
11090
11493
  }
11091
- const dataDir = path22.resolve(explicitDataDir ?? DEFAULT_DATA_DIR);
11494
+ const managedDir = running.find((r) => r.managed)?.dataDir;
11495
+ const dataDir = path23.resolve(explicitDataDir ?? managedDir ?? DEFAULT_DATA_DIR);
11496
+ const targetedService = !explicitDataDir && Boolean(managedDir) && dataDir === managedDir;
11092
11497
  const store = new Store(dataDir);
11093
11498
  const nodeId = ensureNodeId(store);
11094
11499
  const cred = await hubEnrollByCode(opts.hubUrl, {
@@ -11096,25 +11501,45 @@ async function linkNode(opts) {
11096
11501
  nodeId,
11097
11502
  label: opts.label
11098
11503
  });
11099
- store.saveFleetCredential({
11100
- ...cred,
11101
- label: opts.label ?? cred.label,
11102
- enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
11103
- });
11504
+ try {
11505
+ store.saveFleetCredential({
11506
+ ...cred,
11507
+ label: opts.label ?? cred.label,
11508
+ enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
11509
+ });
11510
+ } catch (e) {
11511
+ const err = e;
11512
+ if (err.code === "EACCES" || err.code === "EPERM") {
11513
+ throw new Error(
11514
+ `\u65E0\u6CD5\u5199\u5165\u6570\u636E\u76EE\u5F55 ${dataDir}\uFF08${err.code}: ${err.message}\uFF09
11515
+ \u8BE5\u76EE\u5F55\u5F53\u524D\u7528\u6237\u6CA1\u6709\u5199\u6743\u9650\u3002\u4E24\u79CD\u6539\u6CD5\uFF1A
11516
+ 1) \u53BB\u6389 --data-dir\uFF0C\u8BA9 link \u81EA\u52A8\u53D1\u73B0\u672C\u673A\u8FD0\u884C\u7684\u8282\u70B9\uFF08\u63A8\u8350\uFF09\uFF1B
11517
+ 2) \u7528\u5F53\u524D\u7528\u6237\u53EF\u5199\u7684\u76EE\u5F55\uFF0C\u672C\u5E73\u53F0\u5B89\u88C5\u811A\u672C\u9ED8\u8BA4\uFF1A${installDataDirHint()}
11518
+ \uFF08\u63A5\u5165\u7801\u4E00\u6B21\u6027\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\uFF09`
11519
+ );
11520
+ }
11521
+ throw e;
11522
+ }
11523
+ const otherRunningDirs = [
11524
+ ...new Set(running.map((r) => r.dataDir).filter((d) => d !== dataDir))
11525
+ ];
11104
11526
  return {
11105
11527
  mode: "saved",
11106
11528
  nodeId,
11107
11529
  gatewayUrl: cred.gatewayUrl,
11108
11530
  label: opts.label,
11109
11531
  dataDir,
11110
- explicitDataDir: Boolean(explicitDataDir)
11532
+ explicitDataDir: Boolean(explicitDataDir),
11533
+ runningServiceDirs: otherRunningDirs,
11534
+ targetedService
11111
11535
  };
11112
11536
  }
11113
11537
  function printLinkHelp() {
11114
- cliLog("\u7528\u6CD5: zqagent link --hub <url> --code <\u6CE8\u518C\u7801> [--label \u540D\u79F0] [--data-dir \u76EE\u5F55]");
11538
+ cliLog("\u7528\u6CD5: zqagent link --hub <url> --code <\u6CE8\u518C\u7801> [--label \u540D\u79F0]");
11115
11539
  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");
11116
- 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");
11117
- cliLog(" \u5426\u5219\u5199\u5165 <\u6570\u636E\u76EE\u5F55>/fleet.json\uFF0C\u4E0B\u6B21\u542F\u52A8\u81EA\u52A8\u63A5\u5165\u3002");
11540
+ 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");
11541
+ 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");
11542
+ 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");
11118
11543
  }
11119
11544
  async function runLinkCommand(argv) {
11120
11545
  const args = parseLinkArgs(argv);
@@ -11142,20 +11567,36 @@ async function runLinkCommand(argv) {
11142
11567
  cliLog("\u2705 \u5DF2\u63A5\u5165 Hub\uFF08\u70ED\u63A5\u5165\uFF0C\u514D\u91CD\u542F\uFF09");
11143
11568
  cliLog(` \u8282\u70B9: ${r.nodeId}${r.label ? ` (${r.label})` : ""}`);
11144
11569
  cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"} \u5728\u7EBF: ${r.online ? "\u662F" : "\u8FDE\u63A5\u4E2D\u2026"}`);
11145
- cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}`);
11570
+ cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}${r.managed ? "\uFF08\u6258\u7BA1\u670D\u52A1\uFF0C\u5F00\u673A\u81EA\u542F\uFF09" : ""}`);
11571
+ if (r.warnings.length > 0) {
11572
+ cliLog("");
11573
+ for (const w of r.warnings) cliLog(`\u26A0\uFE0F ${w}`);
11574
+ }
11146
11575
  } else {
11147
11576
  cliLog("\u2705 \u63A5\u5165\u51ED\u636E\u5DF2\u4FDD\u5B58\uFF08\u672C\u673A\u6682\u65E0\u8FD0\u884C\u4E2D\u7684 server\uFF09");
11148
11577
  cliLog(` \u8282\u70B9: ${r.nodeId}`);
11149
11578
  cliLog(` \u7F51\u5173: ${r.gatewayUrl ?? "\uFF08\u672A\u4E0B\u53D1\uFF09"}`);
11150
11579
  cliLog(` \u6570\u636E\u76EE\u5F55: ${r.dataDir}`);
11151
11580
  cliLog(" \u4E0B\u6B21\u542F\u52A8 server \u5373\u81EA\u52A8\u63A5\u5165\uFF1B\u6216\u8FD0\u884C zqagent serve \u5E38\u9A7B\u4E3A\u8282\u70B9\u3002");
11152
- if (!r.explicitDataDir) {
11581
+ if (r.targetedService) {
11153
11582
  cliLog("");
11154
- cliLog("\u26A0\uFE0F \u672A\u6307\u5B9A --data-dir\uFF0C\u51ED\u636E\u5199\u5165\u4E86\u5F53\u524D\u76EE\u5F55\u4E0B\u7684 .zqagent_data\u3002");
11155
- 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");
11156
- 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");
11157
- cliLog(" zqagent link --hub <Hub\u5730\u5740> --code <\u65B0\u63A5\u5165\u7801> --data-dir <\u670D\u52A1\u6570\u636E\u76EE\u5F55>");
11158
- 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");
11583
+ 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");
11584
+ cliLog(" \u7ACB\u5373\u751F\u6548\uFF1A\u91CD\u542F\u8BE5\u670D\u52A1\uFF08Linux: systemctl restart zqagent\uFF1B");
11585
+ cliLog(" macOS: launchctl kickstart -k gui/$(id -u)/com.zqagent.node\uFF09\u3002");
11586
+ } else if (r.runningServiceDirs.length > 0) {
11587
+ cliLog("");
11588
+ 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");
11589
+ cliLog(` \u672C\u6B21\u5199\u5165: ${r.dataDir}`);
11590
+ for (const d of r.runningServiceDirs) cliLog(` \u670D\u52A1\u4F7F\u7528: ${d}`);
11591
+ 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");
11592
+ 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");
11593
+ cliLog(` zqagent link --hub <Hub\u5730\u5740> --code <\u65B0\u63A5\u5165\u7801> --data-dir "${r.runningServiceDirs[0]}"`);
11594
+ } else if (!r.explicitDataDir) {
11595
+ cliLog("");
11596
+ 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");
11597
+ 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");
11598
+ cliLog(" curl -fsSL https://agent.cd-zq.com/install.sh | sh");
11599
+ cliLog(` \uFF08\u670D\u52A1\u9ED8\u8BA4\u6570\u636E\u76EE\u5F55\uFF1A${installDataDirHint()}\uFF09`);
11159
11600
  }
11160
11601
  }
11161
11602
  return true;
@@ -11175,21 +11616,21 @@ import * as path38 from "path";
11175
11616
 
11176
11617
  // src/server/session.ts
11177
11618
  init_esm_shims();
11178
- import * as path25 from "path";
11619
+ import * as path26 from "path";
11179
11620
  import { AsyncLocalStorage } from "async_hooks";
11180
11621
 
11181
11622
  // src/server/mcpConfig.ts
11182
11623
  init_esm_shims();
11183
- import fs19 from "fs";
11184
- import path23 from "path";
11624
+ import fs20 from "fs";
11625
+ import path24 from "path";
11185
11626
  function isValidMcpConfig(v) {
11186
11627
  return !!v && typeof v === "object" && typeof v.name === "string" && (v.type === "stdio" || v.type === "http");
11187
11628
  }
11188
11629
  function readConfigFile(file) {
11189
11630
  if (!file) return [];
11190
11631
  try {
11191
- if (!fs19.existsSync(file)) return [];
11192
- const raw = JSON.parse(fs19.readFileSync(file, "utf8"));
11632
+ if (!fs20.existsSync(file)) return [];
11633
+ const raw = JSON.parse(fs20.readFileSync(file, "utf8"));
11193
11634
  const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.servers) ? raw.servers : [];
11194
11635
  return list.filter(isValidMcpConfig);
11195
11636
  } catch (err) {
@@ -11201,7 +11642,7 @@ function hasServerMcpConfig() {
11201
11642
  if (readConfigFile(process.env.MCP_CONFIG_PATH).length > 0) return true;
11202
11643
  const workspace = process.env.WORKSPACE;
11203
11644
  if (!workspace) return false;
11204
- return readConfigFile(path23.join(workspace, ".agent", "mcp.json")).length > 0;
11645
+ return readConfigFile(path24.join(workspace, ".agent", "mcp.json")).length > 0;
11205
11646
  }
11206
11647
  function loadServerMcpConfigs(tenant) {
11207
11648
  const fromEnv = readConfigFile(process.env.MCP_CONFIG_PATH);
@@ -11211,7 +11652,7 @@ function loadServerMcpConfigs(tenant) {
11211
11652
  }
11212
11653
  const workspace = tenant.workspaceRoot ?? process.env.WORKSPACE;
11213
11654
  if (workspace)
11214
- return readConfigFile(path23.join(workspace, ".agent", "mcp.json"));
11655
+ return readConfigFile(path24.join(workspace, ".agent", "mcp.json"));
11215
11656
  return [];
11216
11657
  }
11217
11658
 
@@ -11356,8 +11797,8 @@ function resolveSessionProvider(store, tenant, providerId) {
11356
11797
 
11357
11798
  // src/server/usage.ts
11358
11799
  init_esm_shims();
11359
- import * as fs20 from "fs";
11360
- import * as path24 from "path";
11800
+ import * as fs21 from "fs";
11801
+ import * as path25 from "path";
11361
11802
  var LEDGER_RETENTION_MONTHS = 12;
11362
11803
  var USAGE_SCHEMA = 1;
11363
11804
  var DEFAULT_PRICING = {
@@ -11438,15 +11879,15 @@ function retainedMonths(now = /* @__PURE__ */ new Date()) {
11438
11879
  return out;
11439
11880
  }
11440
11881
  function atomicWrite(file, content) {
11441
- fs20.mkdirSync(path24.dirname(file), { recursive: true });
11882
+ fs21.mkdirSync(path25.dirname(file), { recursive: true });
11442
11883
  const tmp = `${file}.tmp`;
11443
- fs20.writeFileSync(tmp, content, "utf-8");
11444
- fs20.renameSync(tmp, file);
11884
+ fs21.writeFileSync(tmp, content, "utf-8");
11885
+ fs21.renameSync(tmp, file);
11445
11886
  }
11446
11887
  var UsageLedger = class {
11447
11888
  constructor(root) {
11448
11889
  this.root = root;
11449
- this.dir = path24.join(root, "usage");
11890
+ this.dir = path25.join(root, "usage");
11450
11891
  }
11451
11892
  root;
11452
11893
  dir;
@@ -11459,18 +11900,18 @@ var UsageLedger = class {
11459
11900
  return this.dir;
11460
11901
  }
11461
11902
  ledgerFile(month) {
11462
- return path24.join(this.ledgerDir, `ledger-${month}.jsonl`);
11903
+ return path25.join(this.ledgerDir, `ledger-${month}.jsonl`);
11463
11904
  }
11464
11905
  get rollupFile() {
11465
- return path24.join(this.ledgerDir, "rollup.json");
11906
+ return path25.join(this.ledgerDir, "rollup.json");
11466
11907
  }
11467
11908
  get pricingFile() {
11468
- return path24.join(this.ledgerDir, "pricing.json");
11909
+ return path25.join(this.ledgerDir, "pricing.json");
11469
11910
  }
11470
11911
  // ===================== 初始化 =====================
11471
11912
  init(now = /* @__PURE__ */ new Date()) {
11472
11913
  if (this.inited) return;
11473
- fs20.mkdirSync(this.ledgerDir, { recursive: true });
11914
+ fs21.mkdirSync(this.ledgerDir, { recursive: true });
11474
11915
  this.pricing = this.loadPricing();
11475
11916
  this.rollup = this.loadRollup(now);
11476
11917
  this.pruneLedgers(now);
@@ -11478,7 +11919,7 @@ var UsageLedger = class {
11478
11919
  }
11479
11920
  loadPricing() {
11480
11921
  try {
11481
- const raw = fs20.readFileSync(this.pricingFile, "utf-8");
11922
+ const raw = fs21.readFileSync(this.pricingFile, "utf-8");
11482
11923
  const parsed = JSON.parse(raw);
11483
11924
  const vendors = {};
11484
11925
  for (const v of /* @__PURE__ */ new Set([
@@ -11507,7 +11948,7 @@ var UsageLedger = class {
11507
11948
  }
11508
11949
  loadRollup(now) {
11509
11950
  try {
11510
- const raw = fs20.readFileSync(this.rollupFile, "utf-8");
11951
+ const raw = fs21.readFileSync(this.rollupFile, "utf-8");
11511
11952
  const parsed = JSON.parse(raw);
11512
11953
  return {
11513
11954
  schema: USAGE_SCHEMA,
@@ -11534,10 +11975,10 @@ var UsageLedger = class {
11534
11975
  const keep = retainedMonths(now);
11535
11976
  let changed = false;
11536
11977
  try {
11537
- for (const f of fs20.readdirSync(this.ledgerDir)) {
11978
+ for (const f of fs21.readdirSync(this.ledgerDir)) {
11538
11979
  const m = /^ledger-(\d{4}-\d{2})\.jsonl$/.exec(f);
11539
11980
  if (m && !keep.has(m[1])) {
11540
- fs20.unlinkSync(path24.join(this.ledgerDir, f));
11981
+ fs21.unlinkSync(path25.join(this.ledgerDir, f));
11541
11982
  }
11542
11983
  }
11543
11984
  } catch {
@@ -11614,7 +12055,7 @@ var UsageLedger = class {
11614
12055
  };
11615
12056
  const line = JSON.stringify(rec) + "\n";
11616
12057
  try {
11617
- fs20.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
12058
+ fs21.appendFileSync(this.ledgerFile(monthOf(rec.ts)), line);
11618
12059
  } catch (e) {
11619
12060
  console.error(`[usage] \u5199\u8D26\u672C\u5931\u8D25: ${String(e)}`);
11620
12061
  }
@@ -11786,10 +12227,10 @@ var UsageLedger = class {
11786
12227
  const rows = [];
11787
12228
  for (const month of months) {
11788
12229
  const file = this.ledgerFile(month);
11789
- if (!fs20.existsSync(file)) continue;
12230
+ if (!fs21.existsSync(file)) continue;
11790
12231
  let content = "";
11791
12232
  try {
11792
- content = fs20.readFileSync(file, "utf-8");
12233
+ content = fs21.readFileSync(file, "utf-8");
11793
12234
  } catch {
11794
12235
  continue;
11795
12236
  }
@@ -11898,10 +12339,10 @@ var UsageLedger = class {
11898
12339
  const seen = /* @__PURE__ */ new Set();
11899
12340
  for (const month of this.monthsInRange("", "")) {
11900
12341
  const file = this.ledgerFile(month);
11901
- if (!fs20.existsSync(file)) continue;
12342
+ if (!fs21.existsSync(file)) continue;
11902
12343
  let content = "";
11903
12344
  try {
11904
- content = fs20.readFileSync(file, "utf-8");
12345
+ content = fs21.readFileSync(file, "utf-8");
11905
12346
  } catch {
11906
12347
  continue;
11907
12348
  }
@@ -12006,11 +12447,11 @@ function isAffirmative(answer) {
12006
12447
  return /^(yes|approve|同意|批准|允许|确定|可以|好的|确认)/.test(a);
12007
12448
  }
12008
12449
  function defer() {
12009
- let resolve23;
12450
+ let resolve24;
12010
12451
  const promise = new Promise((r) => {
12011
- resolve23 = r;
12452
+ resolve24 = r;
12012
12453
  });
12013
- return { promise, resolve: resolve23 };
12454
+ return { promise, resolve: resolve24 };
12014
12455
  }
12015
12456
  var SessionManager = class {
12016
12457
  constructor(store, approvalTimeoutMs = APPROVAL_TIMEOUT_MS, opts = {}) {
@@ -12582,11 +13023,11 @@ data: ${JSON.stringify({ sessionId, seq: entry.seq, ts: entry.ts, data: entry.da
12582
13023
  status: "pending",
12583
13024
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
12584
13025
  };
12585
- const { promise, resolve: resolve23 } = defer();
13026
+ const { promise, resolve: resolve24 } = defer();
12586
13027
  const timer = setTimeout(() => {
12587
13028
  this._recycleApproval(rt, id);
12588
13029
  }, this.approvalTimeoutMs);
12589
- rt.pendingApprovals.set(id, { promise, resolve: resolve23, record, timer });
13030
+ rt.pendingApprovals.set(id, { promise, resolve: resolve24, record, timer });
12590
13031
  rt.approvals.set(id, record);
12591
13032
  this._setStatus(rt, "waiting_approval");
12592
13033
  this._persist(rt);
@@ -12664,11 +13105,11 @@ data: ${JSON.stringify({ sessionId, seq: entry.seq, ts: entry.ts, data: entry.da
12664
13105
  status: "pending",
12665
13106
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
12666
13107
  };
12667
- const { promise, resolve: resolve23 } = defer();
13108
+ const { promise, resolve: resolve24 } = defer();
12668
13109
  const timer = setTimeout(() => {
12669
13110
  this._recycleQuestion(rt, qid);
12670
13111
  }, this.approvalTimeoutMs);
12671
- rt.pendingQuestions.set(qid, { promise, resolve: resolve23, record, timer });
13112
+ rt.pendingQuestions.set(qid, { promise, resolve: resolve24, record, timer });
12672
13113
  rt.questions.set(qid, record);
12673
13114
  this._setStatus(rt, "waiting_question");
12674
13115
  this._persist(rt);
@@ -12977,7 +13418,7 @@ data: ${JSON.stringify(entry.data)}
12977
13418
  if (rt.mcpReady) {
12978
13419
  await Promise.race([
12979
13420
  rt.mcpReady,
12980
- new Promise((resolve23) => setTimeout(resolve23, 15e3))
13421
+ new Promise((resolve24) => setTimeout(resolve24, 15e3))
12981
13422
  ]).catch(() => void 0);
12982
13423
  }
12983
13424
  for await (const event of gen) {
@@ -13212,7 +13653,7 @@ data: ${JSON.stringify(entry.data)}
13212
13653
  softThresholdPct: parseRatio(process.env.COMPACTION_SOFT_THRESHOLD_PCT),
13213
13654
  capTokens: parseOptionalPositiveNumber(process.env.COMPACTION_CAP_TOKENS) ?? null,
13214
13655
  // 超预算工具结果按租户/工作区隔离落盘(多租户共用一个状态目录会混内容)
13215
- toolResultDir: path25.join(workspace, ".agent", "tool-results")
13656
+ toolResultDir: path26.join(workspace, ".agent", "tool-results")
13216
13657
  });
13217
13658
  engine.setApprover(this._makeApprover(rt));
13218
13659
  const mcpConfigs = loadServerMcpConfigs(tenant);
@@ -13393,11 +13834,11 @@ var ApiError = class extends Error {
13393
13834
  // src/server/routes.ts
13394
13835
  init_esm_shims();
13395
13836
  import { createServer } from "http";
13396
- import * as path30 from "path";
13837
+ import * as path31 from "path";
13397
13838
 
13398
13839
  // src/server/projects.ts
13399
13840
  init_esm_shims();
13400
- import * as path26 from "path";
13841
+ import * as path27 from "path";
13401
13842
  var DEFAULT_MODEL = () => process.env.OPENAI_MODEL || process.env.MODEL || "gpt-4o";
13402
13843
  function toProjectSummary(store, tenant) {
13403
13844
  return {
@@ -13530,9 +13971,9 @@ function createProject(store, input) {
13530
13971
  if (store.loadTenant(id)) {
13531
13972
  throw new ApiError(409, "project_exists", `\u9879\u76EE ${id} \u5DF2\u5B58\u5728`);
13532
13973
  }
13533
- const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path26.resolve(raw.workspaceRoot.trim()) : void 0;
13974
+ const workspaceRoot = typeof raw.workspaceRoot === "string" && raw.workspaceRoot.trim() ? path27.resolve(raw.workspaceRoot.trim()) : void 0;
13534
13975
  const explicitName = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : "";
13535
- const defaultName = workspaceRoot ? path26.basename(workspaceRoot) : id;
13976
+ const defaultName = workspaceRoot ? path27.basename(workspaceRoot) : id;
13536
13977
  const tenant = {
13537
13978
  id,
13538
13979
  name: explicitName || defaultName,
@@ -13562,7 +14003,7 @@ function buildNodeInfo(store, opts) {
13562
14003
  nodeId: opts.nodeId ?? ensureNodeId2(store),
13563
14004
  platform: process.platform,
13564
14005
  mode: process.env.NODE_MODE ?? "standalone",
13565
- version: "0.2.8",
14006
+ version: "0.2.11",
13566
14007
  admin: opts.admin,
13567
14008
  projects: opts.projects
13568
14009
  };
@@ -13741,34 +14182,34 @@ async function handleHubProxy(req2, res) {
13741
14182
 
13742
14183
  // src/server/fsBrowser.ts
13743
14184
  init_esm_shims();
13744
- import * as fs21 from "fs";
13745
- import * as os4 from "os";
13746
- import * as path27 from "path";
14185
+ import * as fs22 from "fs";
14186
+ import * as os6 from "os";
14187
+ import * as path28 from "path";
13747
14188
  var MAX_ENTRIES = 2e3;
13748
14189
  var DEFAULT_SEARCH_LIMIT = 200;
13749
14190
  var MAX_SEARCH_LIMIT = 1e3;
13750
14191
  function listDirs(input) {
13751
- const base = (input ?? "").trim() || os4.homedir();
13752
- const target = path27.resolve(base);
14192
+ const base = (input ?? "").trim() || os6.homedir();
14193
+ const target = path28.resolve(base);
13753
14194
  let st;
13754
14195
  try {
13755
- st = fs21.statSync(target);
14196
+ st = fs22.statSync(target);
13756
14197
  } catch {
13757
14198
  throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
13758
14199
  }
13759
14200
  if (!st.isDirectory()) throw new Error(`\u4E0D\u662F\u76EE\u5F55: ${target}`);
13760
14201
  let dirents;
13761
14202
  try {
13762
- dirents = fs21.readdirSync(target, { withFileTypes: true });
14203
+ dirents = fs22.readdirSync(target, { withFileTypes: true });
13763
14204
  } catch {
13764
14205
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
13765
14206
  }
13766
14207
  const entries = dirents.map((d) => {
13767
- const full = path27.join(target, d.name);
14208
+ const full = path28.join(target, d.name);
13768
14209
  let isDir = d.isDirectory();
13769
14210
  if (d.isSymbolicLink()) {
13770
14211
  try {
13771
- isDir = fs21.statSync(full).isDirectory();
14212
+ isDir = fs22.statSync(full).isDirectory();
13772
14213
  } catch {
13773
14214
  }
13774
14215
  }
@@ -13777,7 +14218,7 @@ function listDirs(input) {
13777
14218
  entries.sort(
13778
14219
  (a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
13779
14220
  );
13780
- const parentDir = path27.dirname(target);
14221
+ const parentDir = path28.dirname(target);
13781
14222
  return {
13782
14223
  path: target,
13783
14224
  parent: parentDir !== target ? parentDir : null,
@@ -13808,7 +14249,7 @@ var NAME_CACHE_MAX_DIRS = 4;
13808
14249
  var NAME_CACHE_MAX_ENTRIES = 5e4;
13809
14250
  var nameCache = /* @__PURE__ */ new Map();
13810
14251
  function invalidateDirNames(target) {
13811
- nameCache.delete(path27.resolve(target));
14252
+ nameCache.delete(path28.resolve(target));
13812
14253
  }
13813
14254
  async function loadDirNames(target) {
13814
14255
  const now = Date.now();
@@ -13820,7 +14261,7 @@ async function loadDirNames(target) {
13820
14261
  }
13821
14262
  let dirents;
13822
14263
  try {
13823
- dirents = await fs21.promises.readdir(target, { withFileTypes: true });
14264
+ dirents = await fs22.promises.readdir(target, { withFileTypes: true });
13824
14265
  } catch {
13825
14266
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09: ${target}`);
13826
14267
  }
@@ -13843,22 +14284,22 @@ async function loadDirNames(target) {
13843
14284
  return entries;
13844
14285
  }
13845
14286
  async function toDirEntry(dir, e) {
13846
- const full = path27.join(dir, e.name);
14287
+ const full = path28.join(dir, e.name);
13847
14288
  let isDir = e.isDir;
13848
14289
  if (e.isLink) {
13849
14290
  try {
13850
- isDir = (await fs21.promises.stat(full)).isDirectory();
14291
+ isDir = (await fs22.promises.stat(full)).isDirectory();
13851
14292
  } catch {
13852
14293
  }
13853
14294
  }
13854
14295
  return { name: e.name, path: full, type: isDir ? "dir" : "file" };
13855
14296
  }
13856
14297
  async function searchDirs(input, query, limit) {
13857
- const base = (input ?? "").trim() || os4.homedir();
13858
- const target = path27.resolve(base);
14298
+ const base = (input ?? "").trim() || os6.homedir();
14299
+ const target = path28.resolve(base);
13859
14300
  let st;
13860
14301
  try {
13861
- st = await fs21.promises.stat(target);
14302
+ st = await fs22.promises.stat(target);
13862
14303
  } catch {
13863
14304
  throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`);
13864
14305
  }
@@ -13888,7 +14329,7 @@ async function searchDirs(input, query, limit) {
13888
14329
  );
13889
14330
  const hit = scored.slice(0, max);
13890
14331
  const entries = await Promise.all(hit.map(({ entry }) => toDirEntry(target, entry)));
13891
- const parentDir = path27.dirname(target);
14332
+ const parentDir = path28.dirname(target);
13892
14333
  return {
13893
14334
  path: target,
13894
14335
  parent: parentDir !== target ? parentDir : null,
@@ -13899,52 +14340,52 @@ async function searchDirs(input, query, limit) {
13899
14340
  };
13900
14341
  }
13901
14342
  function makeDirIn(parentInput, name) {
13902
- const parent = path27.resolve((parentInput ?? "").trim() || os4.homedir());
14343
+ const parent = path28.resolve((parentInput ?? "").trim() || os6.homedir());
13903
14344
  const clean = (name ?? "").trim();
13904
14345
  if (!clean) throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u4E3A\u7A7A");
13905
14346
  if (clean === "." || clean === "..") throw new Error("\u76EE\u5F55\u540D\u975E\u6CD5");
13906
- const target = path27.resolve(parent, clean);
13907
- const rel = path27.relative(parent, target);
13908
- if (!rel || rel.startsWith("..") || path27.isAbsolute(rel)) {
14347
+ const target = path28.resolve(parent, clean);
14348
+ const rel = path28.relative(parent, target);
14349
+ if (!rel || rel.startsWith("..") || path28.isAbsolute(rel)) {
13909
14350
  throw new Error("\u76EE\u5F55\u540D\u4E0D\u80FD\u8D8A\u51FA\u5F53\u524D\u76EE\u5F55");
13910
14351
  }
13911
14352
  let st = null;
13912
14353
  try {
13913
- st = fs21.statSync(target);
14354
+ st = fs22.statSync(target);
13914
14355
  } catch {
13915
14356
  st = null;
13916
14357
  }
13917
14358
  if (st && !st.isDirectory()) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u6587\u4EF6: ${target}`);
13918
14359
  try {
13919
- fs21.mkdirSync(target, { recursive: true });
14360
+ fs22.mkdirSync(target, { recursive: true });
13920
14361
  } catch (e) {
13921
14362
  throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
13922
14363
  }
13923
14364
  invalidateDirNames(parent);
13924
- return { name: path27.basename(target), path: target, type: "dir" };
14365
+ return { name: path28.basename(target), path: target, type: "dir" };
13925
14366
  }
13926
14367
 
13927
14368
  // src/server/projectFiles.ts
13928
14369
  init_esm_shims();
13929
- import * as fs22 from "fs";
13930
- import * as path28 from "path";
14370
+ import * as fs23 from "fs";
14371
+ import * as path29 from "path";
13931
14372
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
13932
14373
  var MAX_DOWNLOAD_BYTES = 16 * 1024 * 1024;
13933
14374
  var MAX_ENTRIES2 = 2e3;
13934
14375
  function toPosix(p) {
13935
- return p.split(path28.sep).join("/");
14376
+ return p.split(path29.sep).join("/");
13936
14377
  }
13937
14378
  function projectRoot(workspaceRoot) {
13938
14379
  const r = (workspaceRoot ?? "").trim();
13939
14380
  if (!r) {
13940
14381
  throw new Error("\u9879\u76EE\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF08workspaceRoot\uFF09\uFF0C\u65E0\u6CD5\u6D4F\u89C8\u6587\u4EF6");
13941
14382
  }
13942
- const abs = path28.resolve(r);
13943
- if (!fs22.existsSync(abs)) {
14383
+ const abs = path29.resolve(r);
14384
+ if (!fs23.existsSync(abs)) {
13944
14385
  throw new Error(`\u5DE5\u4F5C\u533A\u76EE\u5F55\u4E0D\u5B58\u5728: ${abs}`);
13945
14386
  }
13946
14387
  try {
13947
- return fs22.realpathSync(abs);
14388
+ return fs23.realpathSync(abs);
13948
14389
  } catch {
13949
14390
  return abs;
13950
14391
  }
@@ -13962,37 +14403,37 @@ function listProjectFiles(workspaceRoot, relPath) {
13962
14403
  const dir = resolveWithin(base, relPath ?? "");
13963
14404
  let st;
13964
14405
  try {
13965
- st = fs22.statSync(dir);
14406
+ st = fs23.statSync(dir);
13966
14407
  } catch {
13967
- throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path28.relative(base, dir)) || "/"}`);
14408
+ throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${toPosix(path29.relative(base, dir)) || "/"}`);
13968
14409
  }
13969
14410
  if (!st.isDirectory()) throw new Error("\u76EE\u6807\u4E0D\u662F\u76EE\u5F55");
13970
14411
  let dirents;
13971
14412
  try {
13972
- dirents = fs22.readdirSync(dir, { withFileTypes: true });
14413
+ dirents = fs23.readdirSync(dir, { withFileTypes: true });
13973
14414
  } catch {
13974
14415
  throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF08\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09");
13975
14416
  }
13976
14417
  const entries = dirents.map((d) => {
13977
- const full = path28.join(dir, d.name);
14418
+ const full = path29.join(dir, d.name);
13978
14419
  let isDir = d.isDirectory();
13979
14420
  if (d.isSymbolicLink()) {
13980
14421
  try {
13981
- isDir = fs22.statSync(full).isDirectory();
14422
+ isDir = fs23.statSync(full).isDirectory();
13982
14423
  } catch {
13983
14424
  }
13984
14425
  }
13985
14426
  let size = 0;
13986
14427
  let mtime = "";
13987
14428
  try {
13988
- const s = fs22.statSync(full);
14429
+ const s = fs23.statSync(full);
13989
14430
  size = s.size;
13990
14431
  mtime = s.mtime.toISOString();
13991
14432
  } catch {
13992
14433
  }
13993
14434
  return {
13994
14435
  name: d.name,
13995
- path: toPosix(path28.relative(base, full)),
14436
+ path: toPosix(path29.relative(base, full)),
13996
14437
  type: isDir ? "dir" : "file",
13997
14438
  size,
13998
14439
  mtime
@@ -14001,12 +14442,12 @@ function listProjectFiles(workspaceRoot, relPath) {
14001
14442
  entries.sort(
14002
14443
  (a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "dir" ? -1 : 1
14003
14444
  );
14004
- const relDir = toPosix(path28.relative(base, dir));
14005
- const parentAbs = path28.dirname(dir);
14445
+ const relDir = toPosix(path29.relative(base, dir));
14446
+ const parentAbs = path29.dirname(dir);
14006
14447
  return {
14007
14448
  root: base,
14008
14449
  path: relDir,
14009
- parent: isWithinRoot(base, parentAbs) ? toPosix(path28.relative(base, parentAbs)) : null,
14450
+ parent: isWithinRoot(base, parentAbs) ? toPosix(path29.relative(base, parentAbs)) : null,
14010
14451
  entries: entries.slice(0, MAX_ENTRIES2)
14011
14452
  };
14012
14453
  }
@@ -14015,17 +14456,17 @@ function readProjectFile(workspaceRoot, relPath) {
14015
14456
  const file = resolveWithin(base, relPath ?? "");
14016
14457
  let st;
14017
14458
  try {
14018
- st = fs22.statSync(file);
14459
+ st = fs23.statSync(file);
14019
14460
  } catch {
14020
14461
  throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
14021
14462
  }
14022
14463
  if (!st.isFile()) throw new Error("\u76EE\u6807\u4E0D\u662F\u6587\u4EF6");
14023
- const buf = fs22.readFileSync(file);
14464
+ const buf = fs23.readFileSync(file);
14024
14465
  const binary = isBinary(buf);
14025
14466
  const truncated = buf.length > MAX_FILE_BYTES;
14026
14467
  const slice = truncated ? buf.subarray(0, MAX_FILE_BYTES) : buf;
14027
14468
  return {
14028
- path: toPosix(path28.relative(base, file)),
14469
+ path: toPosix(path29.relative(base, file)),
14029
14470
  size: st.size,
14030
14471
  binary,
14031
14472
  truncated,
@@ -14037,7 +14478,7 @@ function downloadProjectFile(workspaceRoot, relPath) {
14037
14478
  const file = resolveWithin(base, relPath ?? "");
14038
14479
  let st;
14039
14480
  try {
14040
- st = fs22.statSync(file);
14481
+ st = fs23.statSync(file);
14041
14482
  } catch {
14042
14483
  throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
14043
14484
  }
@@ -14045,10 +14486,10 @@ function downloadProjectFile(workspaceRoot, relPath) {
14045
14486
  if (st.size > MAX_DOWNLOAD_BYTES) {
14046
14487
  throw new Error(`\u6587\u4EF6\u8D85\u8FC7\u4E0B\u8F7D\u4E0A\u9650\uFF08${MAX_DOWNLOAD_BYTES / (1024 * 1024)}MB\uFF09`);
14047
14488
  }
14048
- const buf = fs22.readFileSync(file);
14489
+ const buf = fs23.readFileSync(file);
14049
14490
  return {
14050
- path: toPosix(path28.relative(base, file)),
14051
- name: path28.basename(file),
14491
+ path: toPosix(path29.relative(base, file)),
14492
+ name: path29.basename(file),
14052
14493
  size: buf.length,
14053
14494
  base64: buf.toString("base64")
14054
14495
  };
@@ -14070,12 +14511,12 @@ function writeProjectFile(workspaceRoot, relPath, content) {
14070
14511
  throw new Error("\u5185\u5BB9\u8D85\u8FC7 2MB \u4E0A\u9650\uFF0C\u65E0\u6CD5\u4FDD\u5B58");
14071
14512
  }
14072
14513
  try {
14073
- fs22.mkdirSync(path28.dirname(file), { recursive: true });
14074
- fs22.writeFileSync(file, buf);
14514
+ fs23.mkdirSync(path29.dirname(file), { recursive: true });
14515
+ fs23.writeFileSync(file, buf);
14075
14516
  } catch (e) {
14076
14517
  throw new Error(`\u5199\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
14077
14518
  }
14078
- return { path: toPosix(path28.relative(base, file)), size: buf.length };
14519
+ return { path: toPosix(path29.relative(base, file)), size: buf.length };
14079
14520
  }
14080
14521
  function createProjectDir(workspaceRoot, relPath) {
14081
14522
  const base = projectRoot(workspaceRoot);
@@ -14083,26 +14524,26 @@ function createProjectDir(workspaceRoot, relPath) {
14083
14524
  if (!rel) throw new Error("\u76EE\u5F55\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
14084
14525
  const dir = resolveWithin(base, rel);
14085
14526
  try {
14086
- fs22.mkdirSync(dir, { recursive: true });
14527
+ fs23.mkdirSync(dir, { recursive: true });
14087
14528
  } catch (e) {
14088
14529
  throw new Error(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
14089
14530
  }
14090
- return { path: toPosix(path28.relative(base, dir)) };
14531
+ return { path: toPosix(path29.relative(base, dir)) };
14091
14532
  }
14092
14533
  function createProjectFile(workspaceRoot, relPath, content = "") {
14093
14534
  const base = projectRoot(workspaceRoot);
14094
14535
  const rel = (relPath ?? "").trim();
14095
14536
  if (!rel) throw new Error("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
14096
14537
  const file = resolveWithin(base, rel);
14097
- if (fs22.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
14538
+ if (fs23.existsSync(file)) throw new Error("\u6587\u4EF6\u5DF2\u5B58\u5728");
14098
14539
  const buf = Buffer.from(String(content ?? ""), "utf-8");
14099
14540
  try {
14100
- fs22.mkdirSync(path28.dirname(file), { recursive: true });
14101
- fs22.writeFileSync(file, buf);
14541
+ fs23.mkdirSync(path29.dirname(file), { recursive: true });
14542
+ fs23.writeFileSync(file, buf);
14102
14543
  } catch (e) {
14103
14544
  throw new Error(`\u521B\u5EFA\u6587\u4EF6\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
14104
14545
  }
14105
- return { path: toPosix(path28.relative(base, file)), size: buf.length };
14546
+ return { path: toPosix(path29.relative(base, file)), size: buf.length };
14106
14547
  }
14107
14548
  function searchProjectFiles(workspaceRoot, query) {
14108
14549
  const base = projectRoot(workspaceRoot);
@@ -14125,7 +14566,7 @@ function searchProjectFiles(workspaceRoot, query) {
14125
14566
  if (out.length >= LIMIT) return;
14126
14567
  let dirents;
14127
14568
  try {
14128
- dirents = fs22.readdirSync(dir, { withFileTypes: true });
14569
+ dirents = fs23.readdirSync(dir, { withFileTypes: true });
14129
14570
  } catch {
14130
14571
  return;
14131
14572
  }
@@ -14136,7 +14577,7 @@ function searchProjectFiles(workspaceRoot, query) {
14136
14577
  let isDir = d.isDirectory();
14137
14578
  if (d.isSymbolicLink()) {
14138
14579
  try {
14139
- isDir = fs22.statSync(path28.join(dir, d.name)).isDirectory();
14580
+ isDir = fs23.statSync(path29.join(dir, d.name)).isDirectory();
14140
14581
  } catch {
14141
14582
  isDir = false;
14142
14583
  }
@@ -14144,7 +14585,7 @@ function searchProjectFiles(workspaceRoot, query) {
14144
14585
  if (d.name.toLowerCase().includes(q)) {
14145
14586
  out.push({ path: rel, type: isDir ? "dir" : "file" });
14146
14587
  }
14147
- if (isDir) walk(path28.join(dir, d.name), rel);
14588
+ if (isDir) walk(path29.join(dir, d.name), rel);
14148
14589
  }
14149
14590
  };
14150
14591
  walk(base, "");
@@ -14153,8 +14594,8 @@ function searchProjectFiles(workspaceRoot, query) {
14153
14594
 
14154
14595
  // src/server/projectOps.ts
14155
14596
  init_esm_shims();
14156
- import * as os5 from "os";
14157
- import * as path29 from "path";
14597
+ import * as os7 from "os";
14598
+ import * as path30 from "path";
14158
14599
  var MAX_STDOUT2 = 2e4;
14159
14600
  var MAX_STDERR2 = 1e4;
14160
14601
  var MAX_BUFFER3 = 1024 * 1024 * 5;
@@ -14168,7 +14609,7 @@ function resolveInvocation2(spec, prepared) {
14168
14609
  }
14169
14610
  function workRoot(workspaceRoot) {
14170
14611
  const r = (workspaceRoot ?? "").trim();
14171
- return r ? path29.resolve(r) : os5.homedir();
14612
+ return r ? path30.resolve(r) : os7.homedir();
14172
14613
  }
14173
14614
  async function runProjectCommand(workspaceRoot, command, timeoutSeconds) {
14174
14615
  const cmd = String(command ?? "");
@@ -14479,11 +14920,11 @@ function projectStats(manager, tenantId) {
14479
14920
 
14480
14921
  // src/server/nodeHost.ts
14481
14922
  init_esm_shims();
14482
- import * as fs23 from "fs";
14483
- import * as os6 from "os";
14923
+ import * as fs24 from "fs";
14924
+ import * as os8 from "os";
14484
14925
  function diskInfo(pathStr) {
14485
14926
  try {
14486
- const st = fs23.statfsSync(pathStr);
14927
+ const st = fs24.statfsSync(pathStr);
14487
14928
  return {
14488
14929
  path: pathStr,
14489
14930
  total: Number(st.blocks) * Number(st.bsize),
@@ -14495,7 +14936,7 @@ function diskInfo(pathStr) {
14495
14936
  }
14496
14937
  function networkInfo() {
14497
14938
  const out = [];
14498
- const ifaces = os6.networkInterfaces();
14939
+ const ifaces = os8.networkInterfaces();
14499
14940
  for (const [name, list] of Object.entries(ifaces)) {
14500
14941
  for (const info of list ?? []) {
14501
14942
  out.push({
@@ -14546,23 +14987,23 @@ function overview(store, manager) {
14546
14987
  return { projects, sessions: { total, running, waiting, items }, usage: { tokensIn, tokensOut } };
14547
14988
  }
14548
14989
  function buildNodeHost(store, manager, diskPath) {
14549
- const cpus2 = os6.cpus();
14990
+ const cpus2 = os8.cpus();
14550
14991
  const ov = overview(store, manager);
14551
14992
  return {
14552
14993
  nodeId: store.loadNodeId() ?? "",
14553
- hostname: os6.hostname(),
14994
+ hostname: os8.hostname(),
14554
14995
  platform: process.platform,
14555
14996
  arch: process.arch,
14556
- release: os6.release(),
14557
- uptimeSec: Math.round(os6.uptime()),
14558
- version: "0.2.8",
14997
+ release: os8.release(),
14998
+ uptimeSec: Math.round(os8.uptime()),
14999
+ version: "0.2.11",
14559
15000
  mode: process.env.NODE_MODE ?? "standalone",
14560
15001
  cpu: {
14561
15002
  count: cpus2.length,
14562
15003
  model: cpus2[0]?.model ?? "",
14563
- loadAvg: os6.loadavg().map((n) => Math.round(n * 100) / 100)
15004
+ loadAvg: os8.loadavg().map((n) => Math.round(n * 100) / 100)
14564
15005
  },
14565
- memory: { total: os6.totalmem(), free: os6.freemem() },
15006
+ memory: { total: os8.totalmem(), free: os8.freemem() },
14566
15007
  network: networkInfo(),
14567
15008
  disk: diskInfo(diskPath || process.cwd()),
14568
15009
  ...ov
@@ -14599,7 +15040,7 @@ function saveNotifications(tenant, secretBox, notifications) {
14599
15040
  var API_PREFIX = "/api/v1";
14600
15041
  var MAX_BODY_BYTES = 10 * 1024 * 1024;
14601
15042
  function readBody(req2) {
14602
- return new Promise((resolve23, reject) => {
15043
+ return new Promise((resolve24, reject) => {
14603
15044
  const chunks = [];
14604
15045
  let size = 0;
14605
15046
  req2.on("data", (c) => {
@@ -14612,9 +15053,9 @@ function readBody(req2) {
14612
15053
  chunks.push(c);
14613
15054
  });
14614
15055
  req2.on("end", () => {
14615
- if (chunks.length === 0) return resolve23(void 0);
15056
+ if (chunks.length === 0) return resolve24(void 0);
14616
15057
  try {
14617
- resolve23(JSON.parse(Buffer.concat(chunks).toString("utf-8")));
15058
+ resolve24(JSON.parse(Buffer.concat(chunks).toString("utf-8")));
14618
15059
  } catch {
14619
15060
  reject(new ApiError(400, "bad_request", "\u8BF7\u6C42\u4F53\u4E0D\u662F\u5408\u6CD5 JSON"));
14620
15061
  }
@@ -15130,8 +15571,18 @@ function createApp(store, manager, opts = {}) {
15130
15571
  applyProvidersPatch(tenant, raw);
15131
15572
  if (typeof raw.workspaceRoot === "string") {
15132
15573
  const ws = raw.workspaceRoot.trim();
15133
- if (ws) tenant.workspaceRoot = path30.resolve(ws);
15134
- else delete tenant.workspaceRoot;
15574
+ const next = ws ? path31.resolve(ws) : void 0;
15575
+ if (tenant.workspaceRoot) {
15576
+ if (next !== tenant.workspaceRoot) {
15577
+ throw new ApiError(
15578
+ 409,
15579
+ "workspace_locked",
15580
+ "\u8BE5\u9879\u76EE\u5DF2\u8BBE\u7F6E\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u4E0D\u80FD\u518D\u4FEE\u6539"
15581
+ );
15582
+ }
15583
+ } else if (next) {
15584
+ tenant.workspaceRoot = next;
15585
+ }
15135
15586
  }
15136
15587
  if (typeof raw.name === "string" && raw.name.trim()) {
15137
15588
  tenant.name = raw.name.trim();
@@ -16107,7 +16558,7 @@ function createApp(store, manager, opts = {}) {
16107
16558
  json(res, 200, {
16108
16559
  status: "ok",
16109
16560
  // 版本让运维/探针能确认线上跑的是哪个构建(更新与回滚据此判断)
16110
- version: "0.2.8",
16561
+ version: "0.2.11",
16111
16562
  features: { mcp: hasServerMcpConfig() }
16112
16563
  });
16113
16564
  return;
@@ -16202,7 +16653,7 @@ function createApp(store, manager, opts = {}) {
16202
16653
 
16203
16654
  // src/server/fleet/FleetNodeBridge.ts
16204
16655
  init_esm_shims();
16205
- import * as os7 from "os";
16656
+ import * as os9 from "os";
16206
16657
  import { monitorEventLoopDelay } from "perf_hooks";
16207
16658
 
16208
16659
  // src/server/fleet/FleetCommands.ts
@@ -16223,8 +16674,8 @@ async function cachedFetchModels(baseURL, apiKey) {
16223
16674
  try {
16224
16675
  result = await Promise.race([
16225
16676
  fetchModels(baseURL, apiKey),
16226
- new Promise((resolve23) => {
16227
- const t = setTimeout(() => resolve23(null), MODEL_FETCH_TIMEOUT_MS);
16677
+ new Promise((resolve24) => {
16678
+ const t = setTimeout(() => resolve24(null), MODEL_FETCH_TIMEOUT_MS);
16228
16679
  t.unref?.();
16229
16680
  })
16230
16681
  ]);
@@ -16249,8 +16700,8 @@ async function cachedFetchModelsEnriched(baseURL, apiKey, preferred) {
16249
16700
  if (!target) return r;
16250
16701
  const ctx = await Promise.race([
16251
16702
  fetchModelContext(baseURL, apiKey, target),
16252
- new Promise((resolve23) => {
16253
- const t = setTimeout(() => resolve23(void 0), MODEL_FETCH_TIMEOUT_MS);
16703
+ new Promise((resolve24) => {
16704
+ const t = setTimeout(() => resolve24(void 0), MODEL_FETCH_TIMEOUT_MS);
16254
16705
  t.unref?.();
16255
16706
  })
16256
16707
  ]);
@@ -17379,13 +17830,13 @@ var EVENT_FLUSH_MAX_EVENTS = 60;
17379
17830
  var EVENT_FLUSH_MAX_BYTES = 200 * 1024;
17380
17831
  var LOOP_LAG_WARN_MS = 2e3;
17381
17832
  function withTimeout(p, ms) {
17382
- return new Promise((resolve23, reject) => {
17833
+ return new Promise((resolve24, reject) => {
17383
17834
  const timer = setTimeout(() => reject(new Error(`\u5B50\u6307\u4EE4\u8D85\u65F6\uFF08${ms}ms\uFF09`)), ms);
17384
17835
  timer.unref?.();
17385
17836
  p.then(
17386
17837
  (v) => {
17387
17838
  clearTimeout(timer);
17388
- resolve23(v);
17839
+ resolve24(v);
17389
17840
  },
17390
17841
  (e) => {
17391
17842
  clearTimeout(timer);
@@ -17396,7 +17847,7 @@ function withTimeout(p, ms) {
17396
17847
  }
17397
17848
  function primaryIp() {
17398
17849
  let v6;
17399
- for (const infos of Object.values(os7.networkInterfaces())) {
17850
+ for (const infos of Object.values(os9.networkInterfaces())) {
17400
17851
  for (const info of infos ?? []) {
17401
17852
  if (info.internal) continue;
17402
17853
  if (info.family === "IPv4") return info.address;
@@ -17745,7 +18196,7 @@ var FleetNodeBridge = class {
17745
18196
  label: this.opts.deps.store.loadNodeLabel() ?? void 0,
17746
18197
  platform: process.platform,
17747
18198
  mode: process.env.NODE_MODE ?? "standalone",
17748
- version: "0.2.8",
18199
+ version: "0.2.11",
17749
18200
  ip: primaryIp()
17750
18201
  };
17751
18202
  }
@@ -17800,7 +18251,7 @@ var FleetNodeBridge = class {
17800
18251
  label: this.opts.deps.store.loadNodeLabel() ?? void 0,
17801
18252
  platform: process.platform,
17802
18253
  mode: process.env.NODE_MODE ?? "standalone",
17803
- version: "0.2.8",
18254
+ version: "0.2.11",
17804
18255
  ip: primaryIp(),
17805
18256
  projects,
17806
18257
  totals,
@@ -18569,8 +19020,8 @@ init_esm_shims();
18569
19020
 
18570
19021
  // src/server/qq/bindings.ts
18571
19022
  init_esm_shims();
18572
- import * as fs24 from "fs";
18573
- import * as path31 from "path";
19023
+ import * as fs25 from "fs";
19024
+ import * as path32 from "path";
18574
19025
  function conversationKey(target) {
18575
19026
  switch (target.kind) {
18576
19027
  case "c2c":
@@ -18609,12 +19060,12 @@ var QqBindingsStore = class _QqBindingsStore {
18609
19060
  cache = null;
18610
19061
  /** 基于 DATA_DIR 构造 */
18611
19062
  static in(dataDir) {
18612
- return new _QqBindingsStore(path31.join(dataDir, "qq", "bindings.json"));
19063
+ return new _QqBindingsStore(path32.join(dataDir, "qq", "bindings.json"));
18613
19064
  }
18614
19065
  load() {
18615
19066
  if (this.cache) return this.cache;
18616
19067
  try {
18617
- const raw = JSON.parse(fs24.readFileSync(this.file, "utf-8"));
19068
+ const raw = JSON.parse(fs25.readFileSync(this.file, "utf-8"));
18618
19069
  this.cache = {
18619
19070
  conversations: raw.conversations ?? {}
18620
19071
  };
@@ -18646,8 +19097,8 @@ var QqBindingsStore = class _QqBindingsStore {
18646
19097
  return null;
18647
19098
  }
18648
19099
  persist() {
18649
- fs24.mkdirSync(path31.dirname(this.file), { recursive: true });
18650
- fs24.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
19100
+ fs25.mkdirSync(path32.dirname(this.file), { recursive: true });
19101
+ fs25.writeFileSync(this.file, JSON.stringify(this.cache, null, 2), "utf-8");
18651
19102
  }
18652
19103
  };
18653
19104
 
@@ -19059,8 +19510,8 @@ ${lines.join("\n")}`, msgId);
19059
19510
 
19060
19511
  // src/server/qq/QqManager.ts
19061
19512
  init_esm_shims();
19062
- import * as fs25 from "fs";
19063
- import * as path32 from "path";
19513
+ import * as fs26 from "fs";
19514
+ import * as path33 from "path";
19064
19515
  var QqManager = class {
19065
19516
  constructor(opts) {
19066
19517
  this.opts = opts;
@@ -19069,7 +19520,7 @@ var QqManager = class {
19069
19520
  adapter = null;
19070
19521
  config = null;
19071
19522
  get configFile() {
19072
- return path32.join(this.opts.dataDir, "qq", "config.json");
19523
+ return path33.join(this.opts.dataDir, "qq", "config.json");
19073
19524
  }
19074
19525
  /** 启动:读配置(env 优先,其次文件),有则建连接 */
19075
19526
  start() {
@@ -19107,7 +19558,7 @@ var QqManager = class {
19107
19558
  /** 清空配置:删除文件 + 断开 */
19108
19559
  clearConfig() {
19109
19560
  try {
19110
- fs25.rmSync(this.configFile, { force: true });
19561
+ fs26.rmSync(this.configFile, { force: true });
19111
19562
  } catch {
19112
19563
  }
19113
19564
  this.stop();
@@ -19149,7 +19600,7 @@ var QqManager = class {
19149
19600
  }
19150
19601
  _readConfigFile() {
19151
19602
  try {
19152
- const raw = JSON.parse(fs25.readFileSync(this.configFile, "utf-8"));
19603
+ const raw = JSON.parse(fs26.readFileSync(this.configFile, "utf-8"));
19153
19604
  if (raw.appId && raw.appSecret) {
19154
19605
  return {
19155
19606
  appId: raw.appId,
@@ -19162,8 +19613,8 @@ var QqManager = class {
19162
19613
  return null;
19163
19614
  }
19164
19615
  _persistConfig(cfg) {
19165
- fs25.mkdirSync(path32.dirname(this.configFile), { recursive: true });
19166
- fs25.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
19616
+ fs26.mkdirSync(path33.dirname(this.configFile), { recursive: true });
19617
+ fs26.writeFileSync(this.configFile, JSON.stringify(cfg, null, 2), "utf-8");
19167
19618
  }
19168
19619
  };
19169
19620
 
@@ -19217,11 +19668,11 @@ import * as path37 from "path";
19217
19668
  // src/update/downloader.ts
19218
19669
  init_esm_shims();
19219
19670
  import { createHash as createHash3 } from "crypto";
19220
- import * as fs26 from "fs";
19671
+ import * as fs27 from "fs";
19221
19672
  import { Readable as Readable2 } from "stream";
19222
19673
  import { pipeline as pipeline2 } from "stream/promises";
19223
- import { spawnSync as spawnSync3 } from "child_process";
19224
- import * as path33 from "path";
19674
+ import { spawnSync as spawnSync4 } from "child_process";
19675
+ import * as path34 from "path";
19225
19676
 
19226
19677
  // src/update/manifest.ts
19227
19678
  init_esm_shims();
@@ -19277,8 +19728,10 @@ var PLATFORM_NAMES = {
19277
19728
  linux: "linux",
19278
19729
  win32: "windows"
19279
19730
  };
19731
+ var LIBC_VARIANT = "";
19280
19732
  function platformKey(platform = process.platform, arch = process.arch) {
19281
- return `${PLATFORM_NAMES[platform] ?? platform}-${arch}`;
19733
+ const base = `${PLATFORM_NAMES[platform] ?? platform}-${arch}`;
19734
+ return LIBC_VARIANT ? `${base}-${LIBC_VARIANT}` : base;
19282
19735
  }
19283
19736
  var SHA256_HEX = /^[0-9a-f]{64}$/;
19284
19737
  function isRecord(v) {
@@ -19396,8 +19849,8 @@ async function fetchManifest(url, publicKey, opts = {}) {
19396
19849
  async function downloadBinary(bin, destPath, log = () => {
19397
19850
  }) {
19398
19851
  const partPath = `${destPath}.part`;
19399
- fs26.mkdirSync(path33.dirname(destPath), { recursive: true });
19400
- fs26.rmSync(partPath, { force: true });
19852
+ fs27.mkdirSync(path34.dirname(destPath), { recursive: true });
19853
+ fs27.rmSync(partPath, { force: true });
19401
19854
  let res;
19402
19855
  try {
19403
19856
  res = await fetch(bin.url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS2) });
@@ -19422,30 +19875,30 @@ async function downloadBinary(bin, destPath, log = () => {
19422
19875
  yield chunk;
19423
19876
  }
19424
19877
  },
19425
- fs26.createWriteStream(partPath, { mode: 493 })
19878
+ fs27.createWriteStream(partPath, { mode: 493 })
19426
19879
  );
19427
19880
  } catch (err) {
19428
- fs26.rmSync(partPath, { force: true });
19881
+ fs27.rmSync(partPath, { force: true });
19429
19882
  return { error: `\u4E0B\u8F7D\u4E2D\u65AD\uFF1A${err.message}` };
19430
19883
  }
19431
19884
  const actual = hash.digest("hex");
19432
19885
  if (actual !== bin.sha256) {
19433
- fs26.rmSync(partPath, { force: true });
19886
+ fs27.rmSync(partPath, { force: true });
19434
19887
  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` };
19435
19888
  }
19436
19889
  if (expected && bytes !== expected) {
19437
- fs26.rmSync(partPath, { force: true });
19890
+ fs27.rmSync(partPath, { force: true });
19438
19891
  return { error: `\u4EA7\u7269\u5927\u5C0F\u4E0D\u5339\u914D\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${bytes}\uFF09` };
19439
19892
  }
19440
- fs26.rmSync(destPath, { force: true });
19441
- fs26.renameSync(partPath, destPath);
19442
- fs26.chmodSync(destPath, 493);
19443
- log(`\u5DF2\u4E0B\u8F7D ${path33.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
19893
+ fs27.rmSync(destPath, { force: true });
19894
+ fs27.renameSync(partPath, destPath);
19895
+ fs27.chmodSync(destPath, 493);
19896
+ log(`\u5DF2\u4E0B\u8F7D ${path34.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
19444
19897
  return { bytes };
19445
19898
  }
19446
19899
  function selfCheck(binPath, expectedVersion) {
19447
19900
  try {
19448
- const r = spawnSync3(binPath, ["--version"], {
19901
+ const r = spawnSync4(binPath, ["--version"], {
19449
19902
  timeout: 2e4,
19450
19903
  encoding: "utf-8",
19451
19904
  env: { ...process.env, ZQAGENT_AUTO_UPDATE: "0" }
@@ -19462,104 +19915,6 @@ function selfCheck(binPath, expectedVersion) {
19462
19915
  }
19463
19916
  }
19464
19917
 
19465
- // src/update/platform.ts
19466
- init_esm_shims();
19467
- import * as fs27 from "fs";
19468
- import * as path34 from "path";
19469
- import { spawnSync as spawnSync4 } from "child_process";
19470
- import { createRequire as createRequire3 } from "module";
19471
- function currentVersion() {
19472
- const injected = "0.2.8"?.trim();
19473
- if (injected) return injected;
19474
- try {
19475
- const req2 = createRequire3(import.meta.url);
19476
- const pkg = req2("../../package.json");
19477
- if (pkg?.version) return pkg.version;
19478
- } catch {
19479
- }
19480
- return "dev";
19481
- }
19482
- function isSeaRuntime() {
19483
- if (process.env.ZQAGENT_SEA === "1") return true;
19484
- const base = path34.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
19485
- return base !== "node" && base !== "nodejs";
19486
- }
19487
- var DEFAULT_SERVICE_NAME = "zqagent";
19488
- function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
19489
- const unitName = `${serviceName}.service`;
19490
- for (const line of cgroup.split("\n")) {
19491
- const idx = line.lastIndexOf(":");
19492
- if (idx < 0) continue;
19493
- const unitPath = line.slice(idx + 1).trim();
19494
- if (unitPath.split("/").includes(unitName)) return "systemd";
19495
- }
19496
- return null;
19497
- }
19498
- function detectServiceManager() {
19499
- if (process.platform !== "linux") return null;
19500
- const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
19501
- try {
19502
- return serviceManagerFromCgroup(
19503
- fs27.readFileSync("/proc/self/cgroup", "utf8"),
19504
- serviceName
19505
- );
19506
- } catch {
19507
- return null;
19508
- }
19509
- }
19510
- function serviceManager() {
19511
- const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
19512
- if (v === "systemd" || v === "launchd") return v;
19513
- return detectServiceManager();
19514
- }
19515
- function parseServiceEnvironment(raw) {
19516
- const out = {};
19517
- for (const token of raw.trim().split(/\s+/)) {
19518
- const idx = token.indexOf("=");
19519
- if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
19520
- }
19521
- return out;
19522
- }
19523
- var cachedServiceEnv = null;
19524
- function readServiceEnvironment() {
19525
- const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
19526
- try {
19527
- const res = spawnSync4(
19528
- "systemctl",
19529
- ["show", serviceName, "--property=Environment", "--value", "--no-pager"],
19530
- { encoding: "utf8", timeout: 3e3 }
19531
- );
19532
- if (res.status !== 0 || !res.stdout) return {};
19533
- return parseServiceEnvironment(res.stdout);
19534
- } catch {
19535
- return {};
19536
- }
19537
- }
19538
- function serviceEnvValue(key) {
19539
- if (process.platform !== "linux") return void 0;
19540
- if (serviceManager() !== "systemd") return void 0;
19541
- cachedServiceEnv ??= readServiceEnvironment();
19542
- return cachedServiceEnv[key];
19543
- }
19544
- function selfUpdateCapability(hasPublicKey) {
19545
- if (!isSeaRuntime()) {
19546
- return {
19547
- ok: false,
19548
- 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"
19549
- };
19550
- }
19551
- if (!serviceManager()) {
19552
- return {
19553
- ok: false,
19554
- reason: "\u672A\u88AB systemd/launchd \u6258\u7BA1\uFF1A\u8BF7\u7528 deploy/service/install.sh \u5B89\u88C5\uFF0C\u6216\u624B\u52A8\u5347\u7EA7"
19555
- };
19556
- }
19557
- if (!hasPublicKey) {
19558
- return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
19559
- }
19560
- return { ok: true };
19561
- }
19562
-
19563
19918
  // src/update/installer.ts
19564
19919
  init_esm_shims();
19565
19920
  import * as fs29 from "fs";
@@ -19925,11 +20280,11 @@ var UpdateController = class {
19925
20280
  const minUptime = num("ZQAGENT_UPDATE_MIN_UPTIME_MS", 10 * 60 * 1e3);
19926
20281
  const idleMs = num("ZQAGENT_UPDATE_IDLE_MS", 60 * 1e3);
19927
20282
  let idleSince = null;
19928
- return new Promise((resolve23) => {
20283
+ return new Promise((resolve24) => {
19929
20284
  const timer = setInterval(() => {
19930
20285
  if (this.stopped) {
19931
20286
  clearInterval(timer);
19932
- resolve23(false);
20287
+ resolve24(false);
19933
20288
  return;
19934
20289
  }
19935
20290
  if (Date.now() - this.startedAt < minUptime || !this.opts.isIdle()) {
@@ -19939,7 +20294,7 @@ var UpdateController = class {
19939
20294
  idleSince ??= Date.now();
19940
20295
  if (Date.now() - idleSince >= idleMs) {
19941
20296
  clearInterval(timer);
19942
- resolve23(true);
20297
+ resolve24(true);
19943
20298
  }
19944
20299
  }, 1e3);
19945
20300
  timer.unref();
@@ -20120,6 +20475,46 @@ function exitWhenOrphaned() {
20120
20475
  }, 1500);
20121
20476
  timer.unref();
20122
20477
  }
20478
+ var PORT_FALLBACK_LIMIT = 20;
20479
+ function listenWithPortFallback(server, startPort, host, dataDir, limit = PORT_FALLBACK_LIMIT) {
20480
+ return new Promise((resolve24, reject) => {
20481
+ let attempts = 0;
20482
+ const tryListen = (port) => {
20483
+ server.removeAllListeners("listening");
20484
+ const onError = (err) => {
20485
+ if (err.code === "EADDRINUSE" && attempts < limit) {
20486
+ attempts += 1;
20487
+ const next = port + 1;
20488
+ console.warn(`[server] \u7AEF\u53E3 ${port} \u5DF2\u88AB\u5360\u7528\uFF08EADDRINUSE\uFF09\uFF0C\u5C1D\u8BD5 ${next} \u2026`);
20489
+ tryListen(next);
20490
+ return;
20491
+ }
20492
+ server.removeListener("error", onError);
20493
+ console.error(
20494
+ `[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` : "")
20495
+ );
20496
+ reject(err);
20497
+ };
20498
+ server.once("error", onError);
20499
+ server.listen(port, host, () => {
20500
+ server.removeListener("error", onError);
20501
+ const addr = server.address();
20502
+ const actualPort = typeof addr === "object" && addr !== null ? addr.port : port;
20503
+ if (actualPort !== startPort) {
20504
+ console.warn(`[server] \u5DF2\u6539\u7528\u7AEF\u53E3 ${actualPort}\uFF08\u539F\u5B9A ${startPort} \u88AB\u5360\u7528\uFF09`);
20505
+ }
20506
+ try {
20507
+ fs31.mkdirSync(dataDir, { recursive: true });
20508
+ fs31.writeFileSync(path38.join(dataDir, "server-port"), `${actualPort}
20509
+ `);
20510
+ } catch {
20511
+ }
20512
+ resolve24(actualPort);
20513
+ });
20514
+ };
20515
+ tryListen(startPort);
20516
+ });
20517
+ }
20123
20518
  function startServer(opts = {}) {
20124
20519
  const dataDir = path38.resolve(opts.dataDir ?? env2("DATA_DIR") ?? DEFAULT_DATA_DIR);
20125
20520
  const port = opts.port ?? Number(env2("PORT") ?? 8787);
@@ -20345,11 +20740,11 @@ function startServer(opts = {}) {
20345
20740
  server.timeout = 0;
20346
20741
  server.keepAliveTimeout = 65e3;
20347
20742
  server.headersTimeout = 66e3;
20348
- const ready = new Promise((resolve23) => {
20349
- server.listen(port, host, () => {
20743
+ const ready = new Promise((resolve24, reject) => {
20744
+ listenWithPortFallback(server, port, host, dataDir).then((actualPort) => {
20350
20745
  const info = {
20351
20746
  host,
20352
- port,
20747
+ port: actualPort,
20353
20748
  dataDir,
20354
20749
  adminKey,
20355
20750
  fleetNodeId: fleet ? fleet.getNodeId() : void 0,
@@ -20362,8 +20757,8 @@ function startServer(opts = {}) {
20362
20757
  printReady(info, qq);
20363
20758
  }
20364
20759
  updater?.start();
20365
- resolve23(info);
20366
- });
20760
+ resolve24(info);
20761
+ }, reject);
20367
20762
  });
20368
20763
  return {
20369
20764
  store,
@@ -20499,7 +20894,7 @@ async function runWizard(rl2, store, opts) {
20499
20894
  }
20500
20895
  async function askHidden(rl2, prompt) {
20501
20896
  if (!process.stdin.isTTY) return (await rl2.question(prompt)).trim();
20502
- return new Promise((resolve23) => {
20897
+ return new Promise((resolve24) => {
20503
20898
  const saved = process.stdin.listeners("data");
20504
20899
  process.stdin.removeAllListeners("data");
20505
20900
  cliWrite(prompt);
@@ -20521,13 +20916,13 @@ async function askHidden(rl2, prompt) {
20521
20916
  if (ch === "\r" || ch === "\n") {
20522
20917
  cleanup();
20523
20918
  cliWrite("\n");
20524
- resolve23(buf);
20919
+ resolve24(buf);
20525
20920
  return;
20526
20921
  }
20527
20922
  if (ch === "") {
20528
20923
  cleanup();
20529
20924
  cliWrite("\n");
20530
- resolve23("");
20925
+ resolve24("");
20531
20926
  return;
20532
20927
  }
20533
20928
  if (ch === "\x7F" || ch === "\b") {
@@ -20716,7 +21111,7 @@ function updateSessionMetaFile(stateDir2, sessionId, patch) {
20716
21111
  // src/cli/debug.ts
20717
21112
  init_esm_shims();
20718
21113
  import * as fs33 from "fs";
20719
- import * as os8 from "os";
21114
+ import * as os10 from "os";
20720
21115
  import * as path42 from "path";
20721
21116
  var LOG_NAME = "zqagent-debug.log";
20722
21117
  var TRUTHY = /^(1|true|yes|on)$/i;
@@ -20730,7 +21125,7 @@ function resolveLogFile() {
20730
21125
  const explicit = process.env.ZQAGENT_DEBUG_LOG;
20731
21126
  if (explicit) return path42.resolve(explicit);
20732
21127
  const dir = process.env.ZQAGENT_DEBUG_DIR;
20733
- return path42.join(dir ? path42.resolve(dir) : os8.tmpdir(), LOG_NAME);
21128
+ return path42.join(dir ? path42.resolve(dir) : os10.tmpdir(), LOG_NAME);
20734
21129
  }
20735
21130
  function enableDebug(argv = process.argv) {
20736
21131
  if (enabled) return true;
@@ -21168,27 +21563,14 @@ var _exitHandler = null;
21168
21563
  rl.on("SIGINT", () => {
21169
21564
  _exitHandler?.();
21170
21565
  });
21171
- function patchReadlineHistoryNav() {
21172
- const iface = rl;
21173
- const orig = iface._ttyWrite;
21174
- iface._ttyWrite = function(s, key) {
21175
- const k = key;
21176
- if (k && (k.name === "up" || k.name === "down") && !k.ctrl && !k.meta && !k.shift) {
21177
- if (iface.cursor > 0) {
21178
- iface._moveCursor(-iface.cursor);
21179
- }
21180
- }
21181
- orig.call(rl, s, key);
21182
- };
21183
- }
21184
- patchReadlineHistoryNav();
21566
+ patchReadlineHistoryNav(rl);
21185
21567
  dlog("init", "\u6A21\u5757\u521D\u59CB\u5316\u5B8C\u6210\uFF08readline / \u8F93\u5165\u8DEF\u7531 / \u5386\u53F2\u5BFC\u822A\u5C31\u7EEA\uFF09");
21186
21568
  function question(prompt) {
21187
21569
  return inputRouter.ask(prompt);
21188
21570
  }
21189
21571
  function readPassword(prompt) {
21190
21572
  if (!process.stdin.isTTY) return question(prompt);
21191
- return new Promise((resolve23) => {
21573
+ return new Promise((resolve24) => {
21192
21574
  const savedDataListeners = process.stdin.listeners("data");
21193
21575
  process.stdin.removeAllListeners("data");
21194
21576
  cliWrite(prompt);
@@ -21204,13 +21586,13 @@ function readPassword(prompt) {
21204
21586
  if (ch === "\r" || ch === "\n") {
21205
21587
  cleanup();
21206
21588
  cliWrite("\n");
21207
- resolve23(buf);
21589
+ resolve24(buf);
21208
21590
  return;
21209
21591
  }
21210
21592
  if (ch === "") {
21211
21593
  cleanup();
21212
21594
  cliWrite("\n");
21213
- resolve23("");
21595
+ resolve24("");
21214
21596
  return;
21215
21597
  }
21216
21598
  if (ch === "\x7F" || ch === "\b") {
@@ -21645,7 +22027,7 @@ async function offerTerminalSetup(engine, canUseAgent) {
21645
22027
  }
21646
22028
  var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set(["link", "serve", "update"]);
21647
22029
  function cliVersion() {
21648
- const env3 = "0.2.8"?.trim();
22030
+ const env3 = "0.2.11"?.trim();
21649
22031
  if (env3) return env3;
21650
22032
  try {
21651
22033
  const req2 = createRequire4(import.meta.url);