jinzd-ai-cli 0.4.306 → 0.4.308

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 (95) hide show
  1. package/README.md +1 -0
  2. package/THIRD_PARTY_NOTICES.md +3 -0
  3. package/dist/{agent-client-6GX6QQDU.js → agent-client-TIEAYZXV.js} +4 -1
  4. package/dist/{auth-PE3Z5OHS.js → auth-QTQNZ6IX.js} +2 -2
  5. package/dist/{batch-JLGWQ7HU.js → batch-S7XLEEOH.js} +3 -3
  6. package/dist/{chat-index-2D5GK2DB.js → chat-index-7CLR4CBG.js} +4 -3
  7. package/dist/{chat-index-DGUOUUQW.js → chat-index-RLVLQRRC.js} +3 -2
  8. package/dist/{chunk-JDT6UG6R.js → chunk-2DYQDLLF.js} +1 -1
  9. package/dist/{chunk-T6WQEQI4.js → chunk-2MNUMLN4.js} +1 -1
  10. package/dist/chunk-2XMUCC42.js +67 -0
  11. package/dist/{chunk-7SSCSSC6.js → chunk-3537CQ5M.js} +2 -2
  12. package/dist/{chunk-OAXLMF3U.js → chunk-3YVNDYOY.js} +1 -1
  13. package/dist/{chunk-RYQBBEMM.js → chunk-4PR5AVJ5.js} +115 -19
  14. package/dist/{chunk-GHWZVPUV.js → chunk-6JHACXB3.js} +7 -5
  15. package/dist/{chunk-WZWRA7R2.js → chunk-7OHXFTZO.js} +2 -2
  16. package/dist/{chunk-IUVDZVZ4.js → chunk-AXTLYOA7.js} +24 -17
  17. package/dist/{chunk-XJOK5353.js → chunk-B3MPYI4E.js} +2 -2
  18. package/dist/{chunk-3RDJEYF7.js → chunk-BGZN4K7Q.js} +4 -4
  19. package/dist/chunk-BMLQPSHP.js +69 -0
  20. package/dist/{chunk-NRSYBKWA.js → chunk-C7CKDFJS.js} +1 -1
  21. package/dist/{chunk-JFNBJHR6.js → chunk-E2PNXNKI.js} +4 -4
  22. package/dist/chunk-FVXNEZEW.js +161 -0
  23. package/dist/{chunk-YKJO3SI4.js → chunk-G6DVGQ4I.js} +1 -1
  24. package/dist/{chunk-AYLK4EGA.js → chunk-HC33C4U3.js} +18 -18
  25. package/dist/chunk-JJFJWD35.js +162 -0
  26. package/dist/{chunk-W6PLV3EY.js → chunk-JJNUASMJ.js} +263 -55
  27. package/dist/{chunk-NYNSYLFY.js → chunk-JL44HGZC.js} +1 -1
  28. package/dist/{chunk-NUJTNH76.js → chunk-LBWKMTT3.js} +1 -1
  29. package/dist/{chunk-LAP7TPPK.js → chunk-LR3TGZWQ.js} +2 -2
  30. package/dist/{chunk-WQ3OMEC4.js → chunk-M6HLYG33.js} +1 -1
  31. package/dist/{chunk-I4GQRMFV.js → chunk-M6YV5KSS.js} +6 -4
  32. package/dist/{chunk-K5SDDTN7.js → chunk-NLK45GUS.js} +1 -1
  33. package/dist/{chunk-KBDKKHXB.js → chunk-O6CGOVD4.js} +2 -2
  34. package/dist/chunk-QMFMST2T.js +39 -0
  35. package/dist/{chunk-MI4SFZGX.js → chunk-RMEJR3AW.js} +5 -2
  36. package/dist/{chunk-UFW4S3RU.js → chunk-RVMCY6DP.js} +1 -1
  37. package/dist/{chunk-LF67HWWR.js → chunk-RWRZVQCB.js} +1 -1
  38. package/dist/chunk-SXIJLSVF.js +41 -0
  39. package/dist/chunk-TDQW2YHK.js +65 -0
  40. package/dist/{chunk-VLDB2SVN.js → chunk-TPTFV3Y6.js} +383 -513
  41. package/dist/{chunk-45UVJXFQ.js → chunk-W2UOVGRC.js} +1 -1
  42. package/dist/{chunk-VXEF2SWG.js → chunk-WT252ZDV.js} +2 -2
  43. package/dist/{chunk-NLB7JZSO.js → chunk-WVEIUSXE.js} +246 -55
  44. package/dist/{chunk-OC2XHG5L.js → chunk-YBZM7PHI.js} +4 -4
  45. package/dist/{ci-6PHEL3C7.js → ci-5HONSUAS.js} +7 -7
  46. package/dist/{ci-format-AZYUP6BL.js → ci-format-URUH3ZZ5.js} +2 -2
  47. package/dist/{constants-CPNWIUGZ.js → constants-BLDBI7IG.js} +1 -1
  48. package/dist/{constants-AXBD2EEB.js → constants-FG27DQMS.js} +1 -1
  49. package/dist/doctor-cli-QQLMNJN7.js +18 -0
  50. package/dist/electron-server.js +1003 -829
  51. package/dist/file-checkpoint-64A2AXHE.js +7 -0
  52. package/dist/{file-checkpoint-NKBHGC7L.js → file-checkpoint-VVPUJNCI.js} +2 -1
  53. package/dist/{hub-DDYAWLF2.js → hub-GGFW3DCC.js} +3 -3
  54. package/dist/{hub-server-CBKCP5VD.js → hub-server-JLC7CNP7.js} +55 -17
  55. package/dist/index.js +42 -40
  56. package/dist/{indexer-P3FPRTPJ.js → indexer-GKIWSSRP.js} +3 -3
  57. package/dist/indexer-VRXDZ74A.js +10 -0
  58. package/dist/{persist-S77WRVLD.js → persist-ZZXRWFBX.js} +4 -4
  59. package/dist/{persistent-memory-XZISXRWT.js → persistent-memory-UNLCXUWH.js} +3 -3
  60. package/dist/{persistent-memory-6XYYOMSC.js → persistent-memory-YYWIN6BG.js} +3 -3
  61. package/dist/{pr-UY4ZCZNI.js → pr-YCFGPFI7.js} +7 -7
  62. package/dist/{project-trust-MUG325AW.js → project-trust-6CJB5ZMX.js} +1 -1
  63. package/dist/{project-trust-NKYHL3VZ.js → project-trust-KGG2BWKC.js} +1 -1
  64. package/dist/run-tests-GHY4BYDE.js +13 -0
  65. package/dist/run-tests-KRNMFXAN.js +12 -0
  66. package/dist/{semantic-7GM5M4MU.js → semantic-N227CLSC.js} +4 -4
  67. package/dist/{semantic-QOBAXEQ4.js → semantic-TRQAZSRY.js} +4 -4
  68. package/dist/{server-6IC33CJK.js → server-73UBHSFV.js} +16 -14
  69. package/dist/{server-DBHLVQVP.js → server-QRAWSG4Z.js} +231 -103
  70. package/dist/{store-SIKHCUYJ.js → store-2N6PPPBP.js} +2 -2
  71. package/dist/{store-CMSSWZF7.js → store-76VVFDXM.js} +2 -2
  72. package/dist/{task-orchestrator-RDDETDW2.js → task-orchestrator-GI3RGLZI.js} +15 -14
  73. package/dist/{usage-D473ZVIP.js → usage-GWTIGZF5.js} +4 -4
  74. package/dist/{vector-store-JAXWDZYW.js → vector-store-T2DVUMY4.js} +2 -2
  75. package/dist/{vector-store-PWIOQFTP.js → vector-store-V6UGEYWK.js} +2 -2
  76. package/dist/web/client/app.js +7 -0
  77. package/dist/web/client/app.js.br +0 -0
  78. package/dist/web/client/app.js.gz +0 -0
  79. package/dist/web/client/util.js +14 -23
  80. package/dist/web/client/util.js.br +0 -0
  81. package/dist/web/client/util.js.gz +0 -0
  82. package/dist/web/client/vendor/DOMPurify-LICENSE.txt +202 -0
  83. package/dist/web/client/vendor/purify.es.mjs +2722 -0
  84. package/dist/web/client/vendor/purify.es.mjs.br +0 -0
  85. package/dist/web/client/vendor/purify.es.mjs.gz +0 -0
  86. package/package.json +4 -2
  87. package/dist/chunk-4BKXL7SM.js +0 -98
  88. package/dist/chunk-6BUTA5VW.js +0 -38
  89. package/dist/chunk-IW3Q7AE5.js +0 -40
  90. package/dist/chunk-SKET65WZ.js +0 -96
  91. package/dist/doctor-cli-DDHHDMMJ.js +0 -18
  92. package/dist/file-checkpoint-CGH6OJVI.js +0 -6
  93. package/dist/indexer-FPKY7LCT.js +0 -10
  94. package/dist/run-tests-BAMLVLO6.js +0 -10
  95. package/dist/run-tests-UEE3OMLS.js +0 -9
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ AuthRateLimiter,
4
+ WebAccess,
5
+ equalToken
6
+ } from "./chunk-TDQW2YHK.js";
2
7
  import {
3
8
  DiscussionOrchestrator,
4
9
  getPreset,
@@ -8,10 +13,10 @@ import "./chunk-C2Z77RTU.js";
8
13
  import {
9
14
  AuthManager,
10
15
  TOKEN_EXPIRY_MS
11
- } from "./chunk-RYQBBEMM.js";
16
+ } from "./chunk-4PR5AVJ5.js";
12
17
  import {
13
18
  persistDiscussion
14
- } from "./chunk-NUJTNH76.js";
19
+ } from "./chunk-LBWKMTT3.js";
15
20
  import {
16
21
  AUTO_COMPACT_INSTRUCTION,
17
22
  McpManager,
@@ -81,7 +86,7 @@ import {
81
86
  skillHistoryNote,
82
87
  snapshotFile,
83
88
  stripRoutingTags
84
- } from "./chunk-AYLK4EGA.js";
89
+ } from "./chunk-HC33C4U3.js";
85
90
  import {
86
91
  CNY_PER_USD,
87
92
  CostTracker,
@@ -90,7 +95,7 @@ import {
90
95
  getCostCurrency,
91
96
  getPricing,
92
97
  setCostCurrency
93
- } from "./chunk-YKJO3SI4.js";
98
+ } from "./chunk-G6DVGQ4I.js";
94
99
  import {
95
100
  DEFAULT_MAX_TOOL_OUTPUT_CHARS,
96
101
  ToolExecutor,
@@ -121,12 +126,11 @@ import {
121
126
  resolveAgentConfig,
122
127
  resolveWriteFileArgs,
123
128
  runSafePhases,
124
- runWithSessionKey,
125
129
  setPreferredAgentName,
126
130
  spawnAgentContext,
127
131
  truncateOutput,
128
132
  undoStack
129
- } from "./chunk-VLDB2SVN.js";
133
+ } from "./chunk-TPTFV3Y6.js";
130
134
  import {
131
135
  buildReviewPrompt,
132
136
  buildSecurityReviewPrompt
@@ -134,7 +138,7 @@ import {
134
138
  import {
135
139
  thinkingParamsFor
136
140
  } from "./chunk-6YZEZFX5.js";
137
- import "./chunk-GHWZVPUV.js";
141
+ import "./chunk-6JHACXB3.js";
138
142
  import {
139
143
  addMemoryEntry,
140
144
  backupMemoryStore,
@@ -153,18 +157,18 @@ import {
153
157
  restoreMemoryEntries,
154
158
  updateMemoryApproval,
155
159
  updateMemoryEntry
156
- } from "./chunk-LAP7TPPK.js";
157
- import "./chunk-LF67HWWR.js";
158
- import "./chunk-XJOK5353.js";
159
- import "./chunk-WQ3OMEC4.js";
160
- import "./chunk-NRSYBKWA.js";
160
+ } from "./chunk-LR3TGZWQ.js";
161
+ import "./chunk-RWRZVQCB.js";
162
+ import "./chunk-B3MPYI4E.js";
163
+ import "./chunk-M6HLYG33.js";
164
+ import "./chunk-C7CKDFJS.js";
161
165
  import "./chunk-KHYD3WXE.js";
162
166
  import "./chunk-5C76XDDF.js";
163
167
  import {
164
168
  SessionManager,
165
169
  getContentText
166
- } from "./chunk-IUVDZVZ4.js";
167
- import "./chunk-NYNSYLFY.js";
170
+ } from "./chunk-AXTLYOA7.js";
171
+ import "./chunk-JL44HGZC.js";
168
172
  import {
169
173
  scanString
170
174
  } from "./chunk-3FEZSLGI.js";
@@ -172,7 +176,7 @@ import {
172
176
  buildDoctorReport,
173
177
  formatDoctorReport,
174
178
  loadContextFiles
175
- } from "./chunk-JFNBJHR6.js";
179
+ } from "./chunk-E2PNXNKI.js";
176
180
  import {
177
181
  describePlugin,
178
182
  getActivePluginAssets,
@@ -187,14 +191,14 @@ import {
187
191
  runTool,
188
192
  setPluginEnabled,
189
193
  trustPlugin
190
- } from "./chunk-7SSCSSC6.js";
194
+ } from "./chunk-3537CQ5M.js";
191
195
  import "./chunk-RS7SKYTS.js";
