jinzd-ai-cli 0.4.150 → 0.4.153

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.
Files changed (48) hide show
  1. package/dist/{auth-7KK5BOCA.js → auth-OI4YRVRG.js} +1 -1
  2. package/dist/{batch-KGBSTQU3.js → batch-DBRN4MCC.js} +2 -2
  3. package/dist/{chat-index-MY3DJTV3.js → chat-index-BE4TPLFH.js} +1 -1
  4. package/dist/{chat-index-JBF4ZIQI.js → chat-index-LUQWWLKO.js} +1 -1
  5. package/dist/{chunk-ONOVJIL2.js → chunk-AIZOARZY.js} +1 -1
  6. package/dist/{chunk-QXRDETAI.js → chunk-D62BVFP7.js} +1 -1
  7. package/dist/{chunk-PASCDYMH.js → chunk-D6U75FHP.js} +14 -10
  8. package/dist/{chunk-D5KUERWQ.js → chunk-EIIMBVXN.js} +3 -3
  9. package/dist/{chunk-V6L44Y3F.js → chunk-EYJQJZJ6.js} +1 -1
  10. package/dist/{chunk-O7NM4WTS.js → chunk-GXB7YKF2.js} +3 -1
  11. package/dist/{chunk-UQQJWHRV.js → chunk-HDSKW7Q3.js} +1 -1
  12. package/dist/{chunk-TUGKYLIV.js → chunk-LJPB4ZER.js} +1 -1
  13. package/dist/{chunk-YJ2CUK5O.js → chunk-M4GJOBWN.js} +2 -1
  14. package/dist/{chunk-OWPFDHKC.js → chunk-NXXNLLSG.js} +1 -0
  15. package/dist/{chunk-PCDBAZJK.js → chunk-OP3I24WL.js} +99 -24
  16. package/dist/{chunk-XYM3PCVR.js → chunk-OT2HLGSO.js} +1 -1
  17. package/dist/{chunk-7ZJN4KLV.js → chunk-RXM76HB7.js} +1 -2
  18. package/dist/{chunk-WJ32NAW3.js → chunk-T5VKNPLD.js} +1 -2
  19. package/dist/{chunk-G6LBSAVY.js → chunk-TURORFH2.js} +1 -1
  20. package/dist/{chunk-RCDS54ZC.js → chunk-UWW3EWER.js} +1 -1
  21. package/dist/{chunk-NKR53CPL.js → chunk-YKVFZLSI.js} +1 -1
  22. package/dist/{chunk-2DXY7UGF.js → chunk-ZWVIDFGY.js} +14 -10
  23. package/dist/{ci-XHNSTENI.js → ci-UEEUSELV.js} +4 -4
  24. package/dist/{constants-U2QQSUYK.js → constants-43EVHE2E.js} +1 -1
  25. package/dist/{doctor-cli-5LFGU2TS.js → doctor-cli-ZT674MCQ.js} +6 -6
  26. package/dist/electron-server.js +121 -39
  27. package/dist/{hub-OGEDS4X7.js → hub-MDQNJOMV.js} +61 -3
  28. package/dist/{hub-server-AUMVPNU6.js → hub-server-VPXCBWLA.js} +1 -1
  29. package/dist/index.js +28 -26
  30. package/dist/{run-tests-YSHH37SS.js → run-tests-DCT5LWBB.js} +1 -1
  31. package/dist/{run-tests-Q7COBDVK.js → run-tests-EYZ2JZ4X.js} +2 -2
  32. package/dist/{semantic-V37U4MCW.js → semantic-FKOEXY75.js} +2 -2
  33. package/dist/{semantic-IJKF5ZZC.js → semantic-GJJWTI3A.js} +2 -2
  34. package/dist/{server-J2GQRZP3.js → server-MQWFO2GJ.js} +8 -8
  35. package/dist/{server-5HDA5MNI.js → server-OIYBFKS2.js} +25 -21
  36. package/dist/{task-orchestrator-XBPAOLYD.js → task-orchestrator-BGQBNKAI.js} +8 -8
  37. package/dist/{vector-store-AK6J3RIA.js → vector-store-PLDSXF3V.js} +1 -1
  38. package/dist/{vector-store-MCQ77OOJ.js → vector-store-Z5OF4WWJ.js} +1 -1
  39. package/dist/web/client/app.js +2 -1
  40. package/dist/web/client/index.html +10 -6
  41. package/dist/web/client/sw.js +15 -26
  42. package/dist/web/client/vendor/daisyui-full.min.css +20 -0
  43. package/dist/web/client/vendor/github-dark.min.css +10 -0
  44. package/dist/web/client/vendor/github.min.css +10 -0
  45. package/dist/web/client/vendor/highlight.min.js +1213 -0
  46. package/dist/web/client/vendor/marked.min.js +69 -0
  47. package/dist/web/client/vendor/tailwind.js +83 -0
  48. package/package.json +172 -172
@@ -36,20 +36,20 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-TUGKYLIV.js";
39
+ } from "./chunk-LJPB4ZER.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
43
- } from "./chunk-NKR53CPL.js";
43
+ } from "./chunk-YKVFZLSI.js";
44
44
  import {
45
45
  loadIndex
46
46
  } from "./chunk-DQ2OHJNF.js";
