jinzd-ai-cli 0.4.204 → 0.4.206
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 +1 -1
- package/dist/{batch-ZP52GVVD.js → batch-6PORMJWT.js} +2 -2
- package/dist/{chunk-JLZ5QA2I.js → chunk-2WV6AGHM.js} +1 -1
- package/dist/{chunk-26YWVRLT.js → chunk-BQIV4ZQT.js} +1 -1
- package/dist/{chunk-T5RTPF2Q.js → chunk-KJHMRAJU.js} +1 -1
- package/dist/{chunk-PIZWD5JN.js → chunk-KOU4KX7J.js} +2 -2
- package/dist/{chunk-UZISJ3KZ.js → chunk-QMXC327F.js} +3 -3
- package/dist/{chunk-SCQOEDVL.js → chunk-QNUVZO5X.js} +1 -1
- package/dist/{chunk-B75A6AG4.js → chunk-QQYSZMET.js} +108 -64
- package/dist/{chunk-GYN2PTY2.js → chunk-SFCNFX42.js} +1 -1
- package/dist/{chunk-BENRC3OO.js → chunk-TW47X5AO.js} +1 -1
- package/dist/{chunk-SMFRJCXB.js → chunk-XPBEJB27.js} +8 -1
- package/dist/{ci-LWE2I5QW.js → ci-AYMV66P3.js} +9 -9
- package/dist/{constants-JRY7PDEY.js → constants-NHGTSHKT.js} +1 -1
- package/dist/{doctor-cli-KPUULUPS.js → doctor-cli-WKH7T4AW.js} +6 -6
- package/dist/electron-server.js +131 -82
- package/dist/{hub-4CDJPNIM.js → hub-6R3M3NTC.js} +1 -1
- package/dist/index.js +29 -27
- package/dist/{run-tests-RQWSG25U.js → run-tests-5F2OBWIK.js} +2 -2
- package/dist/{run-tests-6G2F7OSC.js → run-tests-NBCA4KEG.js} +1 -1
- package/dist/{server-XNB76DRR.js → server-37EXC4EB.js} +5 -5
- package/dist/{server-MF6UDMBZ.js → server-LDPQ3DLK.js} +23 -20
- package/dist/{task-orchestrator-PKZ2UTOK.js → task-orchestrator-2DTDVCMR.js} +5 -5
- package/dist/{usage-Z64KJASD.js → usage-SZWZFX3T.js} +2 -2
- package/dist/web/client/app.js +2 -2
- package/package.json +1 -1
|
@@ -2,26 +2,26 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getConfigDirUsage,
|
|
4
4
|
listRecentCrashes
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KJHMRAJU.js";
|
|
6
6
|
import {
|
|
7
7
|
ProviderRegistry
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QMXC327F.js";
|
|
9
9
|
import {
|
|
10
10
|
getStatsSnapshot,
|
|
11
11
|
getTopFailingTools,
|
|
12
12
|
getTopUsedTools,
|
|
13
13
|
resetStats
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-2WV6AGHM.js";
|
|
15
|
+
import "./chunk-XPBEJB27.js";
|
|
16
16
|
import {
|
|
17
17
|
ConfigManager
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-TW47X5AO.js";
|
|
19
19
|
import "./chunk-TZQHYZKT.js";
|
|
20
20
|
import {
|
|
21
21
|
DEV_STATE_FILE_NAME,
|
|
22
22
|
MEMORY_FILE_NAME,
|
|
23
23
|
VERSION
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-QNUVZO5X.js";
|
|
25
25
|
import "./chunk-IW3Q7AE5.js";
|
|
26
26
|
|
|
27
27
|
// src/diagnostics/doctor-cli.ts
|
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-BQIV4ZQT.js";
|
|
40
40
|
import {
|
|
41
41
|
hasSemanticIndex,
|
|
42
42
|
semanticSearch
|
|
@@ -623,8 +623,8 @@ var BaseProvider = class {
|
|
|
623
623
|
* 将 Message[] 转换为 OpenAI API 格式的消息数组。
|
|
624
624
|
* content 为 string 时直接传递;为 MessageContentPart[] 时保留数组格式(vision 请求)。
|
|
625
625
|
*
|
|
626
|
-
*
|
|
627
|
-
*
|
|
626
|
+
* 基类只提供降级格式:跳过 role='tool' 和带 toolCalls 的 assistant 消息。
|
|
627
|
+
* 需要完整工具历史 roundtrip 的 provider 应覆盖消息构造逻辑,例如 OpenAI-compatible provider。
|
|
628
628
|
*/
|
|
629
629
|
normalizeMessages(messages) {
|
|
630
630
|
return messages.filter((m) => m.role !== "tool" && !m.toolCalls).map((m) => ({ role: m.role, content: m.content }));
|
|
@@ -636,7 +636,14 @@ function isFileWriteTool(name) {
|
|
|
636
636
|
return name === "write_file" || name === "edit_file" || name === "notebook_edit";
|
|
637
637
|
}
|
|
638
638
|
function getDangerLevel(toolName, args) {
|
|
639
|
-
if (toolName.startsWith("mcp__"))
|
|
639
|
+
if (toolName.startsWith("mcp__")) {
|
|
640
|
+
const rawName = toolName.split("__").pop() ?? "";
|
|
641
|
+
const normalized = rawName.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
642
|
+
if (/^(read|get|list|search|find|query|fetch|status|describe|inspect|show|lookup)(?:_|$)/.test(normalized)) {
|
|
643
|
+
return "safe";
|
|
644
|
+
}
|
|
645
|
+
return "write";
|
|
646
|
+
}
|
|
640
647
|
if (toolName === "bash") {
|
|
641
648
|
const cmd = String(args["command"] ?? "");
|
|
642
649
|
if (/\brm\s+[^\n]*(?:-\w*[rRfF]\w*|--recursive|--force)\b/.test(cmd)) return "destructive";
|
|
@@ -4725,7 +4732,7 @@ var SessionManager = class {
|
|
|
4725
4732
|
// src/tools/builtin/bash.ts
|
|
4726
4733
|
import { spawn } from "child_process";
|
|
4727
4734
|
import { existsSync as existsSync4, readdirSync as readdirSync2, statSync } from "fs";
|
|
4728
|
-
import { platform } from "os";
|
|
4735
|
+
import { platform as platform2 } from "os";
|
|
4729
4736
|
import { resolve } from "path";
|
|
4730
4737
|
|
|
4731
4738
|
// src/tools/undo-stack.ts
|
|
@@ -4863,9 +4870,32 @@ function runWithSessionKey(sessionKey, fn) {
|
|
|
4863
4870
|
return als.run({ sessionKey: key }, fn);
|
|
4864
4871
|
}
|
|
4865
4872
|
|
|
4866
|
-
// src/tools/
|
|
4873
|
+
// src/tools/win-shell.ts
|
|
4874
|
+
import { platform } from "os";
|
|
4867
4875
|
var IS_WINDOWS = platform() === "win32";
|
|
4868
|
-
var
|
|
4876
|
+
var WIN_UTF8_PREAMBLE = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; $OutputEncoding = [System.Text.Encoding]::UTF8; ";
|
|
4877
|
+
function shellInvocation(command) {
|
|
4878
|
+
if (IS_WINDOWS) {
|
|
4879
|
+
return {
|
|
4880
|
+
shell: "powershell.exe",
|
|
4881
|
+
args: ["-NoProfile", "-NonInteractive", "-Command", WIN_UTF8_PREAMBLE + command]
|
|
4882
|
+
};
|
|
4883
|
+
}
|
|
4884
|
+
return { shell: process.env["SHELL"] ?? "/bin/bash", args: ["-c", command] };
|
|
4885
|
+
}
|
|
4886
|
+
function decodeWindowsBuffer(buf) {
|
|
4887
|
+
const utf8 = buf.toString("utf-8");
|
|
4888
|
+
if (!utf8.includes("\uFFFD")) return utf8;
|
|
4889
|
+
try {
|
|
4890
|
+
return new TextDecoder("gbk").decode(buf);
|
|
4891
|
+
} catch {
|
|
4892
|
+
return utf8;
|
|
4893
|
+
}
|
|
4894
|
+
}
|
|
4895
|
+
|
|
4896
|
+
// src/tools/builtin/bash.ts
|
|
4897
|
+
var IS_WINDOWS2 = platform2() === "win32";
|
|
4898
|
+
var SHELL = IS_WINDOWS2 ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
|
|
4869
4899
|
var cwdBySession = /* @__PURE__ */ new Map();
|
|
4870
4900
|
function getCwd() {
|
|
4871
4901
|
const key = getCurrentSessionKey();
|
|
@@ -4882,7 +4912,7 @@ function setCwd(next) {
|
|
|
4882
4912
|
var bashTool = {
|
|
4883
4913
|
definition: {
|
|
4884
4914
|
name: "bash",
|
|
4885
|
-
description:
|
|
4915
|
+
description: IS_WINDOWS2 ? `Execute commands in PowerShell. Supports mkdir, ls, cat, python, etc.
|
|
4886
4916
|
Important rules:
|
|
4887
4917
|
1. Each bash call runs in an independent subprocess; cd commands do not persist. To run in a specific directory, use the cwd parameter, or combine commands: e.g. "cd mydir; ls" or "mkdir mydir; cd mydir; New-Item file.txt".
|
|
4888
4918
|
2. If a command fails (returns an error or non-zero exit code), stop immediately, report the error to the user, and do not retry the same or similar commands.
|
|
@@ -4898,7 +4928,7 @@ Important rules:
|
|
|
4898
4928
|
parameters: {
|
|
4899
4929
|
command: {
|
|
4900
4930
|
type: "string",
|
|
4901
|
-
description:
|
|
4931
|
+
description: IS_WINDOWS2 ? `PowerShell command to execute. Combine multiple commands with semicolons, e.g.: "mkdir mydir; Set-Content mydir/file.txt 'content'"` : `${SHELL} command to execute. Combine multiple commands with &&, e.g.: "mkdir -p mydir && echo 'content' > mydir/file.txt"`,
|
|
4902
4932
|
required: true
|
|
4903
4933
|
},
|
|
4904
4934
|
cwd: {
|
|
@@ -4950,9 +4980,9 @@ Important rules:
|
|
|
4950
4980
|
setCwd(resolved);
|
|
4951
4981
|
}
|
|
4952
4982
|
let actualCommand;
|
|
4953
|
-
if (
|
|
4983
|
+
if (IS_WINDOWS2) {
|
|
4954
4984
|
const fixedCommand = fixWindowsDeleteCommand(command);
|
|
4955
|
-
actualCommand =
|
|
4985
|
+
actualCommand = WIN_UTF8_PREAMBLE + fixedCommand;
|
|
4956
4986
|
} else {
|
|
4957
4987
|
actualCommand = command;
|
|
4958
4988
|
}
|
|
@@ -4996,7 +5026,7 @@ Important rules:
|
|
|
4996
5026
|
}
|
|
4997
5027
|
updateCwdFromCommand(command, effectiveCwd);
|
|
4998
5028
|
pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
|
|
4999
|
-
const result = Buffer.isBuffer(stdout) ?
|
|
5029
|
+
const result = Buffer.isBuffer(stdout) ? IS_WINDOWS2 ? decodeWindowsBuffer(stdout) : stdout.toString("utf-8") : String(stdout ?? "");
|
|
5000
5030
|
return result || "(command completed with no output)";
|
|
5001
5031
|
} catch (err) {
|
|
5002
5032
|
pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
|
|
@@ -5021,8 +5051,8 @@ How to recover (pick ONE \u2014 do NOT retry the same command):
|
|
|
5021
5051
|
);
|
|
5022
5052
|
}
|
|
5023
5053
|
if ("status" in execErr && execErr.status !== void 0) {
|
|
5024
|
-
const stderr =
|
|
5025
|
-
const stdout =
|
|
5054
|
+
const stderr = IS_WINDOWS2 && Buffer.isBuffer(execErr.stderr) ? decodeWindowsBuffer(execErr.stderr).trim() : execErr.stderr?.toString().trim() ?? "";
|
|
5055
|
+
const stdout = IS_WINDOWS2 && Buffer.isBuffer(execErr.stdout) ? decodeWindowsBuffer(execErr.stdout).trim() : execErr.stdout?.toString().trim() ?? "";
|
|
5026
5056
|
const combined = [stdout, stderr].filter(Boolean).join("\n");
|
|
5027
5057
|
const hint = buildErrorHint(command, combined);
|
|
5028
5058
|
throw new ToolError(
|
|
@@ -5056,23 +5086,14 @@ function fixWindowsDeleteCommand(command) {
|
|
|
5056
5086
|
}
|
|
5057
5087
|
);
|
|
5058
5088
|
}
|
|
5059
|
-
function decodeWindowsBuffer(buf) {
|
|
5060
|
-
const utf8 = buf.toString("utf-8");
|
|
5061
|
-
if (!utf8.includes("\uFFFD")) return utf8;
|
|
5062
|
-
try {
|
|
5063
|
-
return new TextDecoder("gbk").decode(buf);
|
|
5064
|
-
} catch {
|
|
5065
|
-
return utf8;
|
|
5066
|
-
}
|
|
5067
|
-
}
|
|
5068
5089
|
function buildErrorHint(command, stderr) {
|
|
5069
5090
|
const hints = [];
|
|
5070
|
-
if (
|
|
5091
|
+
if (IS_WINDOWS2 && /\|\|/.test(command) && /(\|\||is not a valid argument|不是此版本中的有效|ParserError|Unexpected token)/i.test(stderr)) {
|
|
5071
5092
|
hints.push(
|
|
5072
5093
|
`Hint: PowerShell parses '||' as a pipeline operator (PS 5.x) or logical-or (PS 7+), it CANNOT be passed inline as SQL string concatenation. Workaround: write the SQL to a local .sql file with write_file, then 'scp' it to the remote host and run 'psql -f /tmp/x.sql'. Do NOT try to escape || or wrap the command differently \u2014 it will not work.`
|
|
5073
5094
|
);
|
|
5074
5095
|
}
|
|
5075
|
-
if (
|
|
5096
|
+
if (IS_WINDOWS2 && /\bpython3\b/.test(command)) {
|
|
5076
5097
|
const trivialStderr = stderr.trim().length === 0 || /^[·\s]*exit 1\b/i.test(stderr.trim());
|
|
5077
5098
|
if (/(not recognized|is not recognized|无法将.*识别)/i.test(stderr) || trivialStderr) {
|
|
5078
5099
|
hints.push(
|
|
@@ -5080,27 +5101,27 @@ function buildErrorHint(command, stderr) {
|
|
|
5080
5101
|
);
|
|
5081
5102
|
}
|
|
5082
5103
|
}
|
|
5083
|
-
if (
|
|
5104
|
+
if (IS_WINDOWS2 && /^\s*ssh\b/.test(command) && /\\"/.test(command) && /(syntax error|ERROR:|ParserError|unexpected|parser|意外的|语法|不是此版本|所在位置\s+行)/i.test(stderr)) {
|
|
5084
5105
|
hints.push(
|
|
5085
5106
|
`Hint: SSH + nested quotes ("...\\"...\\"") is fragile across PS \u2192 ssh \u2192 remote-shell \u2192 psql layers. Use the file-based flow: (1) write_file locally to a .sql file, (2) 'scp x.sql root@host:/tmp/', (3) 'ssh root@host "sudo -u postgres psql -d <db> -f /tmp/x.sql"'. Or for remote-only SQL: 'ssh host "cat > /tmp/x.sql << \\'SQLEOF\\'\\n...\\nSQLEOF"' then run psql -f. Avoid inline psql -c with embedded quotes.`
|
|
5086
5107
|
);
|
|
5087
5108
|
}
|
|
5088
|
-
if (
|
|
5109
|
+
if (IS_WINDOWS2 && /\bmkdir\b/.test(command) && /\s-p\b/.test(command) && /(已存在|already exists|ItemExistsUnauthorizedAccessError|exists)/i.test(stderr)) {
|
|
5089
5110
|
hints.push(
|
|
5090
5111
|
`Hint: PowerShell's mkdir does not support the '-p' flag. When the directory already exists it exits with code 1. Use instead: New-Item -ItemType Directory -Force -Path <dir> ('-Force' silently succeeds even if the directory already exists, mimicking 'mkdir -p').`
|
|
5091
5112
|
);
|
|
5092
5113
|
}
|
|
5093
|
-
if (
|
|
5114
|
+
if (IS_WINDOWS2 && /Remove-Item/i.test(command) && /cannot find path|no such file|exit 1/i.test(stderr || command)) {
|
|
5094
5115
|
hints.push(
|
|
5095
5116
|
`Hint: On Windows, "Remove-Item ... -ErrorAction SilentlyContinue" still makes the powershell process exit 1 when no files match (even though the error is silenced). Use Test-Path as a guard: @('tmp_a.sql','tmp_b.sql','tmp_c.csv') | Where-Object { Test-Path $_ } | Remove-Item -Force \u2014 Test-Path returns a bool without raising, so missing paths are filtered out and exit code is 0.`
|
|
5096
5117
|
);
|
|
5097
5118
|
}
|
|
5098
|
-
if (
|
|
5119
|
+
if (IS_WINDOWS2 && /-(?:EA|ErrorAction)\s+(?:SilentlyContinue|Ignore)\b/i.test(command) && /\b(Get-ChildItem|Get-Item|Get-Content|gci|gi|gc|dir|ls)\b/i.test(command) && !/Remove-Item/i.test(command) && !/Test-Path/i.test(command) && /cannot find path|no such file|does not exist|exit 1|不存在|找不到/i.test(stderr || command)) {
|
|
5099
5120
|
hints.push(
|
|
5100
5121
|
`Hint: On Windows, "-ErrorAction SilentlyContinue" silences the error MESSAGE but does NOT change the exit code \u2014 the process still exits 1 when the path doesn't exist (because powershell.exe -NonInteractive flips the exit code on any non-terminating error, silenced or not). If you're just probing for existence, use Test-Path instead \u2014 it returns $true/$false without raising: \`if (Test-Path ./mydir) { Get-ChildItem ./mydir }\` or unconditionally use \`New-Item -ItemType Directory -Force -Path ./mydir\` which is idempotent. Don't retry the same -ErrorAction SilentlyContinue probe expecting a different result.`
|
|
5101
5122
|
);
|
|
5102
5123
|
}
|
|
5103
|
-
if (
|
|
5124
|
+
if (IS_WINDOWS2 && /\bWrite-(?:Content|File)\b/i.test(command) && /(not recognized|无法将.*识别|不是.*cmdlet|CommandNotFoundException)/i.test(stderr)) {
|
|
5104
5125
|
hints.push(
|
|
5105
5126
|
`Hint: 'Write-Content' / 'Write-File' are NOT PowerShell cmdlets (common LLM hallucination). To write a file, prefer the built-in 'write_file' tool \u2014 it handles encoding/escaping cleanly and supports diff preview. If you must stay in bash: use 'Set-Content -Path <file> -Value <string> -Encoding utf8' (overwrites) or 'Add-Content' (appends) or 'Out-File -FilePath <file> -Encoding utf8'.`
|
|
5106
5127
|
);
|
|
@@ -5121,6 +5142,18 @@ function detectBlockingCommand(command) {
|
|
|
5121
5142
|
if (logcat && !/(?:^|\s)-(?:d|t|c|g)\b/.test(logcat[1] ?? "")) {
|
|
5122
5143
|
return blockingMessage("adb logcat", "stream device logs");
|
|
5123
5144
|
}
|
|
5145
|
+
if (/\bStart-Process\b/i.test(command) && /\b(?:emulator|qemu[\w-]*)(?:\.exe)?\b/i.test(command)) {
|
|
5146
|
+
return `This launches a long-running emulator/qemu process via Start-Process, which DETACHES a GUI child that aicli cannot track or stop with task_stop \u2014 and in practice that child often dies together with this bash call, leaving nothing running.
|
|
5147
|
+
|
|
5148
|
+
Use the 'task_create' tool instead so the process is tracked and stoppable:
|
|
5149
|
+
task_create(command: "<sdk>\\emulator\\emulator.exe -avd <name> -no-boot-anim", description: "launch emulator")
|
|
5150
|
+
|
|
5151
|
+
Then poll readiness with SHORT bash calls (these DO exit):
|
|
5152
|
+
adb devices # is the device listed?
|
|
5153
|
+
adb shell getprop sys.boot_completed # prints "1" once fully booted
|
|
5154
|
+
|
|
5155
|
+
[Do NOT launch long-running GUI processes via Start-Process in bash \u2014 use task_create.]`;
|
|
5156
|
+
}
|
|
5124
5157
|
return null;
|
|
5125
5158
|
}
|
|
5126
5159
|
function blockingMessage(example, what) {
|
|
@@ -5200,7 +5233,7 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
|
|
|
5200
5233
|
}
|
|
5201
5234
|
function runShell(command, opts) {
|
|
5202
5235
|
return new Promise((resolvePromise) => {
|
|
5203
|
-
const shellArgs =
|
|
5236
|
+
const shellArgs = IS_WINDOWS2 ? ["-NoProfile", "-NonInteractive", "-Command", command] : ["-c", command];
|
|
5204
5237
|
const child = spawn(SHELL, shellArgs, {
|
|
5205
5238
|
cwd: opts.cwd,
|
|
5206
5239
|
env: opts.env,
|
|
@@ -5263,7 +5296,7 @@ function runShell(command, opts) {
|
|
|
5263
5296
|
}
|
|
5264
5297
|
function killChild(child) {
|
|
5265
5298
|
if (child.killed || child.exitCode !== null) return;
|
|
5266
|
-
if (
|
|
5299
|
+
if (IS_WINDOWS2 && child.pid) {
|
|
5267
5300
|
try {
|
|
5268
5301
|
spawn("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
|
|
5269
5302
|
windowsHide: true,
|
|
@@ -5688,7 +5721,7 @@ ${content}`;
|
|
|
5688
5721
|
};
|
|
5689
5722
|
|
|
5690
5723
|
// src/tools/builtin/write-file.ts
|
|
5691
|
-
import {
|
|
5724
|
+
import { appendFileSync, mkdirSync as mkdirSync4 } from "fs";
|
|
5692
5725
|
import { dirname as dirname3 } from "path";
|
|
5693
5726
|
|
|
5694
5727
|
// src/tools/executor.ts
|
|
@@ -6211,8 +6244,8 @@ var ToolExecutor = class {
|
|
|
6211
6244
|
/** confirm() 的取消回调,由 SIGINT handler 调用 */
|
|
6212
6245
|
cancelConfirmFn = null;
|
|
6213
6246
|
/**
|
|
6214
|
-
* 会话级 auto-approve
|
|
6215
|
-
*
|
|
6247
|
+
* 会话级 auto-approve:跳过 write 确认(仅当前会话有效)。
|
|
6248
|
+
* destructive 操作仍然必须逐次确认。
|
|
6216
6249
|
*/
|
|
6217
6250
|
sessionAutoApprove = false;
|
|
6218
6251
|
/**
|
|
@@ -6296,7 +6329,7 @@ var ToolExecutor = class {
|
|
|
6296
6329
|
}
|
|
6297
6330
|
}
|
|
6298
6331
|
}
|
|
6299
|
-
if (this.sessionAutoApprove && dangerLevel
|
|
6332
|
+
if (this.sessionAutoApprove && dangerLevel === "write") {
|
|
6300
6333
|
this.printToolCall(call);
|
|
6301
6334
|
if (dangerLevel === "write") this.printDiffPreview(call);
|
|
6302
6335
|
console.log(theme.warning(" \u26A1 Auto-approved (session /yolo mode)"));
|
|
@@ -6825,7 +6858,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
6825
6858
|
if (appendMode) {
|
|
6826
6859
|
appendFileSync(filePath, content, encoding);
|
|
6827
6860
|
} else {
|
|
6828
|
-
|
|
6861
|
+
atomicWriteFileSync(filePath, content);
|
|
6829
6862
|
}
|
|
6830
6863
|
const lines = content.split("\n").length;
|
|
6831
6864
|
const mode = appendMode ? "appended" : "written";
|
|
@@ -6841,7 +6874,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
6841
6874
|
};
|
|
6842
6875
|
|
|
6843
6876
|
// src/tools/builtin/edit-file.ts
|
|
6844
|
-
import { readFileSync as readFileSync7,
|
|
6877
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
6845
6878
|
|
|
6846
6879
|
// src/tools/builtin/patch-apply.ts
|
|
6847
6880
|
function parseUnifiedDiff(patch) {
|
|
@@ -7240,7 +7273,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
7240
7273
|
if (res.ok && res.appliedCount > 0 && res.content !== void 0) {
|
|
7241
7274
|
undoStack.push(filePath, `edit_file (patch ${res.appliedCount}/${hunks.length}): ${filePath}`);
|
|
7242
7275
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
7243
|
-
|
|
7276
|
+
atomicWriteFileSync(filePath, res.content);
|
|
7244
7277
|
}
|
|
7245
7278
|
return lines.join("\n");
|
|
7246
7279
|
}
|
|
@@ -7274,7 +7307,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
7274
7307
|
if (writeChanges) {
|
|
7275
7308
|
undoStack.push(filePath, `edit_file (batch ${appliedCount}/${edits.length}): ${filePath}`);
|
|
7276
7309
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
7277
|
-
|
|
7310
|
+
atomicWriteFileSync(filePath, working);
|
|
7278
7311
|
}
|
|
7279
7312
|
const lines = [];
|
|
7280
7313
|
if (anyFailed && stopOnError) {
|
|
@@ -7312,7 +7345,7 @@ Please read the file first and use exact text.`;
|
|
|
7312
7345
|
const label = res.info?.mode === "ignore_whitespace" ? "edit_file (ws-replace)" : res.info?.mode === "replace_all" ? "edit_file (replace_all)" : "edit_file (replace)";
|
|
7313
7346
|
undoStack.push(filePath, `${label}: ${filePath}`);
|
|
7314
7347
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
7315
|
-
|
|
7348
|
+
atomicWriteFileSync(filePath, res.content);
|
|
7316
7349
|
if (res.info?.mode === "replace_all") {
|
|
7317
7350
|
return `Successfully edited ${filePath}${modeLabel}
|
|
7318
7351
|
Replaced: ${res.info.replacedCount} occurrence(s) of ${truncatePreview(oldStr)}
|
|
@@ -7336,7 +7369,7 @@ Please read the file first and use exact text.`;
|
|
|
7336
7369
|
undoStack.push(filePath, `edit_file (insert): ${filePath}`);
|
|
7337
7370
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
7338
7371
|
lines.splice(afterLine, 0, content);
|
|
7339
|
-
|
|
7372
|
+
atomicWriteFileSync(filePath, lines.join("\n"));
|
|
7340
7373
|
return `Successfully inserted ${content.split("\n").length} line(s) after line ${afterLine} in ${filePath}`;
|
|
7341
7374
|
}
|
|
7342
7375
|
if (args["delete_from_line"] !== void 0) {
|
|
@@ -7352,7 +7385,7 @@ Please read the file first and use exact text.`;
|
|
|
7352
7385
|
undoStack.push(filePath, `edit_file (delete): ${filePath}`);
|
|
7353
7386
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
7354
7387
|
const deleted = lines.splice(fromLine - 1, toLine - fromLine + 1);
|
|
7355
|
-
|
|
7388
|
+
atomicWriteFileSync(filePath, lines.join("\n"));
|
|
7356
7389
|
return `Successfully deleted lines ${fromLine}-${toLine} (${deleted.length} lines) from ${filePath}`;
|
|
7357
7390
|
}
|
|
7358
7391
|
throw new ToolError(
|
|
@@ -7837,8 +7870,8 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
7837
7870
|
|
|
7838
7871
|
// src/tools/builtin/run-interactive.ts
|
|
7839
7872
|
import { spawn as spawn2 } from "child_process";
|
|
7840
|
-
import { platform as
|
|
7841
|
-
var
|
|
7873
|
+
import { platform as platform3 } from "os";
|
|
7874
|
+
var IS_WINDOWS3 = platform3() === "win32";
|
|
7842
7875
|
var runInteractiveTool = {
|
|
7843
7876
|
definition: {
|
|
7844
7877
|
name: "run_interactive",
|
|
@@ -7918,7 +7951,7 @@ var runInteractiveTool = {
|
|
|
7918
7951
|
let lineIdx = 0;
|
|
7919
7952
|
const writeNextLine = () => {
|
|
7920
7953
|
if (lineIdx < stdinLines.length && !child.stdin.destroyed) {
|
|
7921
|
-
const line = stdinLines[lineIdx++] + (
|
|
7954
|
+
const line = stdinLines[lineIdx++] + (IS_WINDOWS3 ? "\r\n" : "\n");
|
|
7922
7955
|
const canContinue = child.stdin.write(line);
|
|
7923
7956
|
if (canContinue) {
|
|
7924
7957
|
setTimeout(writeNextLine, 150);
|
|
@@ -8557,7 +8590,7 @@ ${preamble}`;
|
|
|
8557
8590
|
}
|
|
8558
8591
|
|
|
8559
8592
|
// src/tools/builtin/save-last-response.ts
|
|
8560
|
-
import {
|
|
8593
|
+
import { mkdirSync as mkdirSync5, unlinkSync as unlinkSync3, rmdirSync as rmdirSync2 } from "fs";
|
|
8561
8594
|
import { dirname as dirname4 } from "path";
|
|
8562
8595
|
var lastResponseStore = { content: "" };
|
|
8563
8596
|
function cleanupRejectedTeeFile(filePath) {
|
|
@@ -8608,7 +8641,7 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
8608
8641
|
}
|
|
8609
8642
|
undoStack.push(filePath, `save_last_response: ${filePath}`);
|
|
8610
8643
|
mkdirSync5(dirname4(filePath), { recursive: true });
|
|
8611
|
-
|
|
8644
|
+
atomicWriteFileSync(filePath, content);
|
|
8612
8645
|
const lines = content.split("\n").length;
|
|
8613
8646
|
return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
|
|
8614
8647
|
}
|
|
@@ -9231,16 +9264,29 @@ var spawnAgentTool = {
|
|
|
9231
9264
|
// src/tools/builtin/task-manager.ts
|
|
9232
9265
|
import { spawn as spawn3 } from "child_process";
|
|
9233
9266
|
import { randomUUID } from "crypto";
|
|
9234
|
-
import { platform as
|
|
9267
|
+
import { platform as platform4 } from "os";
|
|
9235
9268
|
var MAX_OUTPUT_CHARS = 1e4;
|
|
9269
|
+
var MAX_OUTPUT_BYTES = 64 * 1024;
|
|
9236
9270
|
var MAX_TASKS = 20;
|
|
9237
9271
|
var tasks = /* @__PURE__ */ new Map();
|
|
9238
|
-
function
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9272
|
+
function capChunks(chunks) {
|
|
9273
|
+
let total = 0;
|
|
9274
|
+
for (const c of chunks) total += c.length;
|
|
9275
|
+
while (total > MAX_OUTPUT_BYTES && chunks.length > 1) {
|
|
9276
|
+
total -= chunks.shift().length;
|
|
9242
9277
|
}
|
|
9243
9278
|
}
|
|
9279
|
+
function decodeField(chunks) {
|
|
9280
|
+
const buf = Buffer.concat(chunks);
|
|
9281
|
+
const text = IS_WINDOWS ? decodeWindowsBuffer(buf) : buf.toString("utf-8");
|
|
9282
|
+
return text.length > MAX_OUTPUT_CHARS ? "... [truncated] ...\n" + text.slice(-MAX_OUTPUT_CHARS) : text;
|
|
9283
|
+
}
|
|
9284
|
+
function appendOutput(task, field, chunk) {
|
|
9285
|
+
const chunks = field === "stdout" ? task._stdoutChunks : task._stderrChunks;
|
|
9286
|
+
chunks.push(chunk);
|
|
9287
|
+
capChunks(chunks);
|
|
9288
|
+
task[field] = decodeField(chunks);
|
|
9289
|
+
}
|
|
9244
9290
|
function createTask(command, description) {
|
|
9245
9291
|
if (tasks.size >= MAX_TASKS) {
|
|
9246
9292
|
for (const [id2, t] of tasks) {
|
|
@@ -9251,13 +9297,11 @@ function createTask(command, description) {
|
|
|
9251
9297
|
}
|
|
9252
9298
|
}
|
|
9253
9299
|
const id = randomUUID().slice(0, 8);
|
|
9254
|
-
const
|
|
9255
|
-
const
|
|
9256
|
-
const shellFlag = isWin ? "/c" : "-c";
|
|
9257
|
-
const proc = spawn3(shell, [shellFlag, command], {
|
|
9300
|
+
const { shell, args } = shellInvocation(command);
|
|
9301
|
+
const proc = spawn3(shell, args, {
|
|
9258
9302
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9259
9303
|
detached: false,
|
|
9260
|
-
env: { ...process.env, PYTHONUTF8: "1" }
|
|
9304
|
+
env: { ...process.env, PYTHONUTF8: "1", PYTHONIOENCODING: "utf-8" }
|
|
9261
9305
|
});
|
|
9262
9306
|
const task = {
|
|
9263
9307
|
id,
|
|
@@ -9269,10 +9313,12 @@ function createTask(command, description) {
|
|
|
9269
9313
|
status: "running",
|
|
9270
9314
|
exitCode: null,
|
|
9271
9315
|
stdout: "",
|
|
9272
|
-
stderr: ""
|
|
9316
|
+
stderr: "",
|
|
9317
|
+
_stdoutChunks: [],
|
|
9318
|
+
_stderrChunks: []
|
|
9273
9319
|
};
|
|
9274
|
-
proc.stdout?.on("data", (chunk) => appendOutput(task, "stdout", chunk
|
|
9275
|
-
proc.stderr?.on("data", (chunk) => appendOutput(task, "stderr", chunk
|
|
9320
|
+
proc.stdout?.on("data", (chunk) => appendOutput(task, "stdout", chunk));
|
|
9321
|
+
proc.stderr?.on("data", (chunk) => appendOutput(task, "stderr", chunk));
|
|
9276
9322
|
proc.on("close", (code) => {
|
|
9277
9323
|
task.endTime = Date.now();
|
|
9278
9324
|
task.exitCode = code;
|
|
@@ -9288,7 +9334,9 @@ Process error: ${err.message}`;
|
|
|
9288
9334
|
return task;
|
|
9289
9335
|
}
|
|
9290
9336
|
function listTasks() {
|
|
9291
|
-
return [...tasks.values()].map(
|
|
9337
|
+
return [...tasks.values()].map(
|
|
9338
|
+
({ process: _p, _stdoutChunks: _o, _stderrChunks: _e, ...rest }) => rest
|
|
9339
|
+
);
|
|
9292
9340
|
}
|
|
9293
9341
|
function getTaskOutput(id) {
|
|
9294
9342
|
const task = tasks.get(id);
|
|
@@ -9300,7 +9348,7 @@ function stopTask(id) {
|
|
|
9300
9348
|
if (!task || task.status !== "running") return false;
|
|
9301
9349
|
try {
|
|
9302
9350
|
const proc = task.process;
|
|
9303
|
-
if (
|
|
9351
|
+
if (platform4() === "win32" && proc.pid) {
|
|
9304
9352
|
try {
|
|
9305
9353
|
spawn3("taskkill", ["/PID", String(proc.pid), "/T", "/F"], {
|
|
9306
9354
|
windowsHide: true,
|
|
@@ -9335,7 +9383,7 @@ function stopTask(id) {
|
|
|
9335
9383
|
var taskCreateTool = {
|
|
9336
9384
|
definition: {
|
|
9337
9385
|
name: "task_create",
|
|
9338
|
-
description: `Start a command running in the background and return immediately with a task ID (monitor via task_list, stop via task_stop). Use this for ANY long-running or never-exiting command, including: dev servers (npm run dev, metro / react-native start), Android emulator launch (emulator -avd X), device log streaming (adb logcat), file watchers, and builds you want to monitor while continuing other work. Running such commands via 'bash' instead would block until timeout.`,
|
|
9386
|
+
description: `Start a command running in the background and return immediately with a task ID (monitor via task_list, stop via task_stop). Use this for ANY long-running or never-exiting command, including: dev servers (npm run dev, metro / react-native start), Android emulator launch (emulator -avd X), device log streaming (adb logcat), file watchers, and builds you want to monitor while continuing other work. Running such commands via 'bash' instead would block until timeout. The command runs in the SAME shell as the 'bash' tool (PowerShell on Windows, $SHELL on Unix), so a command you verified with 'bash' can be moved here verbatim \u2014 write it in PowerShell syntax on Windows (e.g. & "C:\\path\\app.exe" -flag), NOT cmd.exe syntax.`,
|
|
9339
9387
|
parameters: {
|
|
9340
9388
|
command: {
|
|
9341
9389
|
type: "string",
|
|
@@ -11299,7 +11347,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
11299
11347
|
}
|
|
11300
11348
|
}
|
|
11301
11349
|
this.sendToolCallStart(call);
|
|
11302
|
-
if (this.sessionAutoApprove &&
|
|
11350
|
+
if (this.sessionAutoApprove && dangerLevel === "write") {
|
|
11303
11351
|
try {
|
|
11304
11352
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
11305
11353
|
const content = truncateOutput(rawContent, call.name);
|
|
@@ -11403,7 +11451,7 @@ import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
|
|
|
11403
11451
|
import { join as join12 } from "path";
|
|
11404
11452
|
|
|
11405
11453
|
// src/repl/dev-state.ts
|
|
11406
|
-
import { existsSync as existsSync18, readFileSync as readFileSync12, writeFileSync as
|
|
11454
|
+
import { existsSync as existsSync18, readFileSync as readFileSync12, writeFileSync as writeFileSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync9 } from "fs";
|
|
11407
11455
|
import { join as join11 } from "path";
|
|
11408
11456
|
import { homedir as homedir6 } from "os";
|
|
11409
11457
|
function getDevStatePath() {
|
|
@@ -11681,7 +11729,7 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
11681
11729
|
}
|
|
11682
11730
|
|
|
11683
11731
|
// src/web/session-handler.ts
|
|
11684
|
-
import { existsSync as existsSync21, readFileSync as readFileSync14, appendFileSync as appendFileSync3, writeFileSync as
|
|
11732
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14, appendFileSync as appendFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync10, readdirSync as readdirSync9, statSync as statSync8, createWriteStream } from "fs";
|
|
11685
11733
|
import { join as join15, resolve as resolve5, dirname as dirname5 } from "path";
|
|
11686
11734
|
import { execSync as execSync3 } from "child_process";
|
|
11687
11735
|
|
|
@@ -13045,7 +13093,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
|
|
|
13045
13093
|
try {
|
|
13046
13094
|
mkdirSync10(dirname5(pendingTeeSave), { recursive: true });
|
|
13047
13095
|
const bodyToSave = stripOuterCodeFence(content);
|
|
13048
|
-
|
|
13096
|
+
atomicWriteFileSync(pendingTeeSave, bodyToSave);
|
|
13049
13097
|
undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
|
|
13050
13098
|
const lines = bodyToSave.split("\n").length;
|
|
13051
13099
|
this.send({ type: "info", message: `\u2705 Saved (from reply): ${pendingTeeSave} (${lines} lines, ${bodyToSave.length} chars)` });
|
|
@@ -13220,7 +13268,7 @@ ${summaryContent}`,
|
|
|
13220
13268
|
let summary;
|
|
13221
13269
|
try {
|
|
13222
13270
|
mkdirSync10(dirname5(saveToFile), { recursive: true });
|
|
13223
|
-
fileStream = createWriteStream(saveToFile
|
|
13271
|
+
fileStream = createWriteStream(saveToFile);
|
|
13224
13272
|
const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
|
|
13225
13273
|
const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
|
|
13226
13274
|
const chatRequest = {
|
|
@@ -13260,7 +13308,7 @@ ${summaryContent}`,
|
|
|
13260
13308
|
fullContent = "";
|
|
13261
13309
|
} else {
|
|
13262
13310
|
if (verdict.kind === "salvaged" || verdict.kind === "fallback") {
|
|
13263
|
-
|
|
13311
|
+
atomicWriteFileSync(saveToFile, verdict.content);
|
|
13264
13312
|
fullContent = verdict.content;
|
|
13265
13313
|
}
|
|
13266
13314
|
summary = verdict.summary;
|
|
@@ -13712,7 +13760,7 @@ ${activated.meta.description || ""}` });
|
|
|
13712
13760
|
this.send({ type: "info", message: "\u{1F512} Auto-approve disabled \u2014 confirmations restored for this session." });
|
|
13713
13761
|
} else {
|
|
13714
13762
|
this.toolExecutor.sessionAutoApprove = true;
|
|
13715
|
-
this.send({ type: "info", message: "\u26A1 YOLO mode ON \u2014
|
|
13763
|
+
this.send({ type: "info", message: "\u26A1 YOLO mode ON \u2014 write tools auto-approved; destructive tools still require confirmation.\nUse /yolo off to re-enable write confirmations." });
|
|
13716
13764
|
}
|
|
13717
13765
|
break;
|
|
13718
13766
|
}
|
|
@@ -14325,7 +14373,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
14325
14373
|
case "test": {
|
|
14326
14374
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
14327
14375
|
try {
|
|
14328
|
-
const { executeTests } = await import("./run-tests-
|
|
14376
|
+
const { executeTests } = await import("./run-tests-NBCA4KEG.js");
|
|
14329
14377
|
const argStr = args.join(" ").trim();
|
|
14330
14378
|
let testArgs = {};
|
|
14331
14379
|
if (argStr) {
|
|
@@ -14354,7 +14402,7 @@ Use /init --force to overwrite.` });
|
|
|
14354
14402
|
const projectInfo = this.scanProject(cwd);
|
|
14355
14403
|
const prompt = this.buildInitPrompt(projectInfo, cwd);
|
|
14356
14404
|
const content = await this.chatOnce(prompt, { temperature: 0.3, maxTokens: 4096 });
|
|
14357
|
-
|
|
14405
|
+
writeFileSync4(targetPath, content, "utf-8");
|
|
14358
14406
|
this.send({ type: "info", message: `\u2713 Generated: ${targetPath} (${content.length} chars)
|
|
14359
14407
|
Use /context reload to load it.` });
|
|
14360
14408
|
} catch (err) {
|
|
@@ -14833,7 +14881,7 @@ Add .md files to create commands.` });
|
|
|
14833
14881
|
const configDir = this.config.getConfigDir();
|
|
14834
14882
|
const memPath = join15(configDir, MEMORY_FILE_NAME);
|
|
14835
14883
|
try {
|
|
14836
|
-
|
|
14884
|
+
writeFileSync4(memPath, "", "utf-8");
|
|
14837
14885
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
14838
14886
|
} catch (err) {
|
|
14839
14887
|
this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
|
|
@@ -15309,7 +15357,7 @@ async function setupProxy(configProxy) {
|
|
|
15309
15357
|
}
|
|
15310
15358
|
|
|
15311
15359
|
// src/web/auth.ts
|
|
15312
|
-
import { existsSync as existsSync22, readFileSync as readFileSync15, writeFileSync as
|
|
15360
|
+
import { existsSync as existsSync22, readFileSync as readFileSync15, writeFileSync as writeFileSync5, mkdirSync as mkdirSync11, readdirSync as readdirSync10, copyFileSync } from "fs";
|
|
15313
15361
|
import { join as join16 } from "path";
|
|
15314
15362
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
15315
15363
|
var USERS_FILE = "users.json";
|
|
@@ -15511,7 +15559,7 @@ var AuthManager = class {
|
|
|
15511
15559
|
if (existsSync22(globalConfig)) {
|
|
15512
15560
|
try {
|
|
15513
15561
|
const content = readFileSync15(globalConfig, "utf-8");
|
|
15514
|
-
|
|
15562
|
+
writeFileSync5(join16(userDir, "config.json"), content, "utf-8");
|
|
15515
15563
|
} catch {
|
|
15516
15564
|
}
|
|
15517
15565
|
}
|
|
@@ -15519,7 +15567,7 @@ var AuthManager = class {
|
|
|
15519
15567
|
if (existsSync22(globalMemory)) {
|
|
15520
15568
|
try {
|
|
15521
15569
|
const content = readFileSync15(globalMemory, "utf-8");
|
|
15522
|
-
|
|
15570
|
+
writeFileSync5(join16(userDir, "memory.md"), content, "utf-8");
|
|
15523
15571
|
} catch {
|
|
15524
15572
|
}
|
|
15525
15573
|
}
|
|
@@ -16048,7 +16096,7 @@ async function startWebServer(options = {}) {
|
|
|
16048
16096
|
handlers.set(tabId, handler);
|
|
16049
16097
|
clearPreAuthTimer();
|
|
16050
16098
|
console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
16051
|
-
ws.send(JSON.stringify({ type: "auth_result", success: true,
|
|
16099
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
|
|
16052
16100
|
return;
|
|
16053
16101
|
}
|
|
16054
16102
|
if (action === "token") {
|
|
@@ -16064,7 +16112,7 @@ async function startWebServer(options = {}) {
|
|
|
16064
16112
|
handlers.set(tabId, handler);
|
|
16065
16113
|
clearPreAuthTimer();
|
|
16066
16114
|
console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
|
|
16067
|
-
ws.send(JSON.stringify({ type: "auth_result", success: true,
|
|
16115
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, username: verifiedUser }));
|
|
16068
16116
|
return;
|
|
16069
16117
|
}
|
|
16070
16118
|
if (action === "login") {
|
|
@@ -16079,7 +16127,7 @@ async function startWebServer(options = {}) {
|
|
|
16079
16127
|
handlers.set(tabId, handler);
|
|
16080
16128
|
clearPreAuthTimer();
|
|
16081
16129
|
console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
16082
|
-
ws.send(JSON.stringify({ type: "auth_result", success: true,
|
|
16130
|
+
ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
|
|
16083
16131
|
return;
|
|
16084
16132
|
}
|
|
16085
16133
|
ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Unknown auth action" }));
|
|
@@ -16105,9 +16153,10 @@ async function startWebServer(options = {}) {
|
|
|
16105
16153
|
});
|
|
16106
16154
|
ws.on("close", () => {
|
|
16107
16155
|
clearPreAuthTimer();
|
|
16108
|
-
|
|
16156
|
+
const wasTracked = handlers.has(tabId);
|
|
16109
16157
|
if (handler) handler.onDisconnect();
|
|
16110
|
-
handlers.delete(tabId);
|
|
16158
|
+
if (wasTracked) handlers.delete(tabId);
|
|
16159
|
+
console.log(` \u2717 Tab disconnected: ${tabId.slice(0, 12)} (total: ${handlers.size})`);
|
|
16111
16160
|
});
|
|
16112
16161
|
ws.on("error", (err) => {
|
|
16113
16162
|
console.error(` WebSocket error: ${err.message}`);
|
|
@@ -154,7 +154,7 @@ ${content}`);
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
157
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
157
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-2DTDVCMR.js");
|
|
158
158
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
159
159
|
let interrupted = false;
|
|
160
160
|
const onSigint = () => {
|