192
196
  import {
193
197
  ProviderRegistry
194
- } from "./chunk-VXEF2SWG.js";
198
+ } from "./chunk-WT252ZDV.js";
195
199
  import {
196
200
  ConfigManager
197
- } from "./chunk-OC2XHG5L.js";
201
+ } from "./chunk-YBZM7PHI.js";
198
202
  import {
199
203
  CONTENT_ONLY_STREAM_REMINDER,
200
204
  TEE_FINAL_USER_NUDGE,
@@ -210,7 +214,7 @@ import {
210
214
  runAgentLoop,
211
215
  stripToolCallReminder,
212
216
  teeStreamErrorSummary
213
- } from "./chunk-MI4SFZGX.js";
217
+ } from "./chunk-RMEJR3AW.js";
214
218
  import "./chunk-TU3L3PW7.js";
215
219
  import {
216
220
  formatGitContextForPrompt,
@@ -219,13 +223,15 @@ import {
219
223
  } from "./chunk-HOSJZMQS.js";
220
224
  import {
221
225
  atomicWriteFileSync
222
- } from "./chunk-IW3Q7AE5.js";
226
+ } from "./chunk-BMLQPSHP.js";
223
227
  import {
224
228
  applyConfigCommand,
225
229
  maskConfigSecrets
226
230
  } from "./chunk-XNFIYS7G.js";