47
- import "./chunk-PASCDYMH.js";
47
+ import "./chunk-D6U75FHP.js";
48
48
  import {
49
49
  loadChatIndex,
50
50
  redactJson,
51
51
  searchChatMemory
52
- } from "./chunk-WJ32NAW3.js";
52
+ } from "./chunk-T5VKNPLD.js";
53
53
  import "./chunk-JV5N65KN.js";
54
54
  import "./chunk-3RG5ZIWI.js";
55
55
 
@@ -638,6 +638,7 @@ function getDangerLevel(toolName, args) {
638
638
  if (/\btee\b|\bcp\b|\bmv\b|\bln\s+-s/.test(cmd)) return "write";
639
639
  if (/\bchmod\b|\bchown\b/.test(cmd)) return "write";
640
640
  if (/\bSet-Content\b|\bOut-File\b|\bAdd-Content\b|\bCopy-Item\b|\bMove-Item\b|\bSet-ItemProperty\b|\bNew-ItemProperty\b/i.test(cmd)) return "write";
641
+ if (/\b(python3?|node|deno|bun|perl|ruby|php)\b[^\n]*?\s-(?:c|e|p|r)\b/i.test(cmd)) return "write";
641
642
  return "safe";
642
643
  }
643
644
  if (toolName === "write_file") return "write";
@@ -710,10 +711,10 @@ var ClaudeProvider = class extends BaseProvider {
710
711
  };
711
712
  const proxyUrl = options?.proxy;
712
713
  try {
713
- const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
714
+ const { Agent: Agent2, ProxyAgent, fetch: undiciFetch } = await import("undici");
714
715
  const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
715
716
  const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
716
- const dispatcher = proxyUrl ? new ProxyAgent({ uri: proxyUrl, bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT }) : new Agent({ bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT });
717
+ const dispatcher = proxyUrl ? new ProxyAgent({ uri: proxyUrl, bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT }) : new Agent2({ bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT });
717
718
  clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
718
719
  } catch {
719
720
  }
@@ -1575,14 +1576,14 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1575
1576
  };
1576
1577
  const proxyUrl = options?.proxy;
