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.
- package/README.md +2 -1
- package/README.zh-CN.md +1 -1
- package/THIRD_PARTY_NOTICES.md +3 -0
- package/dist/{agent-client-6GX6QQDU.js → agent-client-TIEAYZXV.js} +4 -1
- package/dist/{auth-PE3Z5OHS.js → auth-QTQNZ6IX.js} +2 -2
- package/dist/{batch-HMADXNOD.js → batch-H4WWSZQ3.js} +3 -3
- package/dist/{chat-index-2D5GK2DB.js → chat-index-7CLR4CBG.js} +4 -3
- package/dist/{chat-index-DGUOUUQW.js → chat-index-RLVLQRRC.js} +3 -2
- package/dist/{chunk-CIM2PV2F.js → chunk-2E5W2E64.js} +2 -2
- package/dist/chunk-2XMUCC42.js +67 -0
- package/dist/{chunk-VAV2IJ3R.js → chunk-3CNPJLVI.js} +1 -1
- package/dist/{chunk-KRCYR46A.js → chunk-3GEO2LPO.js} +264 -55
- package/dist/{chunk-OAXLMF3U.js → chunk-3YVNDYOY.js} +1 -1
- package/dist/{chunk-5GQL4TSZ.js → chunk-4BXTGXZ6.js} +2 -2
- package/dist/{chunk-RYQBBEMM.js → chunk-4PR5AVJ5.js} +115 -19
- package/dist/{chunk-GHWZVPUV.js → chunk-6JHACXB3.js} +7 -5
- package/dist/{chunk-IUVDZVZ4.js → chunk-AXTLYOA7.js} +24 -17
- package/dist/{chunk-XJOK5353.js → chunk-B3MPYI4E.js} +2 -2
- package/dist/chunk-BMLQPSHP.js +69 -0
- package/dist/{chunk-NRSYBKWA.js → chunk-C7CKDFJS.js} +1 -1
- package/dist/{chunk-SJFZPS65.js → chunk-EAVX3RLD.js} +1 -1
- package/dist/{chunk-VXEF2SWG.js → chunk-EHMMBIZT.js} +16 -12
- package/dist/chunk-FVXNEZEW.js +161 -0
- package/dist/{chunk-AHL6KOGM.js → chunk-HXINWG37.js} +2 -2
- package/dist/{chunk-3CKJOTVM.js → chunk-IW6Q66WS.js} +4 -4
- package/dist/chunk-JJFJWD35.js +162 -0
- package/dist/{chunk-NYNSYLFY.js → chunk-JL44HGZC.js} +1 -1
- package/dist/{chunk-3TBXHLSC.js → chunk-K4F7QEGW.js} +1 -1
- package/dist/{chunk-NUJTNH76.js → chunk-LBWKMTT3.js} +1 -1
- package/dist/{chunk-YKJO3SI4.js → chunk-LONB36SP.js} +16 -3
- package/dist/{chunk-WQ3OMEC4.js → chunk-M6HLYG33.js} +1 -1
- package/dist/{chunk-I4GQRMFV.js → chunk-M6YV5KSS.js} +6 -4
- package/dist/{chunk-KBDKKHXB.js → chunk-O6CGOVD4.js} +2 -2
- package/dist/{chunk-L2RJPVHV.js → chunk-O7MMYJAD.js} +266 -512
- package/dist/chunk-QMFMST2T.js +39 -0
- package/dist/{chunk-AQF6IXSR.js → chunk-RBCKX2FZ.js} +11 -6
- package/dist/{chunk-4W6VDVQU.js → chunk-RLERORUI.js} +246 -56
- package/dist/{chunk-MI4SFZGX.js → chunk-RMEJR3AW.js} +5 -2
- package/dist/{chunk-UFW4S3RU.js → chunk-RVMCY6DP.js} +1 -1
- package/dist/{chunk-LF67HWWR.js → chunk-RWRZVQCB.js} +1 -1
- package/dist/{chunk-NQJSPMY2.js → chunk-SCG2VBPB.js} +4 -4
- package/dist/chunk-SXIJLSVF.js +41 -0
- package/dist/{chunk-YFDQIUQ3.js → chunk-T42GXE7Y.js} +18 -18
- package/dist/chunk-TDQW2YHK.js +65 -0
- package/dist/{chunk-45UVJXFQ.js → chunk-W2UOVGRC.js} +1 -1
- package/dist/{ci-QD2Q7NWY.js → ci-H6GPHG4T.js} +7 -7
- package/dist/{ci-format-42XIZO5P.js → ci-format-U47GSGYU.js} +2 -2
- package/dist/{constants-5CMEFZQB.js → constants-GYJTB6KI.js} +1 -1
- package/dist/{constants-NMASHFDL.js → constants-SNJR6546.js} +1 -1
- package/dist/doctor-cli-J6LAQ5LU.js +18 -0
- package/dist/electron-server.js +922 -842
- package/dist/file-checkpoint-64A2AXHE.js +7 -0
- package/dist/{file-checkpoint-NKBHGC7L.js → file-checkpoint-VVPUJNCI.js} +2 -1
- package/dist/{hub-ZLF6GMQD.js → hub-G5FMMTVZ.js} +3 -3
- package/dist/{hub-server-CBKCP5VD.js → hub-server-JLC7CNP7.js} +55 -17
- package/dist/index.js +48 -42
- package/dist/{indexer-P3FPRTPJ.js → indexer-GKIWSSRP.js} +3 -3
- package/dist/indexer-VRXDZ74A.js +10 -0
- package/dist/{persist-S77WRVLD.js → persist-ZZXRWFBX.js} +4 -4
- package/dist/{persistent-memory-GJE7HKVK.js → persistent-memory-EXJBXL33.js} +3 -3
- package/dist/{persistent-memory-MBSBTBGY.js → persistent-memory-LEE454DW.js} +3 -3
- package/dist/{pr-SXRW24JG.js → pr-6PUHVJ6K.js} +7 -7
- package/dist/{project-trust-MUG325AW.js → project-trust-6CJB5ZMX.js} +1 -1
- package/dist/{project-trust-NKYHL3VZ.js → project-trust-KGG2BWKC.js} +1 -1
- package/dist/run-tests-2C7MWZBN.js +12 -0
- package/dist/run-tests-33ZVQE3Q.js +13 -0
- package/dist/{semantic-7GM5M4MU.js → semantic-N227CLSC.js} +4 -4
- package/dist/{semantic-QOBAXEQ4.js → semantic-TRQAZSRY.js} +4 -4
- package/dist/{server-GGEO7SA2.js → server-AJH5TBVO.js} +231 -103
- package/dist/{server-MO3EU2XD.js → server-CYAB56O3.js} +16 -14
- package/dist/{store-SIKHCUYJ.js → store-2N6PPPBP.js} +2 -2
- package/dist/{store-CMSSWZF7.js → store-76VVFDXM.js} +2 -2
- package/dist/{task-orchestrator-JGEEQ2OE.js → task-orchestrator-HNRV3YUD.js} +15 -14
- package/dist/{usage-B7RIAPPT.js → usage-ZN5DU6Z7.js} +4 -4
- package/dist/{vector-store-JAXWDZYW.js → vector-store-T2DVUMY4.js} +2 -2
- package/dist/{vector-store-PWIOQFTP.js → vector-store-V6UGEYWK.js} +2 -2
- package/dist/web/client/app.js +7 -0
- package/dist/web/client/app.js.br +0 -0
- package/dist/web/client/app.js.gz +0 -0
- package/dist/web/client/util.js +14 -23
- package/dist/web/client/util.js.br +0 -0
- package/dist/web/client/util.js.gz +0 -0
- package/dist/web/client/vendor/DOMPurify-LICENSE.txt +202 -0
- package/dist/web/client/vendor/purify.es.mjs +2722 -0
- package/dist/web/client/vendor/purify.es.mjs.br +0 -0
- package/dist/web/client/vendor/purify.es.mjs.gz +0 -0
- package/package.json +4 -2
- package/dist/chunk-4BKXL7SM.js +0 -98
- package/dist/chunk-6BUTA5VW.js +0 -38
- package/dist/chunk-IW3Q7AE5.js +0 -40
- package/dist/chunk-SKET65WZ.js +0 -96
- package/dist/doctor-cli-IOVSHBDI.js +0 -18
- package/dist/file-checkpoint-CGH6OJVI.js +0 -6
- package/dist/indexer-FPKY7LCT.js +0 -10
- package/dist/run-tests-EQCAMFQE.js +0 -10
- 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-
|
|
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-
|
|
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) |
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -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
|
|
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
|
};
|
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
} from "./chunk-RS7SKYTS.js";
|
|
5
5
|
import {
|
|
6
6
|
ConfigManager
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RBCKX2FZ.js";
|
|
8
8
|
import "./chunk-TU3L3PW7.js";
|
|
9
9
|
import {
|
|
10
10
|
atomicWriteFileSync
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
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-
|
|
9
|
+
} from "./chunk-6JHACXB3.js";
|
|
10
10
|
import "./chunk-KHYD3WXE.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-JL44HGZC.js";
|
|
12
12
|
import "./chunk-3FEZSLGI.js";
|
|
13
|
-
import "./chunk-
|
|
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-
|
|
8
|
+
} from "./chunk-M6YV5KSS.js";
|
|
9
9
|
import "./chunk-JV5N65KN.js";
|
|
10
10
|
import "./chunk-FSC6KEWU.js";
|
|
11
|
-
import "./chunk-
|
|
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-
|
|
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-
|
|
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,15 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import {
|
|
2
3
|
TEST_TIMEOUT
|
|
3
|
-
} from "./chunk-
|
|
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
|
|
12
|
-
var
|
|
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:
|
|
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 =
|
|
86
|
-
const cmd = existsSync(join(cwd,
|
|
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 (!
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
};
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-RS7SKYTS.js";
|
|
5
5
|
import {
|
|
6
6
|
atomicWriteFileSync
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BMLQPSHP.js";
|
|
8
8
|
import {
|
|
9
9
|
CONFIG_DIR_NAME,
|
|
10
10
|
PLUGINS_DIR_NAME
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-3CNPJLVI.js";
|
|
12
12
|
|
|
13
13
|
// src/diagnostics/tool-stats.ts
|
|
14
14
|
import { mkdirSync } from "fs";
|