227
- import "./chunk-4BKXL7SM.js";
228
- import "./chunk-W6PLV3EY.js";
231
+ import {
232
+ assertReadablePath
233
+ } from "./chunk-JJFJWD35.js";
234
+ import "./chunk-JJNUASMJ.js";
229
235
  import {
230
236
  AGENTIC_BEHAVIOR_GUIDELINE,
231
237
  AUTHOR,
@@ -241,7 +247,11 @@ import {
241
247
  SKILLS_DIR_NAME,
242
248
  VERSION,
243
249
  buildUserIdentityPrompt
244
- } from "./chunk-T6WQEQI4.js";
250
+ } from "./chunk-2MNUMLN4.js";
251
+ import {
252
+ runWithExecutionContext,
253
+ runWithSessionKey
254
+ } from "./chunk-SXIJLSVF.js";
245
255
 
246
256
  // src/web/server.ts
247
257
  import express from "express";
@@ -968,7 +978,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
968
978
  Cost: ${formatCost(cost)}` : "";
969
979
  let memoryLine = "";
970
980
  try {
971
- const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-6XYYOMSC.js");
981
+ const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-YYWIN6BG.js");
972
982
  const pending = countPendingMemories2(ctx.config.getConfigDir());
973
983
  if (pending > 0) memoryLine = `
974
984
  Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
@@ -1591,7 +1601,7 @@ async function handleRewind(args, ctx) {
1591
1601
  ctx.send({ type: "error", message: intent.message });
1592
1602
  return;
1593
1603
  }