1577
1578
  try {
1578
- const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
1579
+ const { Agent: Agent2, ProxyAgent, fetch: undiciFetch } = await import("undici");
1579
1580
  const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
1580
1581
  const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
1581
1582
  const dispatcher = proxyUrl ? new ProxyAgent({
1582
1583
  uri: proxyUrl,
1583
1584
  bodyTimeout: STREAM_BODY_TIMEOUT,
1584
1585
  headersTimeout: STREAM_HEADERS_TIMEOUT
1585
- }) : new Agent({
1586
+ }) : new Agent2({
1586
1587
  bodyTimeout: STREAM_BODY_TIMEOUT,
1587
1588
  headersTimeout: STREAM_HEADERS_TIMEOUT
1588
1589
  });
@@ -4987,7 +4988,7 @@ function checkPermission(toolName, args, dangerLevel, rules, defaultAction = "co
4987
4988
  if (rule.when.pathPattern) {
4988
4989
  if (toolName === "bash" && rule.action === "auto-approve") {
4989
4990
  const cmd = String(args["command"] ?? "").trim();
4990
- if (/[;&|`$<>]/.test(cmd) || /\$\(/.test(cmd)) continue;
4991
+ if (/[;&|`$<>\n\r]/.test(cmd) || /\$\(/.test(cmd)) continue;
4991
4992
  if (!cmd.startsWith(rule.when.pathPattern)) continue;
4992
4993
  } else {
4993
4994
  const path3 = String(args["path"] ?? args["command"] ?? "");
@@ -7015,6 +7016,8 @@ ${stderr}`);
7015
7016
 
7016
7017
  // src/tools/builtin/web-fetch.ts
7017
7018
  import { promises as dnsPromises } from "dns";
7019
+ import net from "net";
7020
+ import { Agent } from "undici";
7018
7021
  function htmlToText(html) {
7019
7022
  let text = html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<noscript[\s\S]*?<\/noscript>/gi, "").replace(/<svg[\s\S]*?<\/svg>/gi, "");
7020
7023
  const HTML_REGEX_LIMIT = 2e5;
@@ -7058,34 +7061,100 @@ function extractDescription(html) {
7058
7061
  return m ? m[1].trim() : "";
7059
7062
  }
7060
7063
  var MAX_OUTPUT = 16e3;
7064
+ function parseIpPart(s) {
7065
+ if (/^0x[0-9a-f]+$/i.test(s)) return parseInt(s, 16);
7066
+ if (/^0[0-7]+$/.test(s)) return parseInt(s, 8);
7067
+ if (/^[1-9]\d*$/.test(s) || s === "0") return parseInt(s, 10);
7068
+ return null;
7069
+ }
7070
+ function normalizeIpv4(h) {
7071
+ const parts = h.split(".");
7072
+ if (parts.length === 1) {
7073
+ const n = parseIpPart(parts[0]);
7074
+ if (n === null || n < 0 || n > 4294967295) return null;
7075
+ return [n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255].join(".");
7076
+ }
7077
+ if (parts.length === 4) {
7078
+ const nums = parts.map(parseIpPart);
7079
+ if (nums.some((n) => n === null || n < 0 || n > 255)) return null;
7080
+ return nums.join(".");
7081
+ }
7082
+ return null;
7083
+ }
7084
+ function isPrivateIpv4(ip) {
7085
+ const m = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
7086
+ if (!m) return false;
7087
+ const [o1, o2] = [Number(m[1]), Number(m[2])];
7088
+ if (o1 === 127) return true;
7089
+ if (o1 === 10) return true;
7090
+ if (o1 === 172 && o2 >= 16 && o2 <= 31) return true;
7091
+ if (o1 === 192 && o2 === 168) return true;
7092
+ if (o1 === 169 && o2 === 254) return true;
7093
+ if (o1 === 0) return true;
7094
+ if (o1 >= 224) return true;
7095
+ return false;
7096
+ }
7097
+ function isPrivateIpv6(ip) {
7098
+ const h = ip.toLowerCase().replace(/%.*$/, "");
7099
+ if (h === "::1" || h === "::") return true;
7100
+ const first = h.split(":")[0] ?? "";
7101
+ if (/^f[cd]/.test(first)) return true;
7102
+ if (/^fe[89ab]/.test(first)) return true;
7103
+ const mapped = h.match(/::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);
7104
+ if (mapped) return isPrivateIpv4(mapped[1]);
7105
+ const mappedHex = h.match(/::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
7106
+ if (mappedHex) {
7107
+ const hi = parseInt(mappedHex[1], 16), lo = parseInt(mappedHex[2], 16);
7108
+ const v4 = [hi >> 8 & 255, hi & 255, lo >> 8 & 255, lo & 255].join(".");
7109
+ return isPrivateIpv4(v4);
7110
+ }
7111
+ return false;
7112
+ }
7113
+ function isPrivateAddr(ip) {
7114
+ if (net.isIPv4(ip)) return isPrivateIpv4(ip);
7115
+ if (net.isIPv6(ip)) return isPrivateIpv6(ip);
7116
+ return false;
7117
+ }
7061
7118
  function isPrivateHost(hostname) {
7062
7119
  const h = hostname.toLowerCase().replace(/^\[|\]$/g, "");
7063
- if (h === "localhost" || h === "0.0.0.0" || h === "::1") return true;
7064
- if (h.startsWith("fe80:")) return true;
7065
- const m = h.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
7066
- if (m) {
7067
- const [o1, o2] = [Number(m[1]), Number(m[2])];
7068
- if (o1 === 127) return true;
7069
- if (o1 === 10) return true;
7070
- if (o1 === 172 && o2 >= 16 && o2 <= 31) return true;
7071
- if (o1 === 192 && o2 === 168) return true;
7072
- if (o1 === 169 && o2 === 254) return true;
7073
- if (o1 === 0) return true;
7074
- }
7120
+ if (h === "localhost" || h === "") return true;
7121
+ const v4 = normalizeIpv4(h);
7122
+ if (v4) return isPrivateIpv4(v4);
7123
+ if (net.isIPv6(h)) return isPrivateIpv6(h);
7075
7124
  return false;
7076
7125
  }
7077
7126
  async function resolveAndCheck(hostname) {
7078
7127
  const h = hostname.replace(/^\[|\]$/g, "");
7079
- if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h) || h.includes(":")) return;
7128
+ if (net.isIP(h) !== 0 || normalizeIpv4(h)) return;
7080
7129
  try {
7081
- const { address } = await dnsPromises.lookup(h);
7082
- if (isPrivateHost(address)) {
7083
- throw new NetworkError(`Blocked: "${hostname}" resolves to private address ${address}. web_fetch is restricted to public URLs.`);
7130
+ const addrs = await dnsPromises.lookup(h, { all: true, verbatim: true });
7131
+ for (const { address } of addrs) {
7132
+ if (isPrivateAddr(address)) {
7133
+ throw new NetworkError(`Blocked: "${hostname}" resolves to private address ${address}. web_fetch is restricted to public URLs.`);
7134
+ }
7084
7135
  }
7085
7136
  } catch (e) {
7086
7137
  if (e.message.startsWith("Blocked:")) throw e;
7087
7138
  }
7088
7139
  }
7140
+ function ssrfSafeDispatcher() {
7141
+ return new Agent({
7142
+ connect: {
7143
+ lookup: (hostname, options, cb) => {
7144
+ dnsPromises.lookup(hostname, { all: true, verbatim: true }).then((addrs) => {
7145
+ for (const a of addrs) {
7146
+ if (isPrivateAddr(a.address)) {
7147
+ cb(new NetworkError(`Blocked: "${hostname}" resolves to private address ${a.address}.`), null);
7148
+ return;
7149
+ }
7150
+ }
7151
+ if (options && options.all) cb(null, addrs);
7152
+ else cb(null, addrs[0].address, addrs[0].family);
7153
+ }).catch((err) => cb(err, null));
7154
+ }
7155
+ }
7156
+ });
7157
+ }
7089
7158
  var webFetchTool = {
7090
7159
  definition: {
7091
7160
  name: "web_fetch",
@@ -7130,6 +7199,7 @@ var webFetchTool = {
7130
7199
  Accept: "text/html,application/xhtml+xml,text/plain,*/*",
7131
7200
  "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
7132
7201
  };
7202
+ const dispatcher = ssrfSafeDispatcher();
7133
7203
  try {
7134
7204
  let currentUrl = url;
7135
7205
  let resp = null;
@@ -7142,8 +7212,9 @@ var webFetchTool = {
7142
7212
  const r = await fetch(currentUrl, {
7143
7213
  signal: controller2.signal,
7144
7214
  headers: FETCH_HEADERS,
7145
- redirect: "manual"
7215
+ redirect: "manual",
7146
7216
  // 手动控制重定向
7217
+ dispatcher
7147
7218
  });
7148
7219
  if (r.status >= 300 && r.status < 400) {
7149
7220
  if (hop >= MAX_REDIRECTS) {
@@ -7174,7 +7245,12 @@ var webFetchTool = {
7174
7245
  if (err.name === "AbortError") {
7175
7246
  throw new NetworkError(`Request timed out after 20s: ${url}`, void 0, err);
7176
7247
  }
7248
+ const cause = err.cause;
7249
+ if (cause?.message?.startsWith("Blocked:")) throw new NetworkError(cause.message);
7177
7250
  throw err;
7251
+ } finally {
7252
+ void dispatcher.close().catch(() => {
7253
+ });
7178
7254
  }
7179
7255
  let text;
7180
7256
  if (contentType.includes("text/plain") || contentType.includes("application/json")) {
@@ -12403,7 +12479,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
12403
12479
  const root = process.cwd();
12404
12480
  const { loadIndex: loadIndex2, clearIndex } = await import("./store-A3TZM6PS.js");
12405
12481
  const { indexProject: indexProject2 } = await import("./indexer-ISSNIFQY.js");
12406
- const { loadVectorStore, clearVectorStore } = await import("./vector-store-AK6J3RIA.js");
12482
+ const { loadVectorStore, clearVectorStore } = await import("./vector-store-PLDSXF3V.js");
12407
12483
  if (sub === "status") {
12408
12484
  const idx = loadIndex2(root);
12409
12485
  const vec = loadVectorStore(root);
@@ -12452,7 +12528,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
12452
12528
  message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
12453
12529
  });
12454
12530
  try {
12455
- const { rebuildSemanticIndex } = await import("./semantic-IJKF5ZZC.js");
12531
+ const { rebuildSemanticIndex } = await import("./semantic-GJJWTI3A.js");
12456
12532
  const stats = await rebuildSemanticIndex(root);
12457
12533
  const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
12458
12534
  this.send({
@@ -12639,7 +12715,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
12639
12715
  case "test": {
12640
12716
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
12641
12717
  try {
12642
- const { executeTests } = await import("./run-tests-YSHH37SS.js");
12718
+ const { executeTests } = await import("./run-tests-DCT5LWBB.js");
12643
12719
  const argStr = args.join(" ").trim();
12644
12720
  let testArgs = {};
12645
12721
  if (argStr) {
@@ -13163,7 +13239,7 @@ Add .md files to create commands.` });
13163
13239
  return;
13164
13240
  }
13165
13241
  try {
13166
- const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-MY3DJTV3.js");
13242
+ const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-BE4TPLFH.js");
13167
13243
  const loaded = loadChatIndex2();
13168
13244
  if (!loaded || loaded.idx.chunks.length === 0) {
13169
13245
  this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
@@ -13199,7 +13275,7 @@ Add .md files to create commands.` });
13199
13275
  }
13200
13276
  async handleMemoryStatus() {
13201
13277
  try {
13202
- const { getChatIndexStatus } = await import("./chat-index-MY3DJTV3.js");
13278
+ const { getChatIndexStatus } = await import("./chat-index-BE4TPLFH.js");
13203
13279
  const s = getChatIndexStatus();
13204
13280
  this.send({
13205
13281
  type: "memory_status",
@@ -13224,7 +13300,7 @@ Add .md files to create commands.` });
13224
13300
  type: "info",
13225
13301
  message: full ? "\u{1F9E0} Rebuilding chat memory index (this may take a while on first run \u2014 ~117 MB embedder)." : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026"
13226
13302
  });
