jinzd-ai-cli 0.4.106 → 0.4.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{batch-PF2EB4BE.js → batch-A6F3UA3N.js} +2 -2
- package/dist/{chat-index-7OHUKJY5.js → chat-index-JBF4ZIQI.js} +1 -1
- package/dist/{chat-index-ADG2GPCC.js → chat-index-MY3DJTV3.js} +1 -1
- package/dist/{chunk-ANYYM4CF.js → chunk-7ZJN4KLV.js} +12 -1
- package/dist/{chunk-OSGGBJSW.js → chunk-A26EOV2J.js} +16 -2
- package/dist/{chunk-ML7ISZ7A.js → chunk-A3HZY7DA.js} +32 -11
- package/dist/{chunk-ITHFJSJQ.js → chunk-DNKI3JIL.js} +1 -1
- package/dist/{chunk-RSZ75E2W.js → chunk-IKX4D6C7.js} +3 -3
- package/dist/{chunk-W7ZG4KRQ.js → chunk-NN2B3UBA.js} +1 -1
- package/dist/{chunk-5S3PIG5O.js → chunk-WJ32NAW3.js} +12 -1
- package/dist/{chunk-RD4RCKIB.js → chunk-XWJTZWS7.js} +1 -1
- package/dist/{constants-FVJPGHS7.js → constants-QZWMYEMB.js} +1 -1
- package/dist/electron-server.js +79 -22
- package/dist/{hub-3KDAZUH4.js → hub-3HEMIJTM.js} +1 -1
- package/dist/index.js +13 -13
- package/dist/{run-tests-UZGGOXOS.js → run-tests-LWSTPCZY.js} +2 -2
- package/dist/{run-tests-6FVJND6Q.js → run-tests-VBJ5H3XR.js} +1 -1
- package/dist/{server-CWDCYN3N.js → server-6F2JF2JK.js} +39 -17
- package/dist/{server-5MJQOIZU.js → server-DSG3EYRA.js} +4 -4
- package/dist/{task-orchestrator-32DSRTMN.js → task-orchestrator-6WIYZPDB.js} +4 -4
- package/dist/web/client/app.js +61 -23
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ConfigManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-A26EOV2J.js";
|
|
5
5
|
import "./chunk-2ZD3YTVM.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-DNKI3JIL.js";
|
|
7
7
|
import "./chunk-PDX44BCA.js";
|
|
8
8
|
|
|
9
9
|
// src/cli/batch.ts
|
|
@@ -23,6 +23,11 @@ var DEFAULT_PATTERNS = [
|
|
|
23
23
|
{ kind: "db-uri-password", regex: /(\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp|mssql):\/\/[^:\s]+:)([^@\s]+)(@)/gi },
|
|
24
24
|
// Anthropic API keys
|
|
25
25
|
{ kind: "anthropic-key", regex: /(sk-ant-[a-zA-Z0-9_-]{90,})/g },
|
|
26
|
+
// L6 (v0.4.108): Zhipu / GLM API keys — `<24+ hex/base64-ish>.<32+>`
|
|
27
|
+
// Two segments separated by a dot, each safely identifiable by length
|
|
28
|
+
// and char class. Conservative on the lower bound so we don't eat
|
|
29
|
+
// version strings like `1.0.0` or filenames.
|
|
30
|
+
{ kind: "zhipu-key", regex: /\b([a-zA-Z0-9]{24,}\.[a-zA-Z0-9]{32,})\b/g },
|
|
26
31
|
// OpenAI / generic sk- keys — requires length ≥32 to avoid eating short identifiers
|
|
27
32
|
{ kind: "openai-key", regex: /(sk-(?:proj-)?[a-zA-Z0-9_-]{32,})/g },
|
|
28
33
|
// GitHub personal access tokens
|
|
@@ -46,18 +51,24 @@ var DEFAULT_PATTERNS = [
|
|
|
46
51
|
// Private key PEM blocks — catch the header+footer together
|
|
47
52
|
{ kind: "private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g }
|
|
48
53
|
];
|
|
54
|
+
var MAX_CUSTOM = 32;
|
|
55
|
+
var MAX_PATTERN_LEN = 500;
|
|
56
|
+
var SUSPICIOUS_REDOS = /\([^)]*[+*][^)]*\)\s*[+*{]/;
|
|
49
57
|
function render(placeholder, kind) {
|
|
50
58
|
return placeholder.replace("{kind}", kind);
|
|
51
59
|
}
|
|
52
60
|
function redactString(input, options) {
|
|
53
61
|
if (!options.enabled || !input) return { redacted: input, hits: [] };
|
|
54
62
|
const placeholder = options.placeholder ?? "[REDACTED:{kind}]";
|
|
63
|
+
const customSrcs = (options.customRegexes ?? []).slice(0, MAX_CUSTOM);
|
|
55
64
|
const patterns = [
|
|
56
65
|
...options.patterns ?? DEFAULT_PATTERNS,
|
|
57
|
-
...
|
|
66
|
+
...customSrcs.flatMap((src, i) => {
|
|
67
|
+
if (typeof src !== "string" || src.length === 0 || src.length > MAX_PATTERN_LEN) return [];
|
|
58
68
|
try {
|
|
59
69
|
const flags = src.match(/^\/.*\/([gimsuy]*)$/)?.[1] ?? "";
|
|
60
70
|
const body = src.replace(/^\/(.*)\/[gimsuy]*$/, "$1");
|
|
71
|
+
if (SUSPICIOUS_REDOS.test(body)) return [];
|
|
61
72
|
const regex = new RegExp(body, flags.includes("g") ? flags : flags + "g");
|
|
62
73
|
return [{ kind: `custom-${i}`, regex }];
|
|
63
74
|
} catch {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
CONFIG_FILE_NAME,
|
|
9
9
|
HISTORY_DIR_NAME,
|
|
10
10
|
PLUGINS_DIR_NAME
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-DNKI3JIL.js";
|
|
12
12
|
|
|
13
13
|
// src/config/config-manager.ts
|
|
14
14
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
@@ -268,9 +268,23 @@ ${err}`
|
|
|
268
268
|
/**
|
|
269
269
|
* 仅修改内存中的配置值,不持久化到磁盘。
|
|
270
270
|
* 用于 CLI 命令行参数覆盖(-p, -m, --no-stream),使其只对当前进程生效。
|
|
271
|
+
*
|
|
272
|
+
* M6 fix (v0.4.107):原版直接赋值不走 Zod,调用点必须自律确保来源可信
|
|
273
|
+
* (如 CLI flag、global config)。如果未来谁把外部输入接到这里,类型系统
|
|
274
|
+
* 不会阻止恶意值偷渡进 config。现在用 partial Zod parse 校验单字段,
|
|
275
|
+
* 失败时抛 ConfigError。setTransient 永远不接受外部不可信输入——这是
|
|
276
|
+
* 「调用方信任」契约的硬实现。
|
|
271
277
|
*/
|
|
272
278
|
setTransient(key, value) {
|
|
273
|
-
this.config[key]
|
|
279
|
+
const draft = { ...this.config, [key]: value };
|
|
280
|
+
const result = ConfigSchema.safeParse(draft);
|
|
281
|
+
if (!result.success) {
|
|
282
|
+
const firstErr = result.error.errors[0];
|
|
283
|
+
throw new ConfigError(
|
|
284
|
+
`Invalid transient config value for "${String(key)}": ${firstErr?.message ?? "validation failed"}`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
this.config = result.data;
|
|
274
288
|
}
|
|
275
289
|
isFirstRun() {
|
|
276
290
|
return !existsSync(this.configPath);
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-3BICTI5M.js";
|
|
6
6
|
import {
|
|
7
7
|
runTestsTool
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NN2B3UBA.js";
|
|
9
9
|
import {
|
|
10
10
|
EnvLoader,
|
|
11
11
|
NetworkError,
|
|
@@ -18,14 +18,14 @@ import {
|
|
|
18
18
|
SUBAGENT_ALLOWED_TOOLS,
|
|
19
19
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
20
20
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-DNKI3JIL.js";
|
|
22
22
|
import {
|
|
23
23
|
fileCheckpoints
|
|
24
24
|
} from "./chunk-4BKXL7SM.js";
|
|
25
25
|
import {
|
|
26
26
|
loadChatIndex,
|
|
27
27
|
searchChatMemory
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-7ZJN4KLV.js";
|
|
29
29
|
import {
|
|
30
30
|
indexProject
|
|
31
31
|
} from "./chunk-NHNWUBXB.js";
|
|
@@ -667,6 +667,20 @@ function createCell(cellType, content) {
|
|
|
667
667
|
|
|
668
668
|
// src/tools/builtin/read-file.ts
|
|
669
669
|
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
670
|
+
function getHardRefusal(normalizedPath) {
|
|
671
|
+
const home2 = homedir();
|
|
672
|
+
const p = normalizedPath.toLowerCase();
|
|
673
|
+
const base = basename(normalizedPath).toLowerCase();
|
|
674
|
+
if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "config.json") {
|
|
675
|
+
return `[Refused] Reading ${normalizedPath} is blocked because it contains all of your provider API keys.
|
|
676
|
+
If you genuinely need to inspect or edit it, open it manually outside ai-cli (e.g. via your editor).
|
|
677
|
+
For programmatic access, use \`aicli config\` or \`aicli config get <key>\` which never expose raw keys.`;
|
|
678
|
+
}
|
|
679
|
+
if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "users.json") {
|
|
680
|
+
return `[Refused] Reading ${normalizedPath} is blocked because it contains password hashes and the token-signing secret.`;
|
|
681
|
+
}
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
670
684
|
function getSensitiveWarning(normalizedPath) {
|
|
671
685
|
const home2 = homedir();
|
|
672
686
|
const p = normalizedPath.toLowerCase();
|
|
@@ -819,6 +833,8 @@ var readFileTool = {
|
|
|
819
833
|
const limitLines = typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : void 0;
|
|
820
834
|
if (!filePath) throw new ToolError("read_file", "path is required");
|
|
821
835
|
const normalizedPath = resolve2(filePath);
|
|
836
|
+
const refusal = getHardRefusal(normalizedPath);
|
|
837
|
+
if (refusal) return refusal;
|
|
822
838
|
if (!existsSync3(normalizedPath)) {
|
|
823
839
|
const suggestions = findSimilarFiles(filePath);
|
|
824
840
|
if (suggestions.length > 0) {
|
|
@@ -1136,21 +1152,26 @@ function simpleDiff(oldLines, newLines) {
|
|
|
1136
1152
|
|
|
1137
1153
|
// src/tools/hooks.ts
|
|
1138
1154
|
import { execSync } from "child_process";
|
|
1139
|
-
function
|
|
1140
|
-
|
|
1155
|
+
function rewriteTemplate(template, isWindows) {
|
|
1156
|
+
const ref = (name) => isWindows ? `%${name}%` : `$${name}`;
|
|
1157
|
+
return template.replace(/\{tool\}/g, ref("AICLI_HOOK_TOOL")).replace(/\{dangerLevel\}/g, ref("AICLI_HOOK_DANGER_LEVEL")).replace(/\{args\}/g, ref("AICLI_HOOK_ARGS")).replace(/\{status\}/g, ref("AICLI_HOOK_STATUS"));
|
|
1141
1158
|
}
|
|
1142
1159
|
function runHook(template, vars) {
|
|
1143
1160
|
if (!template) return;
|
|
1144
|
-
|
|
1145
|
-
cmd =
|
|
1146
|
-
cmd = cmd.replace(/\{dangerLevel\}/g, shellEscape(vars.dangerLevel ?? ""));
|
|
1147
|
-
cmd = cmd.replace(/\{args\}/g, shellEscape(vars.args ?? ""));
|
|
1148
|
-
cmd = cmd.replace(/\{status\}/g, shellEscape(vars.status ?? ""));
|
|
1161
|
+
const isWindows = process.platform === "win32";
|
|
1162
|
+
const cmd = rewriteTemplate(template, isWindows);
|
|
1149
1163
|
try {
|
|
1150
1164
|
execSync(cmd, {
|
|
1151
1165
|
timeout: 5e3,
|
|
1152
1166
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1153
|
-
encoding: "utf-8"
|
|
1167
|
+
encoding: "utf-8",
|
|
1168
|
+
env: {
|
|
1169
|
+
...process.env,
|
|
1170
|
+
AICLI_HOOK_TOOL: vars.tool,
|
|
1171
|
+
AICLI_HOOK_DANGER_LEVEL: vars.dangerLevel ?? "",
|
|
1172
|
+
AICLI_HOOK_ARGS: vars.args ?? "",
|
|
1173
|
+
AICLI_HOOK_STATUS: vars.status ?? ""
|
|
1174
|
+
}
|
|
1154
1175
|
});
|
|
1155
1176
|
} catch {
|
|
1156
1177
|
process.stderr.write(`\u26A0 Hook failed: ${cmd.slice(0, 100)}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
schemaToJsonSchema,
|
|
4
4
|
truncateForPersist
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-A3HZY7DA.js";
|
|
6
6
|
import {
|
|
7
7
|
AuthError,
|
|
8
8
|
ProviderError,
|
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
MCP_PROTOCOL_VERSION,
|
|
19
19
|
MCP_TOOL_PREFIX,
|
|
20
20
|
VERSION
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-DNKI3JIL.js";
|
|
22
22
|
import {
|
|
23
23
|
redactJson
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-7ZJN4KLV.js";
|
|
25
25
|
|
|
26
26
|
// src/providers/claude.ts
|
|
27
27
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -22,6 +22,11 @@ var DEFAULT_PATTERNS = [
|
|
|
22
22
|
{ kind: "db-uri-password", regex: /(\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp|mssql):\/\/[^:\s]+:)([^@\s]+)(@)/gi },
|
|
23
23
|
// Anthropic API keys
|
|
24
24
|
{ kind: "anthropic-key", regex: /(sk-ant-[a-zA-Z0-9_-]{90,})/g },
|
|
25
|
+
// L6 (v0.4.108): Zhipu / GLM API keys — `<24+ hex/base64-ish>.<32+>`
|
|
26
|
+
// Two segments separated by a dot, each safely identifiable by length
|
|
27
|
+
// and char class. Conservative on the lower bound so we don't eat
|
|
28
|
+
// version strings like `1.0.0` or filenames.
|
|
29
|
+
{ kind: "zhipu-key", regex: /\b([a-zA-Z0-9]{24,}\.[a-zA-Z0-9]{32,})\b/g },
|
|
25
30
|
// OpenAI / generic sk- keys — requires length ≥32 to avoid eating short identifiers
|
|
26
31
|
{ kind: "openai-key", regex: /(sk-(?:proj-)?[a-zA-Z0-9_-]{32,})/g },
|
|
27
32
|
// GitHub personal access tokens
|
|
@@ -45,18 +50,24 @@ var DEFAULT_PATTERNS = [
|
|
|
45
50
|
// Private key PEM blocks — catch the header+footer together
|
|
46
51
|
{ kind: "private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g }
|
|
47
52
|
];
|
|
53
|
+
var MAX_CUSTOM = 32;
|
|
54
|
+
var MAX_PATTERN_LEN = 500;
|
|
55
|
+
var SUSPICIOUS_REDOS = /\([^)]*[+*][^)]*\)\s*[+*{]/;
|
|
48
56
|
function render(placeholder, kind) {
|
|
49
57
|
return placeholder.replace("{kind}", kind);
|
|
50
58
|
}
|
|
51
59
|
function redactString(input, options) {
|
|
52
60
|
if (!options.enabled || !input) return { redacted: input, hits: [] };
|
|
53
61
|
const placeholder = options.placeholder ?? "[REDACTED:{kind}]";
|
|
62
|
+
const customSrcs = (options.customRegexes ?? []).slice(0, MAX_CUSTOM);
|
|
54
63
|
const patterns = [
|
|
55
64
|
...options.patterns ?? DEFAULT_PATTERNS,
|
|
56
|
-
...
|
|
65
|
+
...customSrcs.flatMap((src, i) => {
|
|
66
|
+
if (typeof src !== "string" || src.length === 0 || src.length > MAX_PATTERN_LEN) return [];
|
|
57
67
|
try {
|
|
58
68
|
const flags = src.match(/^\/.*\/([gimsuy]*)$/)?.[1] ?? "";
|
|
59
69
|
const body = src.replace(/^\/(.*)\/[gimsuy]*$/, "$1");
|
|
70
|
+
if (SUSPICIOUS_REDOS.test(body)) return [];
|
|
60
71
|
const regex = new RegExp(body, flags.includes("g") ? flags : flags + "g");
|
|
61
72
|
return [{ kind: `custom-${i}`, regex }];
|
|
62
73
|
} catch {
|
package/dist/electron-server.js
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
VERSION,
|
|
37
37
|
buildUserIdentityPrompt,
|
|
38
38
|
runTestsTool
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-XWJTZWS7.js";
|
|
40
40
|
import {
|
|
41
41
|
hasSemanticIndex,
|
|
42
42
|
semanticSearch
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
loadChatIndex,
|
|
50
50
|
redactJson,
|
|
51
51
|
searchChatMemory
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-WJ32NAW3.js";
|
|
53
53
|
import "./chunk-JV5N65KN.js";
|
|
54
54
|
import "./chunk-3RG5ZIWI.js";
|
|
55
55
|
|
|
@@ -421,9 +421,23 @@ ${err}`
|
|
|
421
421
|
/**
|
|
422
422
|
* 仅修改内存中的配置值,不持久化到磁盘。
|
|
423
423
|
* 用于 CLI 命令行参数覆盖(-p, -m, --no-stream),使其只对当前进程生效。
|
|
424
|
+
*
|
|
425
|
+
* M6 fix (v0.4.107):原版直接赋值不走 Zod,调用点必须自律确保来源可信
|
|
426
|
+
* (如 CLI flag、global config)。如果未来谁把外部输入接到这里,类型系统
|
|
427
|
+
* 不会阻止恶意值偷渡进 config。现在用 partial Zod parse 校验单字段,
|
|
428
|
+
* 失败时抛 ConfigError。setTransient 永远不接受外部不可信输入——这是
|
|
429
|
+
* 「调用方信任」契约的硬实现。
|
|
424
430
|
*/
|
|
425
431
|
setTransient(key, value) {
|
|
426
|
-
this.config[key]
|
|
432
|
+
const draft = { ...this.config, [key]: value };
|
|
433
|
+
const result = ConfigSchema.safeParse(draft);
|
|
434
|
+
if (!result.success) {
|
|
435
|
+
const firstErr = result.error.errors[0];
|
|
436
|
+
throw new ConfigError(
|
|
437
|
+
`Invalid transient config value for "${String(key)}": ${firstErr?.message ?? "validation failed"}`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
this.config = result.data;
|
|
427
441
|
}
|
|
428
442
|
isFirstRun() {
|
|
429
443
|
return !existsSync(this.configPath);
|
|
@@ -4059,6 +4073,20 @@ function createCell(cellType, content) {
|
|
|
4059
4073
|
|
|
4060
4074
|
// src/tools/builtin/read-file.ts
|
|
4061
4075
|
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
4076
|
+
function getHardRefusal(normalizedPath) {
|
|
4077
|
+
const home2 = homedir2();
|
|
4078
|
+
const p = normalizedPath.toLowerCase();
|
|
4079
|
+
const base = basename(normalizedPath).toLowerCase();
|
|
4080
|
+
if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "config.json") {
|
|
4081
|
+
return `[Refused] Reading ${normalizedPath} is blocked because it contains all of your provider API keys.
|
|
4082
|
+
If you genuinely need to inspect or edit it, open it manually outside ai-cli (e.g. via your editor).
|
|
4083
|
+
For programmatic access, use \`aicli config\` or \`aicli config get <key>\` which never expose raw keys.`;
|
|
4084
|
+
}
|
|
4085
|
+
if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "users.json") {
|
|
4086
|
+
return `[Refused] Reading ${normalizedPath} is blocked because it contains password hashes and the token-signing secret.`;
|
|
4087
|
+
}
|
|
4088
|
+
return null;
|
|
4089
|
+
}
|
|
4062
4090
|
function getSensitiveWarning(normalizedPath) {
|
|
4063
4091
|
const home2 = homedir2();
|
|
4064
4092
|
const p = normalizedPath.toLowerCase();
|
|
@@ -4211,6 +4239,8 @@ var readFileTool = {
|
|
|
4211
4239
|
const limitLines = typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : void 0;
|
|
4212
4240
|
if (!filePath) throw new ToolError("read_file", "path is required");
|
|
4213
4241
|
const normalizedPath = resolve2(filePath);
|
|
4242
|
+
const refusal = getHardRefusal(normalizedPath);
|
|
4243
|
+
if (refusal) return refusal;
|
|
4214
4244
|
if (!existsSync5(normalizedPath)) {
|
|
4215
4245
|
const suggestions = findSimilarFiles(filePath);
|
|
4216
4246
|
if (suggestions.length > 0) {
|
|
@@ -4528,21 +4558,26 @@ function simpleDiff(oldLines, newLines) {
|
|
|
4528
4558
|
|
|
4529
4559
|
// src/tools/hooks.ts
|
|
4530
4560
|
import { execSync } from "child_process";
|
|
4531
|
-
function
|
|
4532
|
-
|
|
4561
|
+
function rewriteTemplate(template, isWindows) {
|
|
4562
|
+
const ref = (name) => isWindows ? `%${name}%` : `$${name}`;
|
|
4563
|
+
return template.replace(/\{tool\}/g, ref("AICLI_HOOK_TOOL")).replace(/\{dangerLevel\}/g, ref("AICLI_HOOK_DANGER_LEVEL")).replace(/\{args\}/g, ref("AICLI_HOOK_ARGS")).replace(/\{status\}/g, ref("AICLI_HOOK_STATUS"));
|
|
4533
4564
|
}
|
|
4534
4565
|
function runHook(template, vars) {
|
|
4535
4566
|
if (!template) return;
|
|
4536
|
-
|
|
4537
|
-
cmd =
|
|
4538
|
-
cmd = cmd.replace(/\{dangerLevel\}/g, shellEscape(vars.dangerLevel ?? ""));
|
|
4539
|
-
cmd = cmd.replace(/\{args\}/g, shellEscape(vars.args ?? ""));
|
|
4540
|
-
cmd = cmd.replace(/\{status\}/g, shellEscape(vars.status ?? ""));
|
|
4567
|
+
const isWindows = process.platform === "win32";
|
|
4568
|
+
const cmd = rewriteTemplate(template, isWindows);
|
|
4541
4569
|
try {
|
|
4542
4570
|
execSync(cmd, {
|
|
4543
4571
|
timeout: 5e3,
|
|
4544
4572
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4545
|
-
encoding: "utf-8"
|
|
4573
|
+
encoding: "utf-8",
|
|
4574
|
+
env: {
|
|
4575
|
+
...process.env,
|
|
4576
|
+
AICLI_HOOK_TOOL: vars.tool,
|
|
4577
|
+
AICLI_HOOK_DANGER_LEVEL: vars.dangerLevel ?? "",
|
|
4578
|
+
AICLI_HOOK_ARGS: vars.args ?? "",
|
|
4579
|
+
AICLI_HOOK_STATUS: vars.status ?? ""
|
|
4580
|
+
}
|
|
4546
4581
|
});
|
|
4547
4582
|
} catch {
|
|
4548
4583
|
process.stderr.write(`\u26A0 Hook failed: ${cmd.slice(0, 100)}
|
|
@@ -11754,7 +11789,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
11754
11789
|
case "test": {
|
|
11755
11790
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
11756
11791
|
try {
|
|
11757
|
-
const { executeTests } = await import("./run-tests-
|
|
11792
|
+
const { executeTests } = await import("./run-tests-VBJ5H3XR.js");
|
|
11758
11793
|
const argStr = args.join(" ").trim();
|
|
11759
11794
|
let testArgs = {};
|
|
11760
11795
|
if (argStr) {
|
|
@@ -12278,7 +12313,7 @@ Add .md files to create commands.` });
|
|
|
12278
12313
|
return;
|
|
12279
12314
|
}
|
|
12280
12315
|
try {
|
|
12281
|
-
const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-
|
|
12316
|
+
const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-MY3DJTV3.js");
|
|
12282
12317
|
const loaded = loadChatIndex2();
|
|
12283
12318
|
if (!loaded || loaded.idx.chunks.length === 0) {
|
|
12284
12319
|
this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
|
|
@@ -12314,7 +12349,7 @@ Add .md files to create commands.` });
|
|
|
12314
12349
|
}
|
|
12315
12350
|
async handleMemoryStatus() {
|
|
12316
12351
|
try {
|
|
12317
|
-
const { getChatIndexStatus } = await import("./chat-index-
|
|
12352
|
+
const { getChatIndexStatus } = await import("./chat-index-MY3DJTV3.js");
|
|
12318
12353
|
const s = getChatIndexStatus();
|
|
12319
12354
|
this.send({
|
|
12320
12355
|
type: "memory_status",
|
|
@@ -12339,7 +12374,7 @@ Add .md files to create commands.` });
|
|
|
12339
12374
|
type: "info",
|
|
12340
12375
|
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"
|
|
12341
12376
|
});
|
|
12342
|
-
const { buildChatIndex } = await import("./chat-index-
|
|
12377
|
+
const { buildChatIndex } = await import("./chat-index-MY3DJTV3.js");
|
|
12343
12378
|
const stats = await buildChatIndex({
|
|
12344
12379
|
full,
|
|
12345
12380
|
onProgress: (p) => {
|
|
@@ -13117,7 +13152,25 @@ async function startWebServer(options = {}) {
|
|
|
13117
13152
|
const token = authManager.login(username, password);
|
|
13118
13153
|
console.log(` \u2713 User registered via API: ${username}${firstRun ? " (first-run)" : ""}`);
|
|
13119
13154
|
res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
|
|
13120
|
-
res.json({ success: true,
|
|
13155
|
+
res.json({ success: true, username });
|
|
13156
|
+
});
|
|
13157
|
+
app.post("/api/auth/login", (req, res) => {
|
|
13158
|
+
const { username, password } = req.body ?? {};
|
|
13159
|
+
if (!username || !password) {
|
|
13160
|
+
res.status(400).json({ error: "Username and password required" });
|
|
13161
|
+
return;
|
|
13162
|
+
}
|
|
13163
|
+
const token = authManager.login(username, password);
|
|
13164
|
+
if (!token) {
|
|
13165
|
+
res.status(401).json({ error: "Invalid username or password" });
|
|
13166
|
+
return;
|
|
13167
|
+
}
|
|
13168
|
+
res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
|
|
13169
|
+
res.json({ success: true, username });
|
|
13170
|
+
});
|
|
13171
|
+
app.post("/api/auth/logout", (_req, res) => {
|
|
13172
|
+
res.clearCookie("aicli_token", { httpOnly: true, sameSite: "strict" });
|
|
13173
|
+
res.json({ success: true });
|
|
13121
13174
|
});
|
|
13122
13175
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
13123
13176
|
const cwd = process.cwd();
|
|
@@ -13358,7 +13411,7 @@ async function startWebServer(options = {}) {
|
|
|
13358
13411
|
handlers.set(tabId, handler);
|
|
13359
13412
|
clearPreAuthTimer();
|
|
13360
13413
|
console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
13361
|
-
ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username
|
|
13414
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username }));
|
|
13362
13415
|
return;
|
|
13363
13416
|
}
|
|
13364
13417
|
if (action === "token") {
|
|
@@ -13389,7 +13442,7 @@ async function startWebServer(options = {}) {
|
|
|
13389
13442
|
handlers.set(tabId, handler);
|
|
13390
13443
|
clearPreAuthTimer();
|
|
13391
13444
|
console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
13392
|
-
ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username
|
|
13445
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username }));
|
|
13393
13446
|
return;
|
|
13394
13447
|
}
|
|
13395
13448
|
ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Unknown auth action" }));
|
|
@@ -13502,10 +13555,14 @@ function parseCookie(cookie) {
|
|
|
13502
13555
|
}
|
|
13503
13556
|
async function openBrowser(url) {
|
|
13504
13557
|
try {
|
|
13505
|
-
const {
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
})
|
|
13558
|
+
const { spawn: spawn5 } = await import("child_process");
|
|
13559
|
+
if (process.platform === "win32") {
|
|
13560
|
+
spawn5("cmd", ["/c", "start", '""', url], { detached: true, stdio: "ignore" }).unref();
|
|
13561
|
+
} else if (process.platform === "darwin") {
|
|
13562
|
+
spawn5("open", [url], { detached: true, stdio: "ignore" }).unref();
|
|
13563
|
+
} else {
|
|
13564
|
+
spawn5("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
|
|
13565
|
+
}
|
|
13509
13566
|
} catch {
|
|
13510
13567
|
}
|
|
13511
13568
|
}
|
|
@@ -386,7 +386,7 @@ ${content}`);
|
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
389
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
389
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-6WIYZPDB.js");
|
|
390
390
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
391
391
|
let interrupted = false;
|
|
392
392
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -25,10 +25,10 @@ import {
|
|
|
25
25
|
saveDevState,
|
|
26
26
|
sessionHasMeaningfulContent,
|
|
27
27
|
setupProxy
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-IKX4D6C7.js";
|
|
29
29
|
import {
|
|
30
30
|
ConfigManager
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-A26EOV2J.js";
|
|
32
32
|
import {
|
|
33
33
|
ToolExecutor,
|
|
34
34
|
ToolRegistry,
|
|
@@ -47,10 +47,10 @@ import {
|
|
|
47
47
|
spawnAgentContext,
|
|
48
48
|
theme,
|
|
49
49
|
undoStack
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-A3HZY7DA.js";
|
|
51
51
|
import "./chunk-3BICTI5M.js";
|
|
52
52
|
import "./chunk-2DXY7UGF.js";
|
|
53
|
-
import "./chunk-
|
|
53
|
+
import "./chunk-NN2B3UBA.js";
|
|
54
54
|
import "./chunk-2ZD3YTVM.js";
|
|
55
55
|
import {
|
|
56
56
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
SKILLS_DIR_NAME,
|
|
74
74
|
VERSION,
|
|
75
75
|
buildUserIdentityPrompt
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-DNKI3JIL.js";
|
|
77
77
|
import {
|
|
78
78
|
formatGitContextForPrompt,
|
|
79
79
|
getGitContext,
|
|
@@ -89,7 +89,7 @@ import {
|
|
|
89
89
|
getChatIndexStatus,
|
|
90
90
|
scanString,
|
|
91
91
|
searchChatMemory
|
|
92
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-7ZJN4KLV.js";
|
|
93
93
|
import "./chunk-KHYD3WXE.js";
|
|
94
94
|
import "./chunk-NHNWUBXB.js";
|
|
95
95
|
import "./chunk-6VRJGH25.js";
|
|
@@ -1594,7 +1594,7 @@ ${text}
|
|
|
1594
1594
|
const { join: join6 } = await import("path");
|
|
1595
1595
|
const { existsSync: existsSync6 } = await import("fs");
|
|
1596
1596
|
const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
|
|
1597
|
-
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-
|
|
1597
|
+
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-QZWMYEMB.js");
|
|
1598
1598
|
const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
|
|
1599
1599
|
const cwd = process.cwd();
|
|
1600
1600
|
const projectRoot = getGitRoot2(cwd) ?? cwd;
|
|
@@ -2644,7 +2644,7 @@ ${hint}` : "")
|
|
|
2644
2644
|
usage: "/test [command|filter]",
|
|
2645
2645
|
async execute(args, ctx) {
|
|
2646
2646
|
try {
|
|
2647
|
-
const { executeTests } = await import("./run-tests-
|
|
2647
|
+
const { executeTests } = await import("./run-tests-LWSTPCZY.js");
|
|
2648
2648
|
const argStr = args.join(" ").trim();
|
|
2649
2649
|
let testArgs = {};
|
|
2650
2650
|
if (argStr) {
|
|
@@ -5068,7 +5068,7 @@ Session '${this.resumeSessionId}' not found.
|
|
|
5068
5068
|
})();
|
|
5069
5069
|
void (async () => {
|
|
5070
5070
|
try {
|
|
5071
|
-
const { getChatIndexStatus: getChatIndexStatus2, buildChatIndex: buildChatIndex2 } = await import("./chat-index-
|
|
5071
|
+
const { getChatIndexStatus: getChatIndexStatus2, buildChatIndex: buildChatIndex2 } = await import("./chat-index-JBF4ZIQI.js");
|
|
5072
5072
|
const initial = getChatIndexStatus2();
|
|
5073
5073
|
this.chatMemoryStatus = {
|
|
5074
5074
|
exists: initial.exists,
|
|
@@ -6800,7 +6800,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
6800
6800
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
6801
6801
|
process.exit(1);
|
|
6802
6802
|
}
|
|
6803
|
-
const { startWebServer } = await import("./server-
|
|
6803
|
+
const { startWebServer } = await import("./server-6F2JF2JK.js");
|
|
6804
6804
|
await startWebServer({ port, host: options.host });
|
|
6805
6805
|
});
|
|
6806
6806
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -6923,7 +6923,7 @@ program.command("sessions").description("List recent conversation sessions").act
|
|
|
6923
6923
|
});
|
|
6924
6924
|
program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
|
|
6925
6925
|
try {
|
|
6926
|
-
const batch = await import("./batch-
|
|
6926
|
+
const batch = await import("./batch-A6F3UA3N.js");
|
|
6927
6927
|
switch (action) {
|
|
6928
6928
|
case "submit":
|
|
6929
6929
|
if (!arg) {
|
|
@@ -6966,7 +6966,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
|
|
|
6966
6966
|
}
|
|
6967
6967
|
});
|
|
6968
6968
|
program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
|
|
6969
|
-
const { startMcpServer } = await import("./server-
|
|
6969
|
+
const { startMcpServer } = await import("./server-DSG3EYRA.js");
|
|
6970
6970
|
await startMcpServer({
|
|
6971
6971
|
allowDestructive: !!options.allowDestructive,
|
|
6972
6972
|
allowOutsideCwd: !!options.allowOutsideCwd,
|
|
@@ -7093,7 +7093,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
7093
7093
|
}),
|
|
7094
7094
|
config.get("customProviders")
|
|
7095
7095
|
);
|
|
7096
|
-
const { startHub } = await import("./hub-
|
|
7096
|
+
const { startHub } = await import("./hub-3HEMIJTM.js");
|
|
7097
7097
|
await startHub(
|
|
7098
7098
|
{
|
|
7099
7099
|
topic: topic ?? "",
|
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
loadDevState,
|
|
19
19
|
persistToolRound,
|
|
20
20
|
setupProxy
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-IKX4D6C7.js";
|
|
22
22
|
import {
|
|
23
23
|
ConfigManager
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-A26EOV2J.js";
|
|
25
25
|
import {
|
|
26
26
|
ToolExecutor,
|
|
27
27
|
ToolRegistry,
|
|
@@ -39,10 +39,10 @@ import {
|
|
|
39
39
|
spawnAgentContext,
|
|
40
40
|
truncateOutput,
|
|
41
41
|
undoStack
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-A3HZY7DA.js";
|
|
43
43
|
import "./chunk-3BICTI5M.js";
|
|
44
44
|
import "./chunk-2DXY7UGF.js";
|
|
45
|
-
import "./chunk-
|
|
45
|
+
import "./chunk-NN2B3UBA.js";
|
|
46
46
|
import "./chunk-2ZD3YTVM.js";
|
|
47
47
|
import {
|
|
48
48
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -62,14 +62,14 @@ import {
|
|
|
62
62
|
SKILLS_DIR_NAME,
|
|
63
63
|
VERSION,
|
|
64
64
|
buildUserIdentityPrompt
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-DNKI3JIL.js";
|
|
66
66
|
import {
|
|
67
67
|
formatGitContextForPrompt,
|
|
68
68
|
getGitContext,
|
|
69
69
|
getGitRoot
|
|
70
70
|
} from "./chunk-HOSJZMQS.js";
|
|
71
71
|
import "./chunk-4BKXL7SM.js";
|
|
72
|
-
import "./chunk-
|
|
72
|
+
import "./chunk-7ZJN4KLV.js";
|
|
73
73
|
import "./chunk-KHYD3WXE.js";
|
|
74
74
|
import "./chunk-NHNWUBXB.js";
|
|
75
75
|
import "./chunk-6VRJGH25.js";
|
|
@@ -2383,7 +2383,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2383
2383
|
case "test": {
|
|
2384
2384
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2385
2385
|
try {
|
|
2386
|
-
const { executeTests } = await import("./run-tests-
|
|
2386
|
+
const { executeTests } = await import("./run-tests-LWSTPCZY.js");
|
|
2387
2387
|
const argStr = args.join(" ").trim();
|
|
2388
2388
|
let testArgs = {};
|
|
2389
2389
|
if (argStr) {
|
|
@@ -2907,7 +2907,7 @@ Add .md files to create commands.` });
|
|
|
2907
2907
|
return;
|
|
2908
2908
|
}
|
|
2909
2909
|
try {
|
|
2910
|
-
const { searchChatMemory, loadChatIndex } = await import("./chat-index-
|
|
2910
|
+
const { searchChatMemory, loadChatIndex } = await import("./chat-index-JBF4ZIQI.js");
|
|
2911
2911
|
const loaded = loadChatIndex();
|
|
2912
2912
|
if (!loaded || loaded.idx.chunks.length === 0) {
|
|
2913
2913
|
this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
|
|
@@ -2943,7 +2943,7 @@ Add .md files to create commands.` });
|
|
|
2943
2943
|
}
|
|
2944
2944
|
async handleMemoryStatus() {
|
|
2945
2945
|
try {
|
|
2946
|
-
const { getChatIndexStatus } = await import("./chat-index-
|
|
2946
|
+
const { getChatIndexStatus } = await import("./chat-index-JBF4ZIQI.js");
|
|
2947
2947
|
const s = getChatIndexStatus();
|
|
2948
2948
|
this.send({
|
|
2949
2949
|
type: "memory_status",
|
|
@@ -2968,7 +2968,7 @@ Add .md files to create commands.` });
|
|
|
2968
2968
|
type: "info",
|
|
2969
2969
|
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"
|
|
2970
2970
|
});
|
|
2971
|
-
const { buildChatIndex } = await import("./chat-index-
|
|
2971
|
+
const { buildChatIndex } = await import("./chat-index-JBF4ZIQI.js");
|
|
2972
2972
|
const stats = await buildChatIndex({
|
|
2973
2973
|
full,
|
|
2974
2974
|
onProgress: (p) => {
|
|
@@ -3524,7 +3524,25 @@ async function startWebServer(options = {}) {
|
|
|
3524
3524
|
const token = authManager.login(username, password);
|
|
3525
3525
|
console.log(` \u2713 User registered via API: ${username}${firstRun ? " (first-run)" : ""}`);
|
|
3526
3526
|
res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
|
|
3527
|
-
res.json({ success: true,
|
|
3527
|
+
res.json({ success: true, username });
|
|
3528
|
+
});
|
|
3529
|
+
app.post("/api/auth/login", (req, res) => {
|
|
3530
|
+
const { username, password } = req.body ?? {};
|
|
3531
|
+
if (!username || !password) {
|
|
3532
|
+
res.status(400).json({ error: "Username and password required" });
|
|
3533
|
+
return;
|
|
3534
|
+
}
|
|
3535
|
+
const token = authManager.login(username, password);
|
|
3536
|
+
if (!token) {
|
|
3537
|
+
res.status(401).json({ error: "Invalid username or password" });
|
|
3538
|
+
return;
|
|
3539
|
+
}
|
|
3540
|
+
res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
|
|
3541
|
+
res.json({ success: true, username });
|
|
3542
|
+
});
|
|
3543
|
+
app.post("/api/auth/logout", (_req, res) => {
|
|
3544
|
+
res.clearCookie("aicli_token", { httpOnly: true, sameSite: "strict" });
|
|
3545
|
+
res.json({ success: true });
|
|
3528
3546
|
});
|
|
3529
3547
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
3530
3548
|
const cwd = process.cwd();
|
|
@@ -3765,7 +3783,7 @@ async function startWebServer(options = {}) {
|
|
|
3765
3783
|
handlers.set(tabId, handler);
|
|
3766
3784
|
clearPreAuthTimer();
|
|
3767
3785
|
console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
3768
|
-
ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username
|
|
3786
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username }));
|
|
3769
3787
|
return;
|
|
3770
3788
|
}
|
|
3771
3789
|
if (action === "token") {
|
|
@@ -3796,7 +3814,7 @@ async function startWebServer(options = {}) {
|
|
|
3796
3814
|
handlers.set(tabId, handler);
|
|
3797
3815
|
clearPreAuthTimer();
|
|
3798
3816
|
console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
3799
|
-
ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username
|
|
3817
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username }));
|
|
3800
3818
|
return;
|
|
3801
3819
|
}
|
|
3802
3820
|
ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Unknown auth action" }));
|
|
@@ -3909,10 +3927,14 @@ function parseCookie(cookie) {
|
|
|
3909
3927
|
}
|
|
3910
3928
|
async function openBrowser(url) {
|
|
3911
3929
|
try {
|
|
3912
|
-
const {
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
})
|
|
3930
|
+
const { spawn } = await import("child_process");
|
|
3931
|
+
if (process.platform === "win32") {
|
|
3932
|
+
spawn("cmd", ["/c", "start", '""', url], { detached: true, stdio: "ignore" }).unref();
|
|
3933
|
+
} else if (process.platform === "darwin") {
|
|
3934
|
+
spawn("open", [url], { detached: true, stdio: "ignore" }).unref();
|
|
3935
|
+
} else {
|
|
3936
|
+
spawn("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
|
|
3937
|
+
}
|
|
3916
3938
|
} catch {
|
|
3917
3939
|
}
|
|
3918
3940
|
}
|
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
ToolRegistry,
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
schemaToJsonSchema
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-A3HZY7DA.js";
|
|
7
7
|
import "./chunk-3BICTI5M.js";
|
|
8
8
|
import "./chunk-2DXY7UGF.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-NN2B3UBA.js";
|
|
10
10
|
import "./chunk-2ZD3YTVM.js";
|
|
11
11
|
import {
|
|
12
12
|
VERSION
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-DNKI3JIL.js";
|
|
14
14
|
import "./chunk-4BKXL7SM.js";
|
|
15
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-7ZJN4KLV.js";
|
|
16
16
|
import "./chunk-KHYD3WXE.js";
|
|
17
17
|
import "./chunk-NHNWUBXB.js";
|
|
18
18
|
import "./chunk-6VRJGH25.js";
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-A3HZY7DA.js";
|
|
8
8
|
import "./chunk-3BICTI5M.js";
|
|
9
9
|
import "./chunk-2DXY7UGF.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-NN2B3UBA.js";
|
|
11
11
|
import "./chunk-2ZD3YTVM.js";
|
|
12
12
|
import {
|
|
13
13
|
SUBAGENT_ALLOWED_TOOLS
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-DNKI3JIL.js";
|
|
15
15
|
import "./chunk-4BKXL7SM.js";
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-7ZJN4KLV.js";
|
|
17
17
|
import "./chunk-KHYD3WXE.js";
|
|
18
18
|
import "./chunk-NHNWUBXB.js";
|
|
19
19
|
import "./chunk-6VRJGH25.js";
|
package/dist/web/client/app.js
CHANGED
|
@@ -8,7 +8,13 @@
|
|
|
8
8
|
let ws = null;
|
|
9
9
|
let connected = false;
|
|
10
10
|
let processing = false;
|
|
11
|
-
|
|
11
|
+
// M4 fix (v0.4.107): the auth token now lives only in an httpOnly cookie.
|
|
12
|
+
// `authToken` remains as a legacy variable so existing call sites compile,
|
|
13
|
+
// but it should always be empty — the cookie is what authenticates the WS
|
|
14
|
+
// upgrade and HTTP requests via `credentials: 'same-origin'`.
|
|
15
|
+
let authToken = '';
|
|
16
|
+
// Clear any legacy localStorage token from an older client.
|
|
17
|
+
try { localStorage.removeItem('aicli-auth-token'); } catch {}
|
|
12
18
|
let authUsername = localStorage.getItem('aicli-auth-user') || '';
|
|
13
19
|
let authMode = 'login'; // 'login' or 'register'
|
|
14
20
|
let currentAssistantEl = null;
|
|
@@ -2718,11 +2724,15 @@ function toggleAuthMode() {
|
|
|
2718
2724
|
updateAuthForm();
|
|
2719
2725
|
}
|
|
2720
2726
|
|
|
2721
|
-
|
|
2727
|
+
// M4 fix (v0.4.107): auth now goes through HTTP /api/auth/{login,register}
|
|
2728
|
+
// so the server can set an httpOnly cookie. The WS upgrade automatically
|
|
2729
|
+
// forwards the cookie. Token is no longer in JSON / localStorage / JS-cookie.
|
|
2730
|
+
async function handleAuth(event) {
|
|
2722
2731
|
event.preventDefault();
|
|
2723
2732
|
const username = document.getElementById('auth-username').value.trim();
|
|
2724
2733
|
const password = document.getElementById('auth-password').value;
|
|
2725
2734
|
const errorEl = document.getElementById('auth-error');
|
|
2735
|
+
const submitBtn = document.getElementById('auth-submit');
|
|
2726
2736
|
|
|
2727
2737
|
if (!username || !password) {
|
|
2728
2738
|
errorEl.textContent = 'Please enter username and password';
|
|
@@ -2731,32 +2741,57 @@ function handleAuth(event) {
|
|
|
2731
2741
|
}
|
|
2732
2742
|
|
|
2733
2743
|
errorEl.classList.add('hidden');
|
|
2734
|
-
|
|
2744
|
+
submitBtn.disabled = true;
|
|
2735
2745
|
|
|
2736
|
-
|
|
2746
|
+
try {
|
|
2747
|
+
const path = authMode === 'register' ? '/api/auth/register' : '/api/auth/login';
|
|
2748
|
+
const resp = await fetch(path, {
|
|
2749
|
+
method: 'POST',
|
|
2750
|
+
headers: { 'content-type': 'application/json' },
|
|
2751
|
+
credentials: 'same-origin', // ensure Set-Cookie is honored
|
|
2752
|
+
body: JSON.stringify({ username, password }),
|
|
2753
|
+
});
|
|
2754
|
+
const data = await resp.json().catch(() => ({}));
|
|
2755
|
+
if (!resp.ok || !data.success) {
|
|
2756
|
+
errorEl.textContent = data.error || `Authentication failed (HTTP ${resp.status})`;
|
|
2757
|
+
errorEl.classList.remove('hidden');
|
|
2758
|
+
submitBtn.disabled = false;
|
|
2759
|
+
return;
|
|
2760
|
+
}
|
|
2761
|
+
authUsername = data.username;
|
|
2762
|
+
// Cookie is httpOnly — we cannot read it from JS, and that's the point.
|
|
2763
|
+
// Drop any legacy localStorage token (M4 cleanup).
|
|
2764
|
+
localStorage.removeItem('aicli-auth-token');
|
|
2765
|
+
localStorage.setItem('aicli-auth-user', data.username);
|
|
2766
|
+
submitBtn.disabled = false;
|
|
2767
|
+
hideAuthScreen();
|
|
2768
|
+
// Reconnect WS so the upgrade request sends the new cookie.
|
|
2769
|
+
if (ws) ws.close();
|
|
2770
|
+
} catch (err) {
|
|
2771
|
+
errorEl.textContent = (err && err.message) ? err.message : 'Network error';
|
|
2772
|
+
errorEl.classList.remove('hidden');
|
|
2773
|
+
submitBtn.disabled = false;
|
|
2774
|
+
}
|
|
2737
2775
|
}
|
|
2738
2776
|
|
|
2777
|
+
// Legacy WS auth_result handler — kept for backwards-compat with clients/
|
|
2778
|
+
// flows that still hit the WS auth path. Token field, if present, is ignored.
|
|
2739
2779
|
function handleAuthResult(msg) {
|
|
2740
2780
|
const errorEl = document.getElementById('auth-error');
|
|
2741
2781
|
const submitBtn = document.getElementById('auth-submit');
|
|
2742
|
-
submitBtn.disabled = false;
|
|
2782
|
+
if (submitBtn) submitBtn.disabled = false;
|
|
2743
2783
|
|
|
2744
2784
|
if (msg.success) {
|
|
2745
|
-
authToken = msg.token;
|
|
2746
2785
|
authUsername = msg.username;
|
|
2747
|
-
localStorage.
|
|
2786
|
+
localStorage.removeItem('aicli-auth-token'); // M4: never store token
|
|
2748
2787
|
localStorage.setItem('aicli-auth-user', msg.username);
|
|
2749
|
-
// Set cookie for secure WebSocket auth (httpOnly cookie set by server on HTTP login,
|
|
2750
|
-
// but for WS-only login, set a JS cookie as fallback)
|
|
2751
|
-
if (msg.setCookie && msg.token) {
|
|
2752
|
-
document.cookie = `aicli_token=${encodeURIComponent(msg.token)}; path=/; max-age=${7 * 24 * 3600}; SameSite=Strict`;
|
|
2753
|
-
}
|
|
2754
2788
|
hideAuthScreen();
|
|
2755
|
-
// Request session list now that we're authenticated
|
|
2756
2789
|
requestSessionList();
|
|
2757
2790
|
} else {
|
|
2758
|
-
errorEl
|
|
2759
|
-
|
|
2791
|
+
if (errorEl) {
|
|
2792
|
+
errorEl.textContent = msg.error || 'Authentication failed';
|
|
2793
|
+
errorEl.classList.remove('hidden');
|
|
2794
|
+
}
|
|
2760
2795
|
}
|
|
2761
2796
|
}
|
|
2762
2797
|
|
|
@@ -2774,14 +2809,16 @@ function updateUserMenu() {
|
|
|
2774
2809
|
}
|
|
2775
2810
|
}
|
|
2776
2811
|
|
|
2777
|
-
function handleLogout() {
|
|
2812
|
+
async function handleLogout() {
|
|
2813
|
+
// M4 fix (v0.4.107): only the server can clear an httpOnly cookie.
|
|
2814
|
+
try {
|
|
2815
|
+
await fetch('/api/auth/logout', { method: 'POST', credentials: 'same-origin' });
|
|
2816
|
+
} catch { /* ignore — close WS anyway */ }
|
|
2778
2817
|
authToken = '';
|
|
2779
2818
|
authUsername = '';
|
|
2780
|
-
localStorage.removeItem('aicli-auth-token');
|
|
2781
|
-
document.cookie = 'aicli_token=; path=/; max-age=0'; // Clear auth cookie
|
|
2819
|
+
localStorage.removeItem('aicli-auth-token'); // legacy cleanup
|
|
2782
2820
|
localStorage.removeItem('aicli-auth-user');
|
|
2783
2821
|
sessionStorage.removeItem('aicli-active-session');
|
|
2784
|
-
// Reconnect — server will send auth_required
|
|
2785
2822
|
if (ws) ws.close();
|
|
2786
2823
|
}
|
|
2787
2824
|
|
|
@@ -2849,9 +2886,12 @@ function showEnableAuthDialog() {
|
|
|
2849
2886
|
const password = prompt('Password (min 4 chars):');
|
|
2850
2887
|
if (!password) return;
|
|
2851
2888
|
|
|
2889
|
+
// M4 fix (v0.4.107): credentials:'same-origin' lets the httpOnly cookie
|
|
2890
|
+
// come back; we no longer touch the token.
|
|
2852
2891
|
fetch('/api/auth/register', {
|
|
2853
2892
|
method: 'POST',
|
|
2854
2893
|
headers: { 'Content-Type': 'application/json' },
|
|
2894
|
+
credentials: 'same-origin',
|
|
2855
2895
|
body: JSON.stringify({ username, password }),
|
|
2856
2896
|
})
|
|
2857
2897
|
.then(r => r.json())
|
|
@@ -2860,14 +2900,12 @@ function showEnableAuthDialog() {
|
|
|
2860
2900
|
alert('Error: ' + data.error);
|
|
2861
2901
|
return;
|
|
2862
2902
|
}
|
|
2863
|
-
// Save token and reconnect with auth
|
|
2864
|
-
authToken = data.token;
|
|
2865
2903
|
authUsername = data.username;
|
|
2866
|
-
localStorage.
|
|
2904
|
+
localStorage.removeItem('aicli-auth-token');
|
|
2867
2905
|
localStorage.setItem('aicli-auth-user', data.username);
|
|
2868
2906
|
document.getElementById('btn-enable-auth').classList.add('hidden');
|
|
2869
2907
|
updateUserMenu();
|
|
2870
|
-
// Reconnect
|
|
2908
|
+
// Reconnect — WS upgrade will pick up the new httpOnly cookie.
|
|
2871
2909
|
if (ws) ws.close();
|
|
2872
2910
|
alert('✓ Auth enabled! User "' + data.username + '" created. Other users can register from the login screen.');
|
|
2873
2911
|
})
|