jinzd-ai-cli 0.4.307 → 0.4.309

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 (96) hide show
  1. package/README.md +2 -1
  2. package/README.zh-CN.md +1 -1
  3. package/THIRD_PARTY_NOTICES.md +3 -0
  4. package/dist/{agent-client-6GX6QQDU.js → agent-client-TIEAYZXV.js} +4 -1
  5. package/dist/{auth-PE3Z5OHS.js → auth-QTQNZ6IX.js} +2 -2
  6. package/dist/{batch-HMADXNOD.js → batch-H4WWSZQ3.js} +3 -3
  7. package/dist/{chat-index-2D5GK2DB.js → chat-index-7CLR4CBG.js} +4 -3
  8. package/dist/{chat-index-DGUOUUQW.js → chat-index-RLVLQRRC.js} +3 -2
  9. package/dist/{chunk-CIM2PV2F.js → chunk-2E5W2E64.js} +2 -2
  10. package/dist/chunk-2XMUCC42.js +67 -0
  11. package/dist/{chunk-VAV2IJ3R.js → chunk-3CNPJLVI.js} +1 -1
  12. package/dist/{chunk-KRCYR46A.js → chunk-3GEO2LPO.js} +264 -55
  13. package/dist/{chunk-OAXLMF3U.js → chunk-3YVNDYOY.js} +1 -1
  14. package/dist/{chunk-5GQL4TSZ.js → chunk-4BXTGXZ6.js} +2 -2
  15. package/dist/{chunk-RYQBBEMM.js → chunk-4PR5AVJ5.js} +115 -19
  16. package/dist/{chunk-GHWZVPUV.js → chunk-6JHACXB3.js} +7 -5
  17. package/dist/{chunk-IUVDZVZ4.js → chunk-AXTLYOA7.js} +24 -17
  18. package/dist/{chunk-XJOK5353.js → chunk-B3MPYI4E.js} +2 -2
  19. package/dist/chunk-BMLQPSHP.js +69 -0
  20. package/dist/{chunk-NRSYBKWA.js → chunk-C7CKDFJS.js} +1 -1
  21. package/dist/{chunk-SJFZPS65.js → chunk-EAVX3RLD.js} +1 -1
  22. package/dist/{chunk-VXEF2SWG.js → chunk-EHMMBIZT.js} +16 -12
  23. package/dist/chunk-FVXNEZEW.js +161 -0
  24. package/dist/{chunk-AHL6KOGM.js → chunk-HXINWG37.js} +2 -2
  25. package/dist/{chunk-3CKJOTVM.js → chunk-IW6Q66WS.js} +4 -4
  26. package/dist/chunk-JJFJWD35.js +162 -0
  27. package/dist/{chunk-NYNSYLFY.js → chunk-JL44HGZC.js} +1 -1
  28. package/dist/{chunk-3TBXHLSC.js → chunk-K4F7QEGW.js} +1 -1
  29. package/dist/{chunk-NUJTNH76.js → chunk-LBWKMTT3.js} +1 -1
  30. package/dist/{chunk-YKJO3SI4.js → chunk-LONB36SP.js} +16 -3
  31. package/dist/{chunk-WQ3OMEC4.js → chunk-M6HLYG33.js} +1 -1
  32. package/dist/{chunk-I4GQRMFV.js → chunk-M6YV5KSS.js} +6 -4
  33. package/dist/{chunk-KBDKKHXB.js → chunk-O6CGOVD4.js} +2 -2
  34. package/dist/{chunk-L2RJPVHV.js → chunk-O7MMYJAD.js} +266 -512
  35. package/dist/chunk-QMFMST2T.js +39 -0
  36. package/dist/{chunk-AQF6IXSR.js → chunk-RBCKX2FZ.js} +11 -6
  37. package/dist/{chunk-4W6VDVQU.js → chunk-RLERORUI.js} +246 -56
  38. package/dist/{chunk-MI4SFZGX.js → chunk-RMEJR3AW.js} +5 -2
  39. package/dist/{chunk-UFW4S3RU.js → chunk-RVMCY6DP.js} +1 -1
  40. package/dist/{chunk-LF67HWWR.js → chunk-RWRZVQCB.js} +1 -1
  41. package/dist/{chunk-NQJSPMY2.js → chunk-SCG2VBPB.js} +4 -4
  42. package/dist/chunk-SXIJLSVF.js +41 -0
  43. package/dist/{chunk-YFDQIUQ3.js → chunk-T42GXE7Y.js} +18 -18
  44. package/dist/chunk-TDQW2YHK.js +65 -0
  45. package/dist/{chunk-45UVJXFQ.js → chunk-W2UOVGRC.js} +1 -1
  46. package/dist/{ci-QD2Q7NWY.js → ci-H6GPHG4T.js} +7 -7
  47. package/dist/{ci-format-42XIZO5P.js → ci-format-U47GSGYU.js} +2 -2
  48. package/dist/{constants-5CMEFZQB.js → constants-GYJTB6KI.js} +1 -1
  49. package/dist/{constants-NMASHFDL.js → constants-SNJR6546.js} +1 -1
  50. package/dist/doctor-cli-J6LAQ5LU.js +18 -0
  51. package/dist/electron-server.js +922 -842
  52. package/dist/file-checkpoint-64A2AXHE.js +7 -0
  53. package/dist/{file-checkpoint-NKBHGC7L.js → file-checkpoint-VVPUJNCI.js} +2 -1
  54. package/dist/{hub-ZLF6GMQD.js → hub-G5FMMTVZ.js} +3 -3
  55. package/dist/{hub-server-CBKCP5VD.js → hub-server-JLC7CNP7.js} +55 -17
  56. package/dist/index.js +48 -42
  57. package/dist/{indexer-P3FPRTPJ.js → indexer-GKIWSSRP.js} +3 -3
  58. package/dist/indexer-VRXDZ74A.js +10 -0
  59. package/dist/{persist-S77WRVLD.js → persist-ZZXRWFBX.js} +4 -4
  60. package/dist/{persistent-memory-GJE7HKVK.js → persistent-memory-EXJBXL33.js} +3 -3
  61. package/dist/{persistent-memory-MBSBTBGY.js → persistent-memory-LEE454DW.js} +3 -3
  62. package/dist/{pr-SXRW24JG.js → pr-6PUHVJ6K.js} +7 -7
  63. package/dist/{project-trust-MUG325AW.js → project-trust-6CJB5ZMX.js} +1 -1
  64. package/dist/{project-trust-NKYHL3VZ.js → project-trust-KGG2BWKC.js} +1 -1
  65. package/dist/run-tests-2C7MWZBN.js +12 -0
  66. package/dist/run-tests-33ZVQE3Q.js +13 -0
  67. package/dist/{semantic-7GM5M4MU.js → semantic-N227CLSC.js} +4 -4
  68. package/dist/{semantic-QOBAXEQ4.js → semantic-TRQAZSRY.js} +4 -4
  69. package/dist/{server-GGEO7SA2.js → server-AJH5TBVO.js} +231 -103
  70. package/dist/{server-MO3EU2XD.js → server-CYAB56O3.js} +16 -14
  71. package/dist/{store-SIKHCUYJ.js → store-2N6PPPBP.js} +2 -2
  72. package/dist/{store-CMSSWZF7.js → store-76VVFDXM.js} +2 -2
  73. package/dist/{task-orchestrator-JGEEQ2OE.js → task-orchestrator-HNRV3YUD.js} +15 -14
  74. package/dist/{usage-B7RIAPPT.js → usage-ZN5DU6Z7.js} +4 -4
  75. package/dist/{vector-store-JAXWDZYW.js → vector-store-T2DVUMY4.js} +2 -2
  76. package/dist/{vector-store-PWIOQFTP.js → vector-store-V6UGEYWK.js} +2 -2
  77. package/dist/web/client/app.js +7 -0
  78. package/dist/web/client/app.js.br +0 -0
  79. package/dist/web/client/app.js.gz +0 -0
  80. package/dist/web/client/util.js +14 -23
  81. package/dist/web/client/util.js.br +0 -0
  82. package/dist/web/client/util.js.gz +0 -0
  83. package/dist/web/client/vendor/DOMPurify-LICENSE.txt +202 -0
  84. package/dist/web/client/vendor/purify.es.mjs +2722 -0
  85. package/dist/web/client/vendor/purify.es.mjs.br +0 -0
  86. package/dist/web/client/vendor/purify.es.mjs.gz +0 -0
  87. package/package.json +4 -2
  88. package/dist/chunk-4BKXL7SM.js +0 -98
  89. package/dist/chunk-6BUTA5VW.js +0 -38
  90. package/dist/chunk-IW3Q7AE5.js +0 -40
  91. package/dist/chunk-SKET65WZ.js +0 -96
  92. package/dist/doctor-cli-IOVSHBDI.js +0 -18
  93. package/dist/file-checkpoint-CGH6OJVI.js +0 -6
  94. package/dist/indexer-FPKY7LCT.js +0 -10
  95. package/dist/run-tests-EQCAMFQE.js +0 -10
  96. package/dist/run-tests-V3N7YV3P.js +0 -9