13227
- const { buildChatIndex } = await import("./chat-index-MY3DJTV3.js");
13303
+ const { buildChatIndex } = await import("./chat-index-BE4TPLFH.js");
13228
13304
  const stats = await buildChatIndex({
13229
13305
  full,
13230
13306
  onProgress: (p) => {
@@ -13751,7 +13827,9 @@ var AuthManager = class {
13751
13827
  if (parts.length !== 2) return null;
13752
13828
  const [payloadB64, signature] = parts;
13753
13829
  const expectedSig = this.sign(payloadB64);
13754
- if (signature !== expectedSig) return null;
13830
+ const sigBuf = Buffer.from(signature ?? "", "utf-8");
13831
+ const expBuf = Buffer.from(expectedSig, "utf-8");
13832
+ if (sigBuf.length !== expBuf.length || !timingSafeEqual(sigBuf, expBuf)) return null;
13755
13833
  const payload = JSON.parse(
13756
13834
  Buffer.from(payloadB64, "base64url").toString("utf-8")
13757
13835
  );
@@ -13950,8 +14028,9 @@ async function startWebServer(options = {}) {
13950
14028
  }
13951
14029
  console.log(` Providers: ${availableProviders.map((p) => p.info.id).join(", ")}`);
13952
14030
  let mcpManager = null;
13953
- const globalMcpServers = config.get("mcpServers") ?? {};
13954
- const projectMcpResolved = resolveProjectMcpPath();
14031
+ const mcpEnabled = config.get("mcpEnabled") !== false;
14032
+ const globalMcpServers = mcpEnabled ? config.get("mcpServers") ?? {} : {};
14033
+ const projectMcpResolved = mcpEnabled ? resolveProjectMcpPath() : null;
13955
14034
  let projectMcpServers = {};
13956
14035
  if (projectMcpResolved) {
13957
14036
  const { checkTrust } = await import("./project-trust-EBGHD7LE.js");
@@ -13964,7 +14043,10 @@ async function startWebServer(options = {}) {
13964
14043
  }
13965
14044
  }
13966
14045
  const mergedMcpServers = { ...globalMcpServers, ...projectMcpServers };
13967
- if (Object.keys(mergedMcpServers).length > 0) {
14046
+ if (!mcpEnabled) {
14047
+ console.log(" \u{1F50C} MCP: disabled (config.mcpEnabled=false)");
14048
+ }
14049
+ if (mcpEnabled && Object.keys(mergedMcpServers).length > 0) {
13968
14050
  mcpManager = new McpManager();
13969
14051
  await mcpManager.connectAll(mergedMcpServers);
13970
14052
  const mcpTools = mcpManager.getAllTools();
@@ -14448,9 +14530,9 @@ async function startWebServer(options = {}) {
14448
14530
  try {
14449
14531
  const nets = networkInterfaces();
14450
14532
  for (const name of Object.keys(nets)) {
14451
- for (const net of nets[name] ?? []) {
14452
- if (net.family === "IPv4" && !net.internal) {
14453
- console.log(` \u{1F4F1} LAN access: http://${net.address}:${actualPort}`);
14533
+ for (const net2 of nets[name] ?? []) {
14534
+ if (net2.family === "IPv4" && !net2.internal) {
14535
+ console.log(` \u{1F4F1} LAN access: http://${net2.address}:${actualPort}`);
14454
14536
  }
14455
14537
  }
14456
14538
  }
@@ -4,7 +4,7 @@ import {
4
4
  assignRoleColors,
5
5
  renderHubBanner,
6
6
  renderHubEvent
7
- } from "./chunk-YJ2CUK5O.js";
7
+ } from "./chunk-M4GJOBWN.js";
8
8
  import "./chunk-PDX44BCA.js";
9
9
 
10
10
  // src/hub/discuss.ts
@@ -272,6 +272,49 @@ function listPresets() {
272
272
  return PRESETS;
273
273
  }
274
274
 
275
+ // src/hub/resolve-providers.ts
276
+ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, available, mix) {
277
+ const warnings = [];
278
+ let pool = null;
279
+ if (mix !== void 0 && mix !== false) {
280
+ if (typeof mix === "string" && mix.trim().length > 0) {
281
+ const requested = mix.split(",").map((s) => s.trim()).filter(Boolean);
282
+ pool = [];
283
+ for (const id of requested) {
284
+ if (lookup.has(id)) pool.push(id);
285
+ else warnings.push(`--mix: provider "${id}" not configured \u2014 skipped.`);
286
+ }
287
+ } else {
288
+ pool = [...available];
289
+ }
290
+ if (pool.length === 0) {
291
+ warnings.push(`--mix: no usable providers \u2014 all roles fall back to "${defaultProvider}".`);
292
+ pool = [defaultProvider];
293
+ }
294
+ }
295
+ const providerDefaultModel = (pid) => pid === defaultProvider ? defaultModel : lookup.defaultModelFor(pid) ?? defaultModel;
296
+ const assignments = [];
297
+ const outRoles = roles.map((role, i) => {
298
+ const desired = pool ? pool[i % pool.length] : role.provider;
299
+ let providerId;
300
+ let fellBack = false;
301
+ if (desired && lookup.has(desired)) {
302
+ providerId = desired;
303
+ } else {
304
+ if (desired && !lookup.has(desired)) {
305
+ warnings.push(`role "${role.id}": provider "${desired}" not available \u2014 using "${defaultProvider}".`);
306
+ fellBack = true;
307
+ }
308
+ providerId = defaultProvider;
309
+ }
310
+ const explicitProvider = role.provider ?? defaultProvider;
311
+ const modelId = !pool && role.model && explicitProvider === providerId ? role.model : providerDefaultModel(providerId);
312
+ assignments.push({ roleId: role.id, roleName: role.name, providerId, modelId, fellBack });
313
+ return { ...role, provider: providerId, model: modelId };
314
+ });
315
+ return { roles: outRoles, assignments, warnings };
316
+ }
317
+
275
318
  // src/hub/index.ts
276
319
  import { readFileSync, existsSync } from "fs";
277
320
  import chalk from "chalk";
@@ -363,6 +406,21 @@ ${content}`);
363
406
  }
364
407
  context = parts.join("\n\n---\n\n");
365
408
  }
409
+ {
410
+ const resolution = resolveRoleProviders(
411
+ roles,
412
+ {
413
+ has: (id) => providers.has(id),
414
+ defaultModelFor: (id) => providers.has(id) ? providers.get(id).info.defaultModel : void 0
415
+ },
416
+ defaultProvider,
417
+ defaultModel,
418
+ providers.listAvailable().map((p) => p.info.id),
419
+ options.mix
420
+ );
421
+ roles = resolution.roles;
422
+ for (const w of resolution.warnings) console.error(chalk.yellow(` \u26A0 ${w}`));
423
+ }
366
424
  const mode = options.mode ?? "discuss";
367
425
  const config = {
368
426
  mode,
@@ -386,7 +444,7 @@ ${content}`);
386
444
  }
387
445
  }
388
446
  async function runTaskMode(config, providers, configManager, topic) {
389
- const { TaskOrchestrator } = await import("./task-orchestrator-XBPAOLYD.js");
447
+ const { TaskOrchestrator } = await import("./task-orchestrator-BGQBNKAI.js");
390
448
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
391
449
  let interrupted = false;
392
450
  const onSigint = () => {
@@ -422,7 +480,7 @@ async function runTaskMode(config, providers, configManager, topic) {
422
480
  }
423
481
  }
424
482
  async function runDistributedDiscussion(config, providers, topic, port) {
425
- const { HubServer } = await import("./hub-server-AUMVPNU6.js");
483
+ const { HubServer } = await import("./hub-server-VPXCBWLA.js");
426
484
  const hub = new HubServer(config, providers, port);
427
485
  let interrupted = false;
428
486
  const onSigint = () => {
@@ -11,7 +11,7 @@ import {
11
11
  renderRoundHeader,
12
12
  renderSummary,
13
13
  renderUserInterrupt
14
- } from "./chunk-YJ2CUK5O.js";
14
+ } from "./chunk-M4GJOBWN.js";
15
15
  import "./chunk-PDX44BCA.js";
16
16
 
17
17
  // src/hub/hub-server.ts
package/dist/index.js CHANGED
@@ -20,12 +20,12 @@ import {
20
20
  saveDevState,
21
21
  sessionHasMeaningfulContent,
22
22
  setupProxy
23
- } from "./chunk-D5KUERWQ.js";
23
+ } from "./chunk-EIIMBVXN.js";
24
24
  import {
25
25
  getConfigDirUsage,
26
26
  listRecentCrashes,
27
27
  writeCrashLog
28
- } from "./chunk-G6LBSAVY.js";
28
+ } from "./chunk-TURORFH2.js";
29
29
  import {
30
30
  CONTENT_ONLY_STREAM_REMINDER,
31
31
  HALLUCINATION_CORRECTION_MESSAGE,
@@ -43,10 +43,10 @@ import {
43
43
  looksLikeDocumentBody,
44
44
  stripPseudoToolCalls,
45
45
  stripToolCallReminder
46
- } from "./chunk-ONOVJIL2.js";
46
+ } from "./chunk-AIZOARZY.js";
47
47
  import {
48
48
  ConfigManager
49
- } from "./chunk-RCDS54ZC.js";
49
+ } from "./chunk-UWW3EWER.js";
50
50
  import {
51
51
  ToolExecutor,
52
52
  ToolRegistry,
@@ -65,17 +65,17 @@ import {
65
65
  spawnAgentContext,
66
66
  theme,
67
67
  undoStack
68
- } from "./chunk-PCDBAZJK.js";
69
- import "./chunk-UQQJWHRV.js";
70
- import "./chunk-2DXY7UGF.js";
71
- import "./chunk-XYM3PCVR.js";
68
+ } from "./chunk-OP3I24WL.js";
69
+ import "./chunk-HDSKW7Q3.js";
70
+ import "./chunk-ZWVIDFGY.js";
71
+ import "./chunk-OT2HLGSO.js";
72
72
  import {
73
73
  getStatsSnapshot,
74
74
  getTopFailingTools,
75
75
  getTopUsedTools,
76
76
  installFlushOnExit
77
- } from "./chunk-V6L44Y3F.js";
78
- import "./chunk-OWPFDHKC.js";
77
+ } from "./chunk-EYJQJZJ6.js";
78
+ import "./chunk-NXXNLLSG.js";
79
79
  import {
80
80
  AuthError,
81
81
  ProviderError,
@@ -102,7 +102,7 @@ import {
102
102
  SKILLS_DIR_NAME,
103
103
  VERSION,
104
104
  buildUserIdentityPrompt
105
- } from "./chunk-QXRDETAI.js";
105
+ } from "./chunk-D62BVFP7.js";
106
106
  import {
107
107
  formatGitContextForPrompt,
108
108
  getGitContext,
@@ -118,7 +118,7 @@ import {
118
118
  getChatIndexStatus,
119
119
  scanString,
120
120
  searchChatMemory
121
- } from "./chunk-7ZJN4KLV.js";
121
+ } from "./chunk-RXM76HB7.js";
122
122
  import "./chunk-KHYD3WXE.js";
123
123
  import "./chunk-VNNYHW6N.js";
124
124
  import "./chunk-OVWE4E46.js";
@@ -1769,7 +1769,7 @@ No tools match "${filter}".
1769
1769
  const { join: join6 } = await import("path");
1770
1770
  const { existsSync: existsSync6 } = await import("fs");
1771
1771
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1772
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-U2QQSUYK.js");
1772
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-43EVHE2E.js");
1773
1773
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1774
1774
  const cwd = process.cwd();
1775
1775
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2705,7 +2705,7 @@ ${hint}` : "")
2705
2705
  const root = process.cwd();
2706
2706
  const { loadIndex, clearIndex } = await import("./store-VMK543OQ.js");
2707
2707
  const { indexProject } = await import("./indexer-S6UMGQKA.js");
2708
- const { loadVectorStore, clearVectorStore } = await import("./vector-store-MCQ77OOJ.js");
2708
+ const { loadVectorStore, clearVectorStore } = await import("./vector-store-Z5OF4WWJ.js");
2709
2709
  if (sub === "status") {
2710
2710
  const idx = loadIndex(root);
2711
2711
  const vec = loadVectorStore(root);
@@ -2764,7 +2764,7 @@ ${hint}` : "")
2764
2764
  }
2765
2765
  console.log(theme.dim(` Building semantic index for ${idx.symbolCount} symbols\u2026`));
2766
2766
  console.log(theme.dim(" (First run downloads ~117 MB embedding model to ~/.aicli/models/)"));
2767
- const { rebuildSemanticIndex } = await import("./semantic-V37U4MCW.js");
2767
+ const { rebuildSemanticIndex } = await import("./semantic-FKOEXY75.js");
2768
2768
  try {
2769
2769
  const stats = await rebuildSemanticIndex(root, {
2770
2770
  onProgress: (done, total) => {
@@ -2830,7 +2830,7 @@ ${hint}` : "")
2830
2830
  usage: "/test [command|filter]",
2831
2831
  async execute(args, ctx) {
2832
2832
  try {
2833
- const { executeTests } = await import("./run-tests-Q7COBDVK.js");
2833
+ const { executeTests } = await import("./run-tests-EYZ2JZ4X.js");
2834
2834
  const argStr = args.join(" ").trim();
2835
2835
  let testArgs = {};
2836
2836
  if (argStr) {
@@ -5560,7 +5560,7 @@ Session '${this.resumeSessionId}' not found.
5560
5560
  })();
5561
5561
  void (async () => {
5562
5562
  try {
5563
- const { getChatIndexStatus: getChatIndexStatus2, buildChatIndex: buildChatIndex2 } = await import("./chat-index-JBF4ZIQI.js");
5563
+ const { getChatIndexStatus: getChatIndexStatus2, buildChatIndex: buildChatIndex2 } = await import("./chat-index-LUQWWLKO.js");
5564
5564
  const initial = getChatIndexStatus2();
5565
5565
  this.chatMemoryStatus = {
5566
5566
  exists: initial.exists,
@@ -7530,11 +7530,11 @@ program.command("web").description("Start Web UI server with browser-based chat
7530
7530
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7531
7531
  process.exit(1);
7532
7532
  }
7533
- const { startWebServer } = await import("./server-5HDA5MNI.js");
7533
+ const { startWebServer } = await import("./server-OIYBFKS2.js");
7534
7534
  await startWebServer({ port, host: options.host });
7535
7535
  });
7536
7536
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
7537
- const { AuthManager } = await import("./auth-7KK5BOCA.js");
7537
+ const { AuthManager } = await import("./auth-OI4YRVRG.js");
7538
7538
  const config = new ConfigManager();
7539
7539
  const auth = new AuthManager(config.getConfigDir());
7540
7540
  if (!action || action === "list") {
@@ -7697,12 +7697,12 @@ program.command("sessions").description("List recent conversation sessions").opt
7697
7697
  console.log(footer + "\n");
7698
7698
  });
7699
7699
  program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
7700
- const { runDoctorCli } = await import("./doctor-cli-5LFGU2TS.js");
7700
+ const { runDoctorCli } = await import("./doctor-cli-ZT674MCQ.js");
7701
7701
  await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
7702
7702
  });
7703
7703
  program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
7704
7704
  try {
7705
- const batch = await import("./batch-KGBSTQU3.js");
7705
+ const batch = await import("./batch-DBRN4MCC.js");
7706
7706
  switch (action) {
7707
7707
  case "submit":
7708
7708
  if (!arg) {
@@ -7745,7 +7745,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7745
7745
  }
7746
7746
  });
7747
7747
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
7748
- const { startMcpServer } = await import("./server-J2GQRZP3.js");
7748
+ const { startMcpServer } = await import("./server-MQWFO2GJ.js");
7749
7749
  await startMcpServer({
7750
7750
  allowDestructive: !!options.allowDestructive,
7751
7751
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7754,7 +7754,7 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
7754
7754
  });
7755
7755
  });
7756
7756
  program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number; diff fetched via `gh pr diff <num>`", (v) => parseInt(v, 10)).option("--base <ref>", "Base ref for `git diff <ref>...HEAD` (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (options) => {
7757
- const { runCi } = await import("./ci-XHNSTENI.js");
7757
+ const { runCi } = await import("./ci-UEEUSELV.js");
7758
7758
  const result = await runCi({
7759
7759
  pr: options.pr,
7760
7760
  base: options.base,
@@ -7827,6 +7827,7 @@ program.command("help").description("Show a comprehensive guide to all aicli fea
7827
7827
  `${B}${C} \u25A0 MULTI-AGENT HUB${R}`,
7828
7828
  ` ${Y}--preset <name>${R} Role preset (tech-review/brainstorm/code-review/debate)`,
7829
7829
  ` ${Y}--roles <file>${R} Custom roles JSON file`,
7830
+ ` ${Y}--mix [providers]${R} Mixed multi-model: spread providers across roles (e.g. claude,openai,deepseek)`,
7830
7831
  ` ${Y}-c, --context <file>${R} Inject context doc (repeatable: -c a.md -c b.md)`,
7831
7832
  ` ${Y}--task${R} Task mode: agents plan & write code with tools`,
7832
7833
  ` ${Y}--distributed${R} Distributed mode (WebSocket, multi-process)`,
@@ -7872,7 +7873,7 @@ program.command("help").description("Show a comprehensive guide to all aicli fea
7872
7873
  ` ${G}aicli --provider claude -m claude-sonnet-4-20250514${R} ${D}# specific model${R}`,
7873
7874
  ` ${G}aicli -p "explain this" < main.py${R} ${D}# headless + file${R}`,
7874
7875
  ` ${G}aicli web --host 0.0.0.0${R} ${D}# Web UI on LAN${R}`,
7875
- ` ${G}aicli hub --preset brainstorm "idea"${R} ${D}# multi-agent brainstorm${R}`,
7876
+ ` ${G}aicli hub --preset brainstorm --mix "idea"${R} ${D}# brainstorm across Claude/GPT/DeepSeek${R}`,
7876
7877
  ` ${G}aicli hub --task --roles team.json "build app"${R} ${D}# task mode: agents write code${R}`,
7877
7878
  ` ${G}aicli hub -c spec.md -c api.md "build it"${R} ${D}# hub + docs context${R}`,
7878
7879
  ` ${G}aicli batch submit prompts.jsonl${R} ${D}# Anthropic batch: 50% off, 24h window${R}`,
@@ -7884,7 +7885,7 @@ program.command("help").description("Show a comprehensive guide to all aicli fea
7884
7885
  ];
7885
7886
  console.log(lines.join("\n"));
7886
7887
  });
7887
- program.command("hub [topic]").description("Start multi-agent hub (discuss / brainstorm with multiple AI roles)").option("--preset <name>", "Use a built-in role preset (default: tech-review)").option("--roles <file>", "Load roles from a JSON file").option("--provider <name>", "Override default AI provider").option("-m, --model <name>", "Override default model").option("--max-rounds <n>", "Max discussion rounds (default: 10)").option("--presets", "List available role presets").option("--task", "Task mode: plan \u2192 approve \uFFFD\uFFFD execute with tools (agents write code)").option("--task-rounds <n>", "Max tool-call rounds per task (default: 30)").option("-c, --context <file>", "Inject context document (repeatable: -c a.md -c b.md)", (val, prev) => prev.concat(val), []).option("--distributed", "Start WebSocket server so remote aicli instances can join as agents").option("--port <n>", "WebSocket port for distributed mode (default: 9527)", "9527").action(async (topic, options) => {
7888
+ program.command("hub [topic]").description("Start multi-agent hub (discuss / brainstorm with multiple AI roles)").option("--preset <name>", "Use a built-in role preset (default: tech-review)").option("--roles <file>", "Load roles from a JSON file").option("--provider <name>", "Override default AI provider").option("-m, --model <name>", "Override default model").option("--mix [providers]", "Mixed multi-model: spread configured providers across roles (or --mix claude,openai,deepseek)").option("--max-rounds <n>", "Max discussion rounds (default: 10)").option("--presets", "List available role presets").option("--task", "Task mode: plan \u2192 approve \uFFFD\uFFFD execute with tools (agents write code)").option("--task-rounds <n>", "Max tool-call rounds per task (default: 30)").option("-c, --context <file>", "Inject context document (repeatable: -c a.md -c b.md)", (val, prev) => prev.concat(val), []).option("--distributed", "Start WebSocket server so remote aicli instances can join as agents").option("--port <n>", "WebSocket port for distributed mode (default: 9527)", "9527").action(async (topic, options) => {
7888
7889
  const config = new ConfigManager();
7889
7890
  const registry = new ProviderRegistry();
7890
7891
  await registry.initialize(
@@ -7895,7 +7896,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7895
7896
  }),
7896
7897
  config.get("customProviders")
7897
7898
  );
7898
- const { startHub } = await import("./hub-OGEDS4X7.js");
7899
+ const { startHub } = await import("./hub-MDQNJOMV.js");
7899
7900
  await startHub(
7900
7901
  {
7901
7902
  topic: topic ?? "",
@@ -7903,6 +7904,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7903
7904
  rolesFile: options.roles,
7904
7905
  provider: options.provider,
7905
7906
  model: options.model,
7907
+ mix: options.mix,
7906
7908
  mode: options.task === true ? "task" : void 0,
7907
7909
  maxRounds: options.maxRounds ? parseInt(options.maxRounds, 10) : void 0,
7908
7910
  listPresets: options.presets === true,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-TUGKYLIV.js";
4
+ } from "./chunk-LJPB4ZER.js";
5
5
  import "./chunk-3RG5ZIWI.js";
6
6
  export {
7
7
  executeTests,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-XYM3PCVR.js";
6
- import "./chunk-QXRDETAI.js";
5
+ } from "./chunk-OT2HLGSO.js";
6
+ import "./chunk-D62BVFP7.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
  export {
9
9
  executeTests,