1594
- const { fileCheckpoints } = await import("./file-checkpoint-NKBHGC7L.js");
1604
+ const { fileCheckpoints } = await import("./file-checkpoint-VVPUJNCI.js");
1595
1605
  if (intent.kind === "list") {
1596
1606
  const rows = buildRewindList(session.messages, fileCheckpoints.getMessageIndices());
1597
1607
  ctx.send({
@@ -1897,7 +1907,7 @@ async function handleTest(args, ctx) {
1897
1907
  const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
1898
1908
  testArgs = isCommand ? { command: argStr } : { filter: argStr };
1899
1909
  }
1900
- const runTests = ctx.runTests ?? (await import("./run-tests-BAMLVLO6.js")).executeTests;
1910
+ const runTests = ctx.runTests ?? (await import("./run-tests-GHY4BYDE.js")).executeTests;
1901
1911
  const report = await runTests(testArgs);
1902
1912
  ctx.send({ type: "info", message: report });
1903
1913
  } catch (err) {
@@ -2074,9 +2084,9 @@ async function handleContext(_args, ctx) {
2074
2084
  async function handleIndex(args, ctx) {
2075
2085
  const sub = (args[0] ?? "status").toLowerCase();
2076
2086
  const root = process.cwd();
2077
- const { loadIndex, clearIndex } = await import("./store-CMSSWZF7.js");
2078
- const { indexProject } = await import("./indexer-P3FPRTPJ.js");
2079
- const { loadVectorStore, clearVectorStore } = await import("./vector-store-PWIOQFTP.js");
2087
+ const { loadIndex, clearIndex } = await import("./store-76VVFDXM.js");
2088
+ const { indexProject } = await import("./indexer-GKIWSSRP.js");
2089
+ const { loadVectorStore, clearVectorStore } = await import("./vector-store-V6UGEYWK.js");
2080
2090
  if (sub === "status") {
2081
2091
  const idx = loadIndex(root);
2082
2092
  const vec = loadVectorStore(root);
@@ -2126,7 +2136,7 @@ async function handleIndex(args, ctx) {
2126
2136
  message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
2127
2137
  });
2128
2138
  try {
2129
- const { rebuildSemanticIndex } = await import("./semantic-7GM5M4MU.js");
2139
+ const { rebuildSemanticIndex } = await import("./semantic-N227CLSC.js");
2130
2140
  const stats = await rebuildSemanticIndex(root);
2131
2141
  const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
2132
2142
  ctx.send({
@@ -2157,7 +2167,7 @@ async function handleMemory(args, ctx) {
2157
2167
  if (intent.kind === "rebuild") {
2158
2168
  ctx.send({ type: "info", message: rebuildStartMessage(intent.full) });
2159
2169
  try {
2160
- const { buildChatIndex } = await import("./chat-index-2D5GK2DB.js");
2170
+ const { buildChatIndex } = await import("./chat-index-7CLR4CBG.js");
2161
2171
  const stats = await buildChatIndex({ full: intent.full });
2162
2172
  ctx.send({ type: "info", message: rebuildDoneMessage(stats) });
2163
2173
  } catch (err) {
@@ -2166,14 +2176,14 @@ async function handleMemory(args, ctx) {
2166
2176
  return;
2167
2177
  }
2168
2178
  if (intent.kind === "index-clear") {
2169
- const { clearChatIndex } = await import("./chat-index-2D5GK2DB.js");
2179
+ const { clearChatIndex } = await import("./chat-index-7CLR4CBG.js");
2170
2180
  clearChatIndex();
2171
2181
  ctx.send({ type: "info", message: "Chat memory index cleared (persistent memory untouched)." });
2172
2182
  return;
2173
2183
  }
2174
2184
  if (intent.kind === "status") {
2175
- const { getChatIndexStatus } = await import("./chat-index-2D5GK2DB.js");
2176
- const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-6XYYOMSC.js");
2185
+ const { getChatIndexStatus } = await import("./chat-index-7CLR4CBG.js");
2186
+ const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-YYWIN6BG.js");
2177
2187
  const memories = listMemoryEntries2(configDir, { includeExpired: true, includeRejected: true });
2178
2188
  const injected = formatMemoryForPrompt2(configDir, process.cwd(), ctx.config.get("memory")?.maxChars);
2179
2189
  const view = buildMemoryStatusView({
@@ -2196,8 +2206,8 @@ async function handleMemory(args, ctx) {
2196
2206
  return;
2197
2207
  }
2198
2208
  if (intent.kind === "recall") {
2199
- const { searchPersistentMemories, touchMemoryReferences } = await import("./persistent-memory-6XYYOMSC.js");
2200
- const { searchChatMemory } = await import("./chat-index-2D5GK2DB.js");
2209
+ const { searchPersistentMemories, touchMemoryReferences } = await import("./persistent-memory-YYWIN6BG.js");
2210
+ const { searchChatMemory } = await import("./chat-index-7CLR4CBG.js");
2201
2211
  const persistentHits = searchPersistentMemories(configDir, intent.query, process.cwd(), 3);
2202
2212
  if (persistentHits.length > 0) {
2203
2213
  touchMemoryReferences(configDir, persistentHits.map((h) => h.entry.id));
@@ -2317,7 +2327,7 @@ async function handleMemory(args, ctx) {
2317
2327
  ctx.handleMemoryManage(intent.action, args[1], intent.action === "expire" ? args[2] : void 0);
2318
2328
  } else if (intent.kind === "export") {
2319
2329
  if (args[1] === "md") {
2320
- const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-6XYYOMSC.js");
2330
+ const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-YYWIN6BG.js");
2321
2331
  ctx.send({
2322
2332
  type: "export_data",
2323
2333
  format: "md",
@@ -2941,7 +2951,18 @@ var SessionHandler = class _SessionHandler {
2941
2951
  }))
2942
2952
  });
2943
2953
  }
2954
+ executionState = /* @__PURE__ */ new Map();
2955
+ connectionAbort = new AbortController();
2944
2956
  async handleMessage(raw) {
2957
+ if (this.connectionAbort.signal.aborted && /"type"\s*:\s*"(?:chat|command|hub_start)"/.test(raw)) this.connectionAbort = new AbortController();
2958
+ return runWithExecutionContext({
2959
+ configDir: this.config.getConfigDir(),
2960
+ sessionKey: this._bashSessionKey,
2961
+ state: this.executionState,
2962
+ signal: this.connectionAbort.signal
2963
+ }, () => this.handleMessageScoped(raw));
2964
+ }
2965
+ async handleMessageScoped(raw) {
2945
2966
  if (this.disconnected) return;
2946
2967
  let msg;
2947
2968
  try {
@@ -2952,6 +2973,7 @@ var SessionHandler = class _SessionHandler {
2952
2973
  }
2953
2974
  switch (msg.type) {
2954
2975
  case "chat": {
2976
+ if (this.connectionAbort.signal.aborted) this.connectionAbort = new AbortController();
2955
2977
  if (msg.messageId) {
2956
2978
  if (typeof msg.messageId !== "string" || msg.messageId.length > 200) throw new Error("Invalid message ID");
2957
2979
  const scope = this.config.getConfigDir();
@@ -3012,6 +3034,7 @@ var SessionHandler = class _SessionHandler {
3012
3034
  return;
3013
3035
  }
3014
3036
  case "abort":
3037
+ this.connectionAbort.abort();
3015
3038
  if (this.abortController) {
3016
3039
  this.abortController.abort();
3017
3040
  }
@@ -3035,8 +3058,16 @@ var SessionHandler = class _SessionHandler {
3035
3058
  }
3036
3059
  }
3037
3060
  onDisconnect(closeSocket = true) {
3061
+ runWithExecutionContext({
3062
+ configDir: this.config.getConfigDir(),
3063
+ sessionKey: this._bashSessionKey,
3064
+ state: this.executionState
3065
+ }, () => this.disconnectScoped(closeSocket));
3066
+ }
3067
+ disconnectScoped(closeSocket) {
3038
3068
  if (this.disconnected) return;
3039
3069
  this.disconnected = true;
3070
+ this.connectionAbort.abort();
3040
3071
  if (closeSocket && this.ws.readyState === this.ws.OPEN) this.ws.close(1001, "Connection closed; reconnect to restore session");
3041
3072
  runLifecycleHooks(mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()), "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
3042
3073
  this.toolExecutor.cancelAll();
@@ -4138,7 +4169,7 @@ Backup saved: ${result.backupPath}` : "";
4138
4169
  return;
4139
4170
  }
4140
4171
  try {
4141
- const { searchChatMemory, loadChatIndex } = await import("./chat-index-2D5GK2DB.js");
4172
+ const { searchChatMemory, loadChatIndex } = await import("./chat-index-7CLR4CBG.js");
4142
4173
  const loaded = loadChatIndex();
4143
4174
  if (!loaded || loaded.idx.chunks.length === 0) {
4144
4175
  this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
@@ -4174,7 +4205,7 @@ Backup saved: ${result.backupPath}` : "";
4174
4205
  }
4175
4206
  async handleMemoryStatus() {
4176
4207
  try {
4177
- const { getChatIndexStatus } = await import("./chat-index-2D5GK2DB.js");
4208
+ const { getChatIndexStatus } = await import("./chat-index-7CLR4CBG.js");
4178
4209
  const s = getChatIndexStatus();
4179
4210
  this.send({
4180
4211
  type: "memory_status",
@@ -4199,7 +4230,7 @@ Backup saved: ${result.backupPath}` : "";
4199
4230
  type: "info",
4200
4231
  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"
4201
4232
  });
4202
- const { buildChatIndex } = await import("./chat-index-2D5GK2DB.js");
4233
+ const { buildChatIndex } = await import("./chat-index-7CLR4CBG.js");
4203
4234
  const stats = await buildChatIndex({
4204
4235
  full,
4205
4236
  onProgress: (p) => {
@@ -4363,6 +4394,10 @@ async function startWebServer(options = {}) {
4363
4394
  console.log(`\u{1F916} ai-cli Web UI v${VERSION}`);
4364
4395
  console.log(` Initializing...`);
4365
4396
  const config = new ConfigManager();
4397
+ const authManager = new AuthManager(config.getConfigDir());
4398
+ if (!authManager.isEnabled() && !["localhost", "127.0.0.1", "::1"].includes(host)) {
4399
+ throw new Error("Network listening requires authentication. Create a user with aicli user create first.");
4400
+ }
4366
4401
  await setupProxy(config.get("proxy"));
4367
4402
  const providers = new ProviderRegistry();
4368
4403
  const sessions = new SessionManager(config);
@@ -4396,7 +4431,7 @@ async function startWebServer(options = {}) {
4396
4431
  const projectMcpResolved = mcpEnabled ? resolveProjectMcpPath() : null;
4397
4432
  let projectMcpServers = {};
4398
4433
  if (projectMcpResolved) {
4399
- const { checkTrust } = await import("./project-trust-NKYHL3VZ.js");
4434
+ const { checkTrust } = await import("./project-trust-KGG2BWKC.js");
4400
4435
  const verdict = checkTrust(config.getConfigDir(), projectMcpResolved);
4401
4436
  if (verdict.trusted) {
4402
4437
  projectMcpServers = loadProjectMcpConfig() ?? {};
@@ -4443,6 +4478,16 @@ async function startWebServer(options = {}) {
4443
4478
  skillManager
4444
4479
  };
4445
4480
  const app = express();
4481
+ const access = new WebAccess(host, options.externalOrigin);
4482
+ const authLimits = new AuthRateLimiter();
4483
+ app.use((req, res, next) => {
4484
+ if (!access.accepts(req)) {
4485
+ res.status(403).json({ error: "Untrusted request origin or host" });
4486
+ return;
4487
+ }
4488
+ res.locals.externalSecure = !!options.externalOrigin?.startsWith("https://") && req.headers.host === new URL(options.externalOrigin).host;
4489
+ next();
4490
+ });
4446
4491
  app.use((_req, res, next) => {
4447
4492
  res.setHeader(
4448
4493
  "Content-Security-Policy",
@@ -4471,19 +4516,23 @@ async function startWebServer(options = {}) {
4471
4516
  const WS_MSG_RATE_PER_SEC = 30;
4472
4517
  const WS_MSG_BURST = 60;
4473
4518
  const WS_PREAUTH_TIMEOUT_MS = 3e4;
4474
- const wss = new WebSocketServer({ server, maxPayload: WS_MAX_PAYLOAD });
4475
- const authManager = new AuthManager(config.getConfigDir());
4519
+ const wss = new WebSocketServer({ server, maxPayload: WS_MAX_PAYLOAD, verifyClient: ({ req }) => {
4520
+ if (!access.accepts(req) || wss.clients.size >= 128) return false;
4521
+ if (req.headers.origin) return true;
4522
+ const cookie = parseCookie(req.headers.cookie ?? "");
4523
+ return equalToken(cookie.aicli_local, access.localToken) || !!authManager.verifyToken(cookie.aicli_token ?? "");
4524
+ } });
4476
4525
  if (authManager.isEnabled()) {
4477
4526
  console.log(` Auth: ${authManager.listUsers().length} user(s) registered`);
4478
4527
  } else {
4479
- console.log(` Auth: disabled (no users registered)`);
4480
- if (host === "0.0.0.0" || host === "::") {
4481
- console.log(" WARNING: Web UI is reachable from the network without authentication.");
4482
- console.log(" Create a user first for LAN/team use: aicli user create <name>");
4483
- }
4528
+ console.log(" Auth: local launch token (no users registered)");
4484
4529
  }
4485
4530
  const requireAuth = (req, res, next) => {
4486
- if (!authManager.isEnabled()) return next();
4531
+ if (!authManager.isEnabled()) {
4532
+ if (equalToken(parseCookie(req.headers.cookie ?? "").aicli_local, access.localToken)) return next();
4533
+ res.status(401).json({ error: "Open the launch URL printed by aicli to authorize this browser" });
4534
+ return;
4535
+ }
4487
4536
  const authHeader = req.headers.authorization;
4488
4537
  const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
4489
4538
  const cookieToken = parseCookie(req.headers.cookie ?? "")["aicli_token"];
@@ -4514,6 +4563,7 @@ async function startWebServer(options = {}) {
4514
4563
  console.log(` Static files: ${clientDir}`);
4515
4564
  const PRECOMPRESSED_TYPES = {
4516
4565
  ".js": "text/javascript; charset=utf-8",
4566
+ ".mjs": "text/javascript; charset=utf-8",
4517
4567
  ".css": "text/css; charset=utf-8",
4518
4568
  ".html": "text/html; charset=utf-8",
4519
4569
  ".json": "application/json; charset=utf-8",
@@ -4554,9 +4604,28 @@ async function startWebServer(options = {}) {
4554
4604
  }
4555
4605
  });
4556
4606
  });
4557
- app.use(express.json());
4558
- app.post("/api/auth/register", (req, res) => {
4607
+ app.use(express.json({ limit: "16kb" }));
4608
+ app.post("/api/auth/bootstrap", (req, res) => {
4609
+ if (!equalToken(req.body?.token, access.localToken)) {
4610
+ res.status(403).json({ error: "Invalid launch token" });
4611
+ return;
4612
+ }
4613
+ res.cookie("aicli_local", access.localToken, { httpOnly: true, sameSite: "strict", secure: isHttpsRequest(req) });
4614
+ res.json({ success: true });
4615
+ });
4616
+ app.use("/api/auth", (req, res, next) => {
4617
+ if (!authLimits.take(req.socket.remoteAddress ?? "unknown")) {
4618
+ res.status(429).json({ error: "Too many authentication attempts; try again later" });
4619
+ return;
4620
+ }
4621
+ next();
4622
+ });
4623
+ app.post("/api/auth/register", async (req, res) => {
4559
4624
  const firstRun = !authManager.hasUsers();
4625
+ if (firstRun && !equalToken(parseCookie(req.headers.cookie ?? "").aicli_local, access.localToken)) {
4626
+ res.status(403).json({ error: "Open the launch URL before enabling authentication" });
4627
+ return;
4628
+ }
4560
4629
  if (!firstRun) {
4561
4630
  const authHeader = req.headers.authorization;
4562
4631
  const token2 = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
@@ -4572,23 +4641,24 @@ async function startWebServer(options = {}) {
4572
4641
  res.status(400).json({ error: "Username and password required" });
4573
4642
  return;
4574
4643
  }
4575
- const err = authManager.register(username, password);
4644
+ const err = await authManager.registerAsync(username, password);
4576
4645
  if (err) {
4577
4646
  res.status(400).json({ error: err });
4578
4647
  return;
4579
4648
  }
4580
- const token = authManager.login(username, password);
4649
+ const token = await authManager.loginAsync(username, password);
4650
+ invalidateConnections();
4581
4651
  console.log(` \u2713 User registered via API: ${username}${firstRun ? " (first-run)" : ""}`);
4582
4652
  res.cookie("aicli_token", token, authCookieOptions(req));
4583
4653
  res.json({ success: true, username });
4584
4654
  });
4585
- app.post("/api/auth/login", (req, res) => {
4655
+ app.post("/api/auth/login", async (req, res) => {
4586
4656
  const { username, password } = req.body ?? {};
4587
4657
  if (!username || !password) {
4588
4658
  res.status(400).json({ error: "Username and password required" });
4589
4659
  return;
4590
4660
  }
4591
- const token = authManager.login(username, password);
4661
+ const token = await authManager.loginAsync(username, password);
4592
4662
  if (!token) {
4593
4663
  res.status(401).json({ error: "Invalid username or password" });
4594
4664
  return;
@@ -4597,6 +4667,9 @@ async function startWebServer(options = {}) {
4597
4667
  res.json({ success: true, username });
4598
4668
  });
4599
4669
  app.post("/api/auth/logout", (req, res) => {
4670
+ const user = authManager.verifyToken(parseCookie(req.headers.cookie ?? "").aicli_token ?? "");
4671
+ if (user) authManager.logoutAll(user);
4672
+ invalidateConnections();
4600
4673
  res.clearCookie("aicli_token", clearAuthCookieOptions(req));
4601
4674
  res.json({ success: true });
4602
4675
  });
@@ -4692,6 +4765,7 @@ async function startWebServer(options = {}) {
4692
4765
  const fullPath = resolve3(join2(cwd, filePath));
4693
4766
  try {
4694
4767
  const canonicalFull = realpathSync(fullPath);
4768
+ assertReadablePath(canonicalFull);
4695
4769
  const canonicalCwd = realpathSync(resolve3(cwd));
4696
4770
  if (!canonicalFull.startsWith(canonicalCwd + sep2) && canonicalFull !== canonicalCwd) {
4697
4771
  res.json({ error: "Access denied" });
@@ -4719,6 +4793,9 @@ async function startWebServer(options = {}) {
4719
4793
  if (cached) return cached;
4720
4794
  const userDataDir = authManager.getUserDataDir(username);
4721
4795
  const userConfig = new ConfigManager(userDataDir);
4796
+ for (const key of ["defaultPermissionProfile", "allowedPermissionProfiles", "permissionProfiles", "permissionRules", "defaultPermission", "networkPolicy"]) {
4797
+ userConfig.setTransient(key, structuredClone(config.get(key)));
4798
+ }
4722
4799
  if (userConfig.isFirstRun()) {
4723
4800
  userConfig.setTransient("defaultProvider", config.get("defaultProvider"));
4724
4801
  userConfig.setTransient("defaultModels", config.get("defaultModels"));
@@ -4729,7 +4806,7 @@ async function startWebServer(options = {}) {
4729
4806
  // 每用户各自的 config 目录 → 各自的 cost-history.json
4730
4807
  costTracker: new CostTracker(userConfig.getConfigDir()),
4731
4808
  providers,
4732
- // Shared — providers re-read API keys per call
4809
+ // Host-owned service credentials; shared explicitly, never per-user keys.
4733
4810
  sessions: userSessions,
4734
4811
  toolRegistry,
4735
4812
  // Shared
@@ -4742,6 +4819,21 @@ async function startWebServer(options = {}) {
4742
4819
  return userShared;
4743
4820
  }
4744
4821
  const handlers = /* @__PURE__ */ new Map();
4822
+ const validators = /* @__PURE__ */ new Set();
4823
+ function invalidateConnections() {
4824
+ for (const validate of validators) validate();
4825
+ }
4826
+ let authRefreshTimer;
4827
+ server.on("listening", () => {
4828
+ if (!authRefreshTimer) {
4829
+ authRefreshTimer = setInterval(invalidateConnections, 1e3);
4830
+ authRefreshTimer.unref();
4831
+ }
4832
+ });
4833
+ server.on("close", () => {
4834
+ clearInterval(authRefreshTimer);
4835
+ authRefreshTimer = void 0;
4836
+ });
4745
4837
  wss.on("connection", (ws, req) => {
4746
4838
  const urlStr = req.url ?? "";
4747
4839
  const qMark = urlStr.indexOf("?");
@@ -4749,6 +4841,8 @@ async function startWebServer(options = {}) {
4749
4841
  const tabId = params.get("tabId") || `tab-${Date.now()}`;
4750
4842
  const cookies = parseCookie(req.headers.cookie ?? "");
4751
4843
  const token = cookies["aicli_token"] || "";
4844
+ let activeToken = token;
4845
+ const localAuthorized = equalToken(cookies.aicli_local, access.localToken);
4752
4846
  let tokens = WS_MSG_BURST;
4753
4847
  let lastRefill = Date.now();
4754
4848
  const takeToken = () => {
@@ -4808,7 +4902,7 @@ async function startWebServer(options = {}) {
4808
4902
  authenticatedUser = authManager.verifyToken(token);
4809
4903
  }
4810
4904
  const authRequired = authManager.isEnabled();
4811
- if (!authRequired) {
4905
+ if (!authRequired && localAuthorized) {
4812
4906
  console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (no auth) (total: ${handlers.size + 1})`);
4813
4907
  handler = attachHandler(shared);
4814
4908
  clearPreAuthTimer();
@@ -4826,7 +4920,26 @@ async function startWebServer(options = {}) {
4826
4920
  }));
4827
4921
  }
4828
4922
  }
4923
+ const validate = () => {
4924
+ if (!handler) return;
4925
+ let valid = false;
4926
+ try {
4927
+ valid = authManager.isEnabled() ? !!activeToken && authManager.verifyToken(activeToken) === authenticatedUser : localAuthorized && !authenticatedUser;
4928
+ } catch {
4929
+ }
4930
+ if (!valid) {
4931
+ handler.onDisconnect(false);
4932
+ if (handlers.get(handlerKey) === handler) handlers.delete(handlerKey);
4933
+ handler = null;
4934
+ ws.close(1008, "Authentication changed; reconnect");
4935
+ }
4936
+ };
4937
+ validators.add(validate);
4938
+ ws.on("close", () => validators.delete(validate));
4939
+ let authBusy = false;
4829
4940
  ws.on("message", async (data) => {
4941
+ validate();
4942
+ if (ws.readyState !== ws.OPEN) return;
4830
4943
  if (!takeToken()) {
4831
4944
  if (ws.readyState === ws.OPEN) {
4832
4945
  try {
@@ -4846,57 +4959,73 @@ async function startWebServer(options = {}) {
4846
4959
  return;
4847
4960
  }
4848
4961
  if (parsed.type === "auth") {
4849
- const { action, username, password } = parsed;
4850
- if (action === "register") {
4851
- if (authManager.hasUsers() && !authenticatedUser) {
4852
- ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Registration is closed. Use `aicli user create` on the host to add users." }));
4962
+ if (authBusy) return;
4963
+ authBusy = true;
4964
+ try {
4965
+ if (!authLimits.take(req.socket.remoteAddress ?? "unknown")) {
4966
+ ws.close(1008, "Authentication rate limit");
4853
4967
  return;
4854
4968
  }
4855
- const err = authManager.register(username, password);
4856
- if (err) {
4857
- ws.send(JSON.stringify({ type: "auth_result", success: false, error: err }));
4969
+ const { action, password } = parsed;
4970
+ const username = typeof parsed.username === "string" ? parsed.username.trim().toLowerCase() : "";
4971
+ if (action === "register") {
4972
+ if (!authenticatedUser && (authManager.hasUsers() || !localAuthorized)) {
4973
+ ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Registration is closed. Use `aicli user create` on the host to add users." }));
4974
+ return;
4975
+ }
4976
+ const err = await authManager.registerAsync(username, password);
4977
+ if (err) {
4978
+ ws.send(JSON.stringify({ type: "auth_result", success: false, error: err }));
4979
+ return;
4980
+ }
4981
+ activeToken = await authManager.loginAsync(username, password) ?? "";
4982
+ if (ws.readyState !== ws.OPEN || authManager.verifyToken(activeToken) !== username) return;
4983
+ authenticatedUser = username;
4984
+ const userShared = getUserShared(username);
4985
+ handler = attachHandler(userShared);
4986
+ clearPreAuthTimer();
4987
+ invalidateConnections();
4988
+ console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
4989
+ ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
4858
4990
  return;
4859
4991
  }
4860
- authManager.login(username, password);
4861
- authenticatedUser = username;
4862
- const userShared = getUserShared(username);
4863
- handler = attachHandler(userShared);
4864
- clearPreAuthTimer();
4865
- console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
4866
- ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
4867
- return;
4868
- }
4869
- if (action === "token") {
4870
- const { token: clientToken } = parsed;
4871
- const verifiedUser = authManager.verifyToken(clientToken);
4872
- if (!verifiedUser) {
4873
- ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Invalid or expired token" }));
4992
+ if (action === "token") {
4993
+ const { token: clientToken } = parsed;
4994
+ const verifiedUser = authManager.verifyToken(clientToken);
4995
+ if (!verifiedUser) {
4996
+ ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Invalid or expired token" }));
4997
+ return;
4998
+ }
4999
+ activeToken = clientToken;
5000
+ authenticatedUser = verifiedUser;
5001
+ const userShared = getUserShared(verifiedUser);
5002
+ handler = attachHandler(userShared);
5003
+ clearPreAuthTimer();
5004
+ console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
5005
+ ws.send(JSON.stringify({ type: "auth_result", success: true, username: verifiedUser }));
4874
5006
  return;
4875
5007
  }
4876
- authenticatedUser = verifiedUser;
4877
- const userShared = getUserShared(verifiedUser);
4878
- handler = attachHandler(userShared);
4879
- clearPreAuthTimer();
4880
- console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
4881
- ws.send(JSON.stringify({ type: "auth_result", success: true, username: verifiedUser }));
4882
- return;
4883
- }
4884
- if (action === "login") {
4885
- const loginToken = authManager.login(username, password);
4886
- if (!loginToken) {
4887
- ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Invalid username or password" }));
5008
+ if (action === "login") {
5009
+ const loginToken = await authManager.loginAsync(username, password);
5010
+ if (!loginToken) {
5011
+ ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Invalid username or password" }));
5012
+ return;
5013
+ }
5014
+ if (ws.readyState !== ws.OPEN || authManager.verifyToken(loginToken) !== username) return;
5015
+ activeToken = loginToken;
5016
+ authenticatedUser = username;
5017
+ const userShared = getUserShared(username);
5018
+ handler = attachHandler(userShared);
5019
+ clearPreAuthTimer();
5020
+ console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
5021
+ ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
4888
5022
  return;
4889
5023
  }
4890
- authenticatedUser = username;
4891
- const userShared = getUserShared(username);
4892
- handler = attachHandler(userShared);
4893
- clearPreAuthTimer();
4894
- console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
4895
- ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
5024
+ ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Unknown auth action" }));
4896
5025
  return;
5026
+ } finally {
5027
+ authBusy = false;
4897
5028
  }
4898
- ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Unknown auth action" }));
4899
- return;
4900
5029
  }
4901
5030
  if (!handler) {
4902
5031
  if (ws.readyState === ws.OPEN) {
@@ -4942,7 +5071,9 @@ async function startWebServer(options = {}) {
4942
5071
  }
4943
5072
  });
4944
5073
  server.listen(actualPort, host, () => {
4945
- const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${actualPort}`;
5074
+ actualPort = server.address().port;
5075
+ const address = host.includes(":") ? `[${host}]` : host === "0.0.0.0" ? "localhost" : host;
5076
+ const url = `http://${address}:${actualPort}` + (!authManager.isEnabled() ? `#token=${access.localToken}` : "");
4946
5077
  console.log(`
4947
5078
  \u{1F310} Web UI ready: ${url}`);
4948
5079
  if (actualPort !== port) console.log(` (port ${port} was in use, using ${actualPort})`);
@@ -4998,10 +5129,7 @@ function loadProjectMcpConfig() {
4998
5129
  }
4999
5130
  }
5000
5131
  function isHttpsRequest(req) {
5001
- if (req.secure) return true;
5002
- const forwardedProto = req.headers["x-forwarded-proto"];
5003
- const proto = Array.isArray(forwardedProto) ? forwardedProto[0] : forwardedProto;
5004
- return proto?.split(",")[0]?.trim().toLowerCase() === "https";
5132
+ return req.secure || req.res?.locals.externalSecure === true;
5005
5133
  }
5006
5134
  function authCookieOptions(req) {
5007
5135
  return {