package/README.md CHANGED
@@ -131,7 +131,7 @@ aicli user delete x # Delete user
131
131
  |----------|--------|-------------|
132
132
  | **Claude** | Opus 4, Sonnet 4, Haiku 4 | [console.anthropic.com](https://console.anthropic.com) |
133
133
  | **Gemini** | 2.5 Pro, 2.5 Flash | [aistudio.google.com](https://aistudio.google.com) |
134
- | **DeepSeek** | deepseek-v4-flash (default), deepseek-v4-pro | [platform.deepseek.com](https://platform.deepseek.com) |
134
+ | **DeepSeek** | deepseek-flash (default, multimodal), deepseek-v4-pro | [platform.deepseek.com](https://platform.deepseek.com) |
135
135
  | **OpenAI** | GPT-5.4, GPT-5, GPT-4.1, o3, o4-mini | [platform.openai.com](https://platform.openai.com) |
136
136
  | **OpenRouter** | 300+ models (Claude, GPT, Gemini, Llama, Qwen, Mistral...) | [openrouter.ai](https://openrouter.ai) |
137
137
  | **Zhipu** | GLM-4, GLM-5 | [open.bigmodel.cn](https://open.bigmodel.cn) |
@@ -578,6 +578,7 @@ Two groups: what **you** write by hand (the script never touches these, and Guar
578
578
  | `CHANGELOG.md` | **By hand, before releasing** | A new `## [X.Y.Z] - YYYY-MM-DD` section with the full narrative: root cause / fix / tests. This is the only place detail lives |
579
579
  | `CLAUDE.md` → `## 最近里程碑` | **By hand, before releasing** | **One** index line at the top, which must carry the full-width marker `(vX.Y.Z)` — this exact line is what Guard A checks |
580
580
  | `package.json` | Script | `"version"` |
581
+ | `package-lock.json` | Script | Root `version` and `packages[""].version`, including rollback on failed verification |
581
582
  | `src/core/constants.ts` | Script | The `VERSION` constant (what `aicli --version` and the help banner actually read — drift here ships a package that reports the wrong version) |
582
583
  | `CLAUDE.md` → `**当前版本**` | Script | The version number (a *second*, separate spot from the hand-written milestone line) |
583
584
  | `docs/USAGE.md`, `docs/USAGE.zh-CN.md`, `docs/ADVANCED*.md`, `docs/TUTORIAL*.md`, `docs/RECIPES*.md`, `SECURITY.md`, `docs/SECURITY*.md` | Script | Only the **document-header** baseline lines (`> **Version**: vX.Y.Z`, security baseline, `当前 vX.Y.Z`). Deliberately first-match-only — "feature introduced in v0.4.246+" markers in the body must keep their original versions |
package/README.zh-CN.md CHANGED
@@ -119,7 +119,7 @@ aicli user delete x # 删除用户
119
119
  |----------|------|-------------|
120
120
  | **Claude** | Opus 4, Sonnet 4, Haiku 4 | [console.anthropic.com](https://console.anthropic.com) |
121
121
  | **Gemini** | 2.5 Pro, 2.5 Flash | [aistudio.google.com](https://aistudio.google.com) |
122
- | **DeepSeek** | deepseek-v4-flash(默认), deepseek-v4-pro | [platform.deepseek.com](https://platform.deepseek.com) |
122
+ | **DeepSeek** | deepseek-flash(默认,多模态), deepseek-v4-pro | [platform.deepseek.com](https://platform.deepseek.com) |
123
123
  | **OpenAI** | GPT-5.4, GPT-5, GPT-4.1, o3, o4-mini | [platform.openai.com](https://platform.openai.com) |
124
124
  | **OpenRouter** | 300+ 模型(Claude/GPT/Gemini/Llama/Qwen/Mistral...) | [openrouter.ai](https://openrouter.ai) |
125
125
  | **Zhipu(智谱)** | GLM-4, GLM-5 | [open.bigmodel.cn](https://open.bigmodel.cn) |
@@ -8,6 +8,9 @@ ai-cli includes the following vendored browser assets. Their licenses apply to t
8
8
  | daisyUI | `src/web/client/vendor/daisyui-full.min.css` | 4.12.23 | MIT | https://github.com/saadeghi/daisyui |
9
9
  | highlight.js and GitHub themes | `highlight.min.js`, `github.min.css`, `github-dark.min.css` | 11.9.0 | BSD-3-Clause | https://github.com/highlightjs/highlight.js |
10
10
  | marked | `src/web/client/vendor/marked.min.js` | 15.0.12 | MIT | https://github.com/markedjs/marked |
11
+ | DOMPurify | `src/web/client/vendor/purify.es.mjs` | 3.4.15 | Apache-2.0 OR MPL-2.0 | https://github.com/cure53/DOMPurify |
12
+
13
+ DOMPurify's full license is included in `src/web/client/vendor/DOMPurify-LICENSE.txt` and copied into the distributed Web client. The build copies the browser module from the installed, lockfile-pinned package.
11
14
 
12
15
  When replacing a vendored asset, update this file and preserve its upstream license header.
13
16
 
@@ -40,7 +40,9 @@ async function joinHub(options, configManager, providers) {
40
40
  console.log(chalk.dim(` Provider: ${defaultProvider} / ${defaultModel}`));
41
41
  }
42
42
  console.log();
43
- const ws = new WebSocket(url);
43
+ const token = process.env.AICLI_HUB_TOKEN;
44
+ if (!token) throw new Error("Set AICLI_HUB_TOKEN to the credential printed by the hub server.");
45
+ const ws = new WebSocket(url, { headers: { Authorization: `Bearer ${token}` }, maxPayload: 1024 * 1024 });
44
46
  let myRoleId = "";
45
47
  let myRoleName = "";
46
48
  let topic = "";
@@ -133,6 +135,7 @@ async function joinHub(options, configManager, providers) {
133
135
  }
134
136
  const reply = {
135
137
  type: "response",
138
+ turnId: msg.turnId,
136
139
  content: passed ? "" : response.trim(),
137
140
  passed
138
141
  };
@@ -3,8 +3,8 @@ import {
3
3
  AuthManager,
4
4
  TOKEN_EXPIRY_MS,
5
5
  __resetLoginAttemptsForTests
6
- } from "./chunk-RYQBBEMM.js";
7
- import "./chunk-IW3Q7AE5.js";
6
+ } from "./chunk-4PR5AVJ5.js";
7
+ import "./chunk-BMLQPSHP.js";
8
8
  export {
9
9
  AuthManager,
10
10
  TOKEN_EXPIRY_MS,
@@ -4,12 +4,12 @@ import {
4
4
  } from "./chunk-RS7SKYTS.js";
5
5
  import {
6
6
  ConfigManager
7
- } from "./chunk-AQF6IXSR.js";
7
+ } from "./chunk-RBCKX2FZ.js";
8
8
  import "./chunk-TU3L3PW7.js";
9
9
  import {
10
10
  atomicWriteFileSync
11
- } from "./chunk-IW3Q7AE5.js";
12
- import "./chunk-VAV2IJ3R.js";
11
+ } from "./chunk-BMLQPSHP.js";
12
+ import "./chunk-3CNPJLVI.js";
13
13
 
14
14
  // src/cli/batch.ts
15
15
  import Anthropic from "@anthropic-ai/sdk";
@@ -6,11 +6,12 @@ import {
6
6
  getChatIndexStatus,
7
7
  loadChatIndex,
8
8
  searchChatMemory
9
- } from "./chunk-GHWZVPUV.js";
9
+ } from "./chunk-6JHACXB3.js";
10
10
  import "./chunk-KHYD3WXE.js";
11
- import "./chunk-NYNSYLFY.js";
11
+ import "./chunk-JL44HGZC.js";
12
12
  import "./chunk-3FEZSLGI.js";
13
- import "./chunk-IW3Q7AE5.js";
13
+ import "./chunk-BMLQPSHP.js";
14
+ import "./chunk-SXIJLSVF.js";
14
15
  export {
15
16
  buildChatIndex,
16
17
  chunkSession,
@@ -5,10 +5,11 @@ import {
5
5
  getChatIndexStatus,
6
6
  loadChatIndex,
7
7
  searchChatMemory
8
- } from "./chunk-I4GQRMFV.js";
8
+ } from "./chunk-M6YV5KSS.js";
9
9
  import "./chunk-JV5N65KN.js";
10
10
  import "./chunk-FSC6KEWU.js";
11
- import "./chunk-6BUTA5VW.js";
11
+ import "./chunk-2XMUCC42.js";
12
+ import "./chunk-QMFMST2T.js";
12
13
  export {
13
14
  buildChatIndex,
14
15
  chunkSession,
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-FSC6KEWU.js";
5
5
  import {
6
6
  atomicWriteFileSync
7
- } from "./chunk-6BUTA5VW.js";
7
+ } from "./chunk-2XMUCC42.js";
8
8
  import {
9
9
  getGitRoot
10
10
  } from "./chunk-CGLYJRFA.js";
@@ -12,7 +12,7 @@ import {
12
12
  MEMORY_FILE_NAME,
13
13
  MEMORY_MAX_CHARS,
14
14
  MEMORY_STORE_FILE_NAME
15
- } from "./chunk-SJFZPS65.js";
15
+ } from "./chunk-EAVX3RLD.js";
16
16
 
17
17
  // src/memory/persistent-memory.ts
18
18
  import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync } from "fs";
@@ -0,0 +1,67 @@
1
+ // src/core/atomic-write.ts
2
+ import fs from "fs";
3
+ import { randomUUID } from "crypto";
4
+ import { setTimeout as delay } from "timers/promises";
5
+ function modeFor(target, options) {
6
+ if (options.mode !== void 0) return options.mode;
7
+ try {
8
+ return fs.statSync(target).mode & 511;
9
+ } catch {
10
+ return 384;
11
+ }
12
+ }
13
+ function atomicWriteFileSync(target, data, options = {}) {
14
+ const tmp = `${target}.${process.pid}.${randomUUID()}.tmp`;
15
+ try {
16
+ fs.writeFileSync(tmp, data, { flag: "wx", mode: modeFor(target, options) });
17
+ fs.renameSync(tmp, target);
18
+ } finally {
19
+ try {
20
+ fs.unlinkSync(tmp);
21
+ } catch {
22
+ }
23
+ }
24
+ }
25
+ var pending = /* @__PURE__ */ new Map();
26
+ async function atomicWriteFile(target, data, options = {}) {
27
+ const previous = pending.get(target) ?? Promise.resolve();
28
+ const operation = previous.catch(() => {
29
+ }).then(async () => {
30
+ const tmp = `${target}.${process.pid}.${randomUUID()}.tmp`;
31
+ try {
32
+ let mode = options.mode ?? 384;
33
+ if (options.mode === void 0) {
34
+ try {
35
+ mode = (await fs.promises.stat(target)).mode & 511;
36
+ } catch {
37
+ }
38
+ }
39
+ await fs.promises.writeFile(tmp, data, { flag: "wx", mode });
40
+ for (let attempt = 0; ; attempt++) {
41
+ try {
42
+ await fs.promises.rename(tmp, target);
43
+ break;
44
+ } catch (error) {
45
+ if (attempt >= 10 || !["EPERM", "EBUSY", "EACCES"].includes(error.code ?? "")) throw error;
46
+ await delay(5 * (attempt + 1));
47
+ }
48
+ }
49
+ } finally {
50
+ try {
51
+ await fs.promises.unlink(tmp);
52
+ } catch {
53
+ }
54
+ }
55
+ });
56
+ pending.set(target, operation);
57
+ try {
58
+ await operation;
59
+ } finally {
60
+ if (pending.get(target) === operation) pending.delete(target);
61
+ }
62
+ }
63
+
64
+ export {
65
+ atomicWriteFileSync,
66
+ atomicWriteFile
67
+ };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.307";
4
+ var VERSION = "0.4.309";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -1,15 +1,195 @@
1
+ #!/usr/bin/env node
1
2
  import {
2
3
  TEST_TIMEOUT
3
- } from "./chunk-SJFZPS65.js";
4
+ } from "./chunk-3CNPJLVI.js";
5
+ import {
6
+ getExecutionConfigDir,
7
+ getExecutionSignal
8
+ } from "./chunk-SXIJLSVF.js";
9
+
10
+ // src/tools/builtin/run-tests.ts
11
+ import { spawn as spawn2 } from "child_process";
12
+
13
+ // src/core/interrupt.ts
14
+ var controller = new AbortController();
15
+ var interrupted = false;
16
+ function currentAbortSignal() {
17
+ return getExecutionSignal() ?? controller.signal;
18
+ }
19
+ function isInterrupted() {
20
+ return interrupted;
21
+ }
22
+ function requestInterrupt() {
23
+ interrupted = true;
24
+ if (!controller.signal.aborted) {
25
+ controller.abort(new DOMException("User interrupted (Ctrl+C)", "AbortError"));
26
+ }
27
+ }
28
+ function resetInterrupt() {
29
+ controller = new AbortController();
30
+ interrupted = false;
31
+ }
32
+
33
+ // src/tools/win-shell.ts
34
+ import { platform } from "os";
35
+ import { spawn } from "child_process";
36
+ var IS_WINDOWS = platform() === "win32";
37
+ var WIN_UTF8_PREAMBLE = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; $OutputEncoding = [System.Text.Encoding]::UTF8; ";
38
+ function shellInvocation(command) {
39
+ if (IS_WINDOWS) {
40
+ return {
41
+ shell: "powershell.exe",
42
+ args: ["-NoProfile", "-NonInteractive", "-Command", WIN_UTF8_PREAMBLE + command]
43
+ };
44
+ }
45
+ return { shell: process.env["SHELL"] ?? "/bin/bash", args: ["-c", command] };
46
+ }
47
+ function decodeWindowsBuffer(buf) {
48
+ const utf8 = buf.toString("utf-8");
49
+ if (!utf8.includes("\uFFFD")) return utf8;
50
+ try {
51
+ return new TextDecoder("gbk").decode(buf);
52
+ } catch {
53
+ return utf8;
54
+ }
55
+ }
56
+ var GBK_MOJIBAKE_FINGERPRINTS = [
57
+ "\uFFFD",
58
+ "\u9215",
59
+ "\u951B",
60
+ "\u9983",
61
+ "\u9241",
62
+ "\u6D93",
63
+ "\u935B",
64
+ "\u9428",
65
+ "\u7EFE",
66
+ "\u95AB",
67
+ "\u9593",
68
+ "\u95BF",
69
+ "\u68E3",
70
+ "\u95B4",
71
+ "\u5A11",
72
+ "\u95B8",
73
+ "\u95BB",
74
+ "\u7F01",
75
+ "\u95C1"
76
+ ];
77
+ var gbkReverse = null;
78
+ function gbkReverseMap() {
79
+ if (gbkReverse) return gbkReverse;
80
+ const map = /* @__PURE__ */ new Map();
81
+ try {
82
+ const dec = new TextDecoder("gbk");
83
+ for (let hi = 129; hi <= 254; hi++) {
84
+ for (let lo = 64; lo <= 254; lo++) {
85
+ if (lo === 127) continue;
86
+ const ch = dec.decode(new Uint8Array([hi, lo]));
87
+ if (ch.length === 1 && ch !== "\uFFFD" && !map.has(ch)) map.set(ch, [hi, lo]);
88
+ }
89
+ }
90
+ } catch {
91
+ }
92
+ gbkReverse = map;
93
+ return map;
94
+ }
95
+ function decodeRunAsChinese(bytes) {
96
+ for (let start = 0; start <= 2 && start < bytes.length; start++) {
97
+ for (let end = 0; end <= 2 && bytes.length - end > start; end++) {
98
+ const slice = bytes.slice(start, bytes.length - end);
99
+ if (slice.length < 6) continue;
100
+ try {
101
+ const out = new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(slice));
102
+ if (/[一-鿿]/.test(out)) return out;
103
+ } catch {
104
+ }
105
+ }
106
+ }
107
+ return null;
108
+ }
109
+ function recoverGbkMojibake(text) {
110
+ if (!text) return null;
111
+ const map = gbkReverseMap();
112
+ if (map.size === 0) return null;
113
+ let run = [];
114
+ let best = null;
115
+ const flush = () => {
116
+ if (run.length >= 4) {
117
+ const bytes = [];
118
+ for (const p of run) bytes.push(p[0], p[1]);
119
+ const out = decodeRunAsChinese(bytes);
120
+ if (out && (!best || out.length > best.length)) best = out;
121
+ }
122
+ run = [];
123
+ };
124
+ for (const ch of text) {
125
+ const pair = ch.codePointAt(0) < 128 ? void 0 : map.get(ch);
126
+ if (pair) run.push(pair);
127
+ else flush();
128
+ }
129
+ flush();
130
+ return best;
131
+ }
132
+ function looksLikeGbkMojibake(text) {
133
+ if (recoverGbkMojibake(text) !== null) return true;
134
+ let hits = 0;
135
+ for (const ch of GBK_MOJIBAKE_FINGERPRINTS) {
136
+ if (text.includes(ch) && ++hits >= 2) return true;
137
+ }
138
+ return false;
139
+ }
140
+ function killProcessTree(proc, processGroup = false) {
141
+ if (proc.killed || proc.exitCode !== null) return;
142
+ if (IS_WINDOWS && proc.pid) {
143
+ try {
144
+ const tk = spawn("taskkill", ["/PID", String(proc.pid), "/T", "/F"], {
145
+ windowsHide: true,
146
+ stdio: "ignore"
147
+ });
148
+ tk.on("exit", () => {
149
+ try {
150
+ proc.kill();
151
+ } catch {
152
+ }
153
+ });
154
+ tk.on("error", () => {
155
+ try {
156
+ proc.kill();
157
+ } catch {
158
+ }
159
+ });
160
+ } catch {
161
+ try {
162
+ proc.kill();
163
+ } catch {
164
+ }
165
+ }
166
+ return;
167
+ }
168
+ const signal = (value) => {
169
+ if (processGroup && proc.pid) process.kill(-proc.pid, value);
170
+ else proc.kill(value);
171
+ };
172
+ try {
173
+ signal("SIGTERM");
174
+ } catch {
175
+ }
176
+ setTimeout(() => {
177
+ if (processGroup || proc.exitCode === null) {
178
+ try {
179
+ signal("SIGKILL");
180
+ } catch {
181
+ }
182
+ }
183
+ }, 500).unref();
184
+ }
4
185
 
5
186
  // src/tools/builtin/run-tests.ts
6
- import { execSync, spawnSync } from "child_process";
7
187
  import { existsSync, readFileSync, readdirSync } from "fs";
8
188
  import { join } from "path";
9
- import { platform } from "os";
189
+ import { platform as platform2 } from "os";
10
190
  import chalk from "chalk";
11
- var FILTER_WHITELIST = /^[\w.\-/:#\s*?()\[\]@+]{1,200}$/;
12
- var IS_WINDOWS = platform() === "win32";
191
+ var FILTER_WHITELIST = /^[\w.\-/:# *?\[\]@+]{1,200}$/;
192
+ var IS_WINDOWS2 = platform2() === "win32";
13
193
  function detectNodeTestFramework(cwd, pkg) {
14
194
  const devDeps = pkg.devDependencies ?? {};
15
195
  const deps = pkg.dependencies ?? {};
@@ -79,11 +259,11 @@ function safeReadPackageJson(cwd) {
79
259
  }
80
260
  function detectProject(cwd) {
81
261
  if (existsSync(join(cwd, "pom.xml"))) {
82
- return { type: "java", framework: "Maven (JUnit)", command: IS_WINDOWS ? "mvn.cmd test" : "mvn test" };
262
+ return { type: "java", framework: "Maven (JUnit)", command: IS_WINDOWS2 ? "mvn.cmd test" : "mvn test" };
83
263
  }
84
264
  if (existsSync(join(cwd, "build.gradle")) || existsSync(join(cwd, "build.gradle.kts"))) {
85
- const wrapper = IS_WINDOWS ? "gradlew.bat" : "./gradlew";
86
- const cmd = existsSync(join(cwd, IS_WINDOWS ? "gradlew.bat" : "gradlew")) ? `${wrapper} test` : "gradle test";
265
+ const wrapper = IS_WINDOWS2 ? "gradlew.bat" : "./gradlew";
266
+ const cmd = existsSync(join(cwd, IS_WINDOWS2 ? "gradlew.bat" : "gradlew")) ? `${wrapper} test` : "gradle test";
87
267
  return { type: "java", framework: "Gradle (JUnit)", command: cmd };
88
268
  }
89
269
  if (existsSync(join(cwd, "package.json"))) {
@@ -239,7 +419,7 @@ function renderColorReport(summary, framework) {
239
419
  console.log();
240
420
  }
241
421
  function needsWindowsShell(file) {
242
- if (!IS_WINDOWS) return false;
422
+ if (!IS_WINDOWS2) return false;
243
423
  const lower = file.toLowerCase();
244
424
  return lower.endsWith(".cmd") || lower.endsWith(".bat") || lower === "mvn" || lower === "gradle" || lower === "npm" || lower === "npx";
245
425
  }
@@ -306,51 +486,9 @@ Got: ${filter.slice(0, 60)}${filter.length > 60 ? "..." : ""}`;
306
486
  framework = detected.framework;
307
487
  argv = buildArgv(detected, filter);
308
488
  }
309
- let output;
310
- let exitCode = 0;
311
- if (argv) {
312
- const r = spawnSync(argv.file, argv.args, {
313
- cwd,
314
- timeout: TEST_TIMEOUT,
315
- stdio: ["pipe", "pipe", "pipe"],
316
- // shell:false is the default for spawnSync; on Windows .cmd/.bat
317
- // requires shell:true, so we opt-in only for known wrapper names.
318
- shell: needsWindowsShell(argv.file),
319
- env: {
320
- ...process.env,
321
- ...IS_WINDOWS ? {} : { FORCE_COLOR: "0" }
322
- },
323
- encoding: "utf-8",
324
- maxBuffer: 64 * 1024 * 1024
325
- });
326
- if (r.error) {
327
- output = `Failed to launch test runner: ${r.error.message}`;
328
- exitCode = 1;
329
- } else {
330
- exitCode = r.status ?? 1;
331
- output = (r.stdout ?? "") + (r.stderr ? "\n" + r.stderr : "");
332
- }
333
- } else {
334
- try {
335
- const buf = execSync(command, {
336
- cwd,
337
- timeout: TEST_TIMEOUT,
338
- encoding: "buffer",
339
- stdio: ["pipe", "pipe", "pipe"],
340
- env: {
341
- ...process.env,
342
- ...IS_WINDOWS ? {} : { FORCE_COLOR: "0" }
343
- }
344
- });
345
- output = buf.toString("utf-8");
346
- } catch (err) {
347
- const e = err;
348
- exitCode = e.status ?? 1;
349
- const stdout = e.stdout?.toString("utf-8") ?? "";
350
- const stderr = e.stderr?.toString("utf-8") ?? "";
351
- output = stdout + (stderr ? "\n" + stderr : "");
352
- }
353
- }
489
+ const result = await runTestProcess(argv, command, cwd, currentAbortSignal());
490
+ const output = result.output;
491
+ const exitCode = result.exitCode;
354
492
  let summary = {
355
493
  tests: 0,
356
494
  passed: 0,
@@ -389,6 +527,7 @@ Got: ${filter.slice(0, 60)}${filter.length > 60 ? "..." : ""}`;
389
527
  }
390
528
  }
391
529
  }
530
+ if (exitCode !== 0 && summary.failures + summary.errors === 0) summary.errors = 1;
392
531
  renderColorReport(summary, framework);
393
532
  return formatReport(summary, framework, output, exitCode);
394
533
  }
@@ -413,8 +552,78 @@ var runTestsTool = {
413
552
  return executeTests(args);
414
553
  }
415
554
  };
555
+ var MAX_TEST_OUTPUT_BYTES = 4 * 1024 * 1024;
556
+ var activeTestProcesses = 0;
557
+ var activeTestsByOwner = /* @__PURE__ */ new Map();
558
+ async function runTestProcess(argv, command, cwd, signal) {
559
+ if (signal.aborted) throw new Error("Test run cancelled");
560
+ const owner = getExecutionConfigDir();
561
+ if ((activeTestsByOwner.get(owner) ?? 0) >= 2) throw new Error("Test runner capacity reached (2 per user).");
562
+ if (activeTestProcesses >= 4) throw new Error("Test runner capacity reached (4). Try again after a run finishes.");
563
+ activeTestProcesses++;
564
+ activeTestsByOwner.set(owner, (activeTestsByOwner.get(owner) ?? 0) + 1);
565
+ try {
566
+ return await new Promise((resolve, reject) => {
567
+ const child = argv ? spawn2(argv.file, argv.args, { cwd, shell: needsWindowsShell(argv.file), windowsHide: true, detached: !IS_WINDOWS2, stdio: ["ignore", "pipe", "pipe"] }) : spawn2(command, { cwd, shell: true, windowsHide: true, detached: !IS_WINDOWS2, stdio: ["ignore", "pipe", "pipe"] });
568
+ const chunks = [];
569
+ let bytes = 0;
570
+ let stopped = "";
571
+ let settled = false;
572
+ const stop = (reason) => {
573
+ if (stopped) return;
574
+ stopped = reason;
575
+ killProcessTree(child, true);
576
+ };
577
+ const onAbort = () => stop("Test run cancelled");
578
+ const timer = setTimeout(() => stop("Test run timed out"), TEST_TIMEOUT);
579
+ signal.addEventListener("abort", onAbort, { once: true });
580
+ if (signal.aborted) onAbort();
581
+ const append = (chunk) => {
582
+ const take = Math.min(chunk.length, MAX_TEST_OUTPUT_BYTES - bytes);
583
+ if (take > 0) chunks.push(chunk.subarray(0, take));
584
+ bytes += take;
585
+ if (bytes >= MAX_TEST_OUTPUT_BYTES) stop("Test output exceeded 4 MiB limit");
586
+ };
587
+ child.stdout?.on("data", append);
588
+ child.stderr?.on("data", append);
589
+ const clean = () => {
590
+ clearTimeout(timer);
591
+ signal.removeEventListener("abort", onAbort);
592
+ };
593
+ child.once("error", (error) => {
594
+ if (settled) return;
595
+ settled = true;
596
+ clean();
597
+ reject(error);
598
+ });
599
+ child.once("close", (code) => {
600
+ if (settled) return;
601
+ settled = true;
602
+ clean();
603
+ resolve({ output: Buffer.concat(chunks).toString("utf8") + (stopped ? "\n" + stopped : ""), exitCode: stopped ? 1 : code ?? 1 });
604
+ });
605
+ });
606
+ } finally {
607
+ activeTestProcesses--;
608
+ const remaining = (activeTestsByOwner.get(owner) ?? 1) - 1;
609
+ if (remaining) activeTestsByOwner.set(owner, remaining);
610
+ else activeTestsByOwner.delete(owner);
611
+ }
612
+ }
416
613
 
417
614
  export {
615
+ IS_WINDOWS,
616
+ WIN_UTF8_PREAMBLE,
617
+ shellInvocation,
618
+ decodeWindowsBuffer,
619
+ recoverGbkMojibake,
620
+ looksLikeGbkMojibake,
621
+ killProcessTree,
622
+ currentAbortSignal,
623
+ isInterrupted,
624
+ requestInterrupt,
625
+ resetInterrupt,
418
626
  executeTests,
419
- runTestsTool
627
+ runTestsTool,
628
+ runTestProcess
420
629
  };
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-JV5N65KN.js";
4
4
  import {
5
5
  atomicWriteFileSync
6
- } from "./chunk-6BUTA5VW.js";
6
+ } from "./chunk-2XMUCC42.js";
7
7
 
8
8
  // src/symbols/vector-store.ts
9
9
  import fs from "fs";
@@ -4,11 +4,11 @@ import {
4
4
  } from "./chunk-RS7SKYTS.js";
5
5
  import {
6
6
  atomicWriteFileSync
7
- } from "./chunk-IW3Q7AE5.js";
7
+ } from "./chunk-BMLQPSHP.js";
8
8
  import {
9
9
  CONFIG_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-VAV2IJ3R.js";
11
+ } from "./chunk-3CNPJLVI.js";
12
12
 
13
13
  // src/diagnostics/tool-stats.ts
14
14
  import { mkdirSync } from "fs";