jinzd-ai-cli 0.4.114 → 0.4.116
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/{auth-VBV7HTLQ.js → auth-7KK5BOCA.js} +3 -1
- package/dist/{batch-Q5NQCXKN.js → batch-GRHEH3JF.js} +2 -2
- package/dist/{chunk-W45U3KQE.js → chunk-BXDHW7JO.js} +1 -1
- package/dist/{chunk-UF62SHR7.js → chunk-H3F2E4MD.js} +1 -1
- package/dist/{chunk-CP6PALA4.js → chunk-KRTQDZMY.js} +1 -1
- package/dist/{chunk-5UPFMM2A.js → chunk-O7NM4WTS.js} +14 -2
- package/dist/{chunk-E24HT62E.js → chunk-QMT4T4PH.js} +63 -11
- package/dist/{chunk-2WAF7FOX.js → chunk-VJE5V4H2.js} +1 -1
- package/dist/{chunk-XXKWSBRC.js → chunk-YTP26DOV.js} +32 -4
- package/dist/{constants-YEBRZDBP.js → constants-NWATGC4A.js} +1 -1
- package/dist/electron-server.js +137 -18
- package/dist/{hub-DGJC2RRF.js → hub-YNP5HCTT.js} +1 -1
- package/dist/index.js +159 -39
- package/dist/{run-tests-OB4CWKKX.js → run-tests-OKQEMYZK.js} +1 -1
- package/dist/{run-tests-7WN5Q7YV.js → run-tests-QOZZHW4L.js} +2 -2
- package/dist/{server-NQ5J6FAL.js → server-HI6GVBD3.js} +3 -3
- package/dist/{server-AKG7HG36.js → server-IX6L4W2L.js} +42 -11
- package/dist/{task-orchestrator-32YQ6HB2.js → task-orchestrator-SCH3JUKT.js} +3 -3
- package/dist/web/client/style.css +129 -129
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
AuthManager,
|
|
4
|
+
TOKEN_EXPIRY_MS,
|
|
4
5
|
__resetLoginAttemptsForTests
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-O7NM4WTS.js";
|
|
6
7
|
import "./chunk-PDX44BCA.js";
|
|
7
8
|
export {
|
|
8
9
|
AuthManager,
|
|
10
|
+
TOKEN_EXPIRY_MS,
|
|
9
11
|
__resetLoginAttemptsForTests
|
|
10
12
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ConfigManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KRTQDZMY.js";
|
|
5
5
|
import "./chunk-2ZD3YTVM.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-H3F2E4MD.js";
|
|
7
7
|
import "./chunk-PDX44BCA.js";
|
|
8
8
|
|
|
9
9
|
// src/cli/batch.ts
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/web/auth.ts
|
|
4
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFileSync } from "fs";
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFileSync, renameSync, unlinkSync } from "fs";
|
|
5
5
|
import { join } from "path";
|
|
6
6
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
7
7
|
var USERS_FILE = "users.json";
|
|
8
8
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
9
|
+
var TOKEN_EXPIRY_MS = TOKEN_EXPIRY_HOURS * 3600 * 1e3;
|
|
9
10
|
var USERS_DIR = "users";
|
|
10
11
|
var LOGIN_MAX_FAILS = 5;
|
|
11
12
|
var LOGIN_LOCKOUT_MS = 15 * 60 * 1e3;
|
|
@@ -252,7 +253,17 @@ var AuthManager = class {
|
|
|
252
253
|
}
|
|
253
254
|
saveDB(db) {
|
|
254
255
|
mkdirSync(this.baseDir, { recursive: true });
|
|
255
|
-
|
|
256
|
+
const tmp = `${this.usersFile}.tmp`;
|
|
257
|
+
try {
|
|
258
|
+
writeFileSync(tmp, JSON.stringify(db, null, 2), "utf-8");
|
|
259
|
+
renameSync(tmp, this.usersFile);
|
|
260
|
+
} catch (err) {
|
|
261
|
+
try {
|
|
262
|
+
unlinkSync(tmp);
|
|
263
|
+
} catch {
|
|
264
|
+
}
|
|
265
|
+
throw err;
|
|
266
|
+
}
|
|
256
267
|
}
|
|
257
268
|
/** Legacy hash — kept only for migrating old users (v0.2.x) */
|
|
258
269
|
hashPasswordLegacy(password, salt) {
|
|
@@ -278,6 +289,7 @@ var AuthManager = class {
|
|
|
278
289
|
};
|
|
279
290
|
|
|
280
291
|
export {
|
|
292
|
+
TOKEN_EXPIRY_MS,
|
|
281
293
|
__resetLoginAttemptsForTests,
|
|
282
294
|
AuthManager
|
|
283
295
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
schemaToJsonSchema,
|
|
4
4
|
truncateForPersist
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-YTP26DOV.js";
|
|
6
6
|
import {
|
|
7
7
|
AuthError,
|
|
8
8
|
ProviderError,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
MCP_PROTOCOL_VERSION,
|
|
19
19
|
MCP_TOOL_PREFIX,
|
|
20
20
|
VERSION
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-H3F2E4MD.js";
|
|
22
22
|
import {
|
|
23
23
|
redactJson
|
|
24
24
|
} from "./chunk-7ZJN4KLV.js";
|
|
@@ -72,10 +72,20 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
72
72
|
]
|
|
73
73
|
};
|
|
74
74
|
async initialize(apiKey, options) {
|
|
75
|
-
|
|
75
|
+
const clientOptions = {
|
|
76
76
|
apiKey,
|
|
77
77
|
baseURL: options?.baseUrl
|
|
78
|
-
}
|
|
78
|
+
};
|
|
79
|
+
const proxyUrl = options?.proxy;
|
|
80
|
+
try {
|
|
81
|
+
const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
|
|
82
|
+
const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
|
|
83
|
+
const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
|
|
84
|
+
const dispatcher = proxyUrl ? new ProxyAgent({ uri: proxyUrl, bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT }) : new Agent({ bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT });
|
|
85
|
+
clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
this.client = new Anthropic(clientOptions);
|
|
79
89
|
}
|
|
80
90
|
/**
|
|
81
91
|
* 将内部 MessageContentPart[] 格式转换为 Anthropic SDK 期望的 ContentBlockParam[]。
|
|
@@ -932,13 +942,20 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
932
942
|
timeout: this.defaultTimeout
|
|
933
943
|
};
|
|
934
944
|
const proxyUrl = options?.proxy;
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
945
|
+
try {
|
|
946
|
+
const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
|
|
947
|
+
const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
|
|
948
|
+
const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
|
|
949
|
+
const dispatcher = proxyUrl ? new ProxyAgent({
|
|
950
|
+
uri: proxyUrl,
|
|
951
|
+
bodyTimeout: STREAM_BODY_TIMEOUT,
|
|
952
|
+
headersTimeout: STREAM_HEADERS_TIMEOUT
|
|
953
|
+
}) : new Agent({
|
|
954
|
+
bodyTimeout: STREAM_BODY_TIMEOUT,
|
|
955
|
+
headersTimeout: STREAM_HEADERS_TIMEOUT
|
|
956
|
+
});
|
|
957
|
+
clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
|
|
958
|
+
} catch {
|
|
942
959
|
}
|
|
943
960
|
this.client = new OpenAI(clientOptions);
|
|
944
961
|
}
|
|
@@ -1854,6 +1871,40 @@ function peelMetaNarration(content) {
|
|
|
1854
1871
|
}
|
|
1855
1872
|
return out.trim();
|
|
1856
1873
|
}
|
|
1874
|
+
var META_NARRATION_HARD_MARKERS = [
|
|
1875
|
+
/\[⚠️\s*CONTENT GENERATION MODE\]/,
|
|
1876
|
+
/CONTENT_ONLY_STREAM_REMINDER\b/,
|
|
1877
|
+
/<system-reminder>/i
|
|
1878
|
+
];
|
|
1879
|
+
var META_NARRATION_HEURISTICS = [
|
|
1880
|
+
/\bthe user (?:is asking me|wants me|is requesting|expects me)\b/i,
|
|
1881
|
+
/\blet me (?:re-?read|re-?consider|reconsider|think about|carefully (?:re-?read|consider))\b/i,
|
|
1882
|
+
/\bI'?m (?:in (?:a )?content-only|in CONTENT-ONLY|currently in)\b/i,
|
|
1883
|
+
/\bI think (?:there might be|I should|I cannot|the (?:user|best)|maybe)\b/i,
|
|
1884
|
+
/\bWait,?\s+let me\b/i,
|
|
1885
|
+
/\bActually,?\s+I\b/i,
|
|
1886
|
+
/\bI need to be honest with the user\b/i,
|
|
1887
|
+
/\bI(?:'m| am) in a special mode\b/i,
|
|
1888
|
+
/\bGiven that I cannot\b/i
|
|
1889
|
+
];
|
|
1890
|
+
function detectMetaNarration(content) {
|
|
1891
|
+
if (!content) return null;
|
|
1892
|
+
const head = content.slice(0, 2e3);
|
|
1893
|
+
for (const re of META_NARRATION_HARD_MARKERS) {
|
|
1894
|
+
if (re.test(head)) return re.source;
|
|
1895
|
+
}
|
|
1896
|
+
if (/^#{1,3}\s+\S/m.test(head)) return null;
|
|
1897
|
+
let hits = 0;
|
|
1898
|
+
let firstMatch = "";
|
|
1899
|
+
for (const re of META_NARRATION_HEURISTICS) {
|
|
1900
|
+
if (re.test(head)) {
|
|
1901
|
+
hits++;
|
|
1902
|
+
if (!firstMatch) firstMatch = re.source;
|
|
1903
|
+
if (hits >= 2) return `meta-narration:${firstMatch}`;
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
return null;
|
|
1907
|
+
}
|
|
1857
1908
|
function looksLikeDocumentBody(content) {
|
|
1858
1909
|
if (!content || content.length < 200) return false;
|
|
1859
1910
|
if (/^#{1,6}\s+\S/m.test(content)) return true;
|
|
@@ -4156,6 +4207,7 @@ export {
|
|
|
4156
4207
|
buildPhantomCorrectionMessage,
|
|
4157
4208
|
detectPseudoToolCalls,
|
|
4158
4209
|
stripPseudoToolCalls,
|
|
4210
|
+
detectMetaNarration,
|
|
4159
4211
|
looksLikeDocumentBody,
|
|
4160
4212
|
stripToolCallReminder,
|
|
4161
4213
|
TEE_FINAL_USER_NUDGE,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-3BICTI5M.js";
|
|
6
6
|
import {
|
|
7
7
|
runTestsTool
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VJE5V4H2.js";
|
|
9
9
|
import {
|
|
10
10
|
EnvLoader,
|
|
11
11
|
NetworkError,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
SUBAGENT_ALLOWED_TOOLS,
|
|
19
19
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
20
20
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-H3F2E4MD.js";
|
|
22
22
|
import {
|
|
23
23
|
fileCheckpoints
|
|
24
24
|
} from "./chunk-4BKXL7SM.js";
|
|
@@ -372,7 +372,7 @@ Important rules:
|
|
|
372
372
|
}
|
|
373
373
|
updateCwdFromCommand(command, effectiveCwd);
|
|
374
374
|
pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
|
|
375
|
-
const result =
|
|
375
|
+
const result = Buffer.isBuffer(stdout) ? stdout.toString("utf-8") : String(stdout ?? "");
|
|
376
376
|
return result || "(command completed with no output)";
|
|
377
377
|
} catch (err) {
|
|
378
378
|
pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
|
|
@@ -400,12 +400,15 @@ How to recover (pick ONE \u2014 do NOT retry the same command):
|
|
|
400
400
|
const stderr = IS_WINDOWS && Buffer.isBuffer(execErr.stderr) ? execErr.stderr.toString("utf-8").trim() : execErr.stderr?.toString().trim() ?? "";
|
|
401
401
|
const stdout = IS_WINDOWS && Buffer.isBuffer(execErr.stdout) ? execErr.stdout.toString("utf-8").trim() : execErr.stdout?.toString().trim() ?? "";
|
|
402
402
|
const combined = [stdout, stderr].filter(Boolean).join("\n");
|
|
403
|
+
const hint = buildErrorHint(command, combined);
|
|
403
404
|
throw new ToolError(
|
|
404
405
|
"bash",
|
|
405
406
|
`Exit code ${execErr.status}:
|
|
406
407
|
${combined || (execErr.message ?? "Unknown error")}
|
|
407
408
|
|
|
408
|
-
|
|
409
|
+
` + (hint ? `${hint}
|
|
410
|
+
|
|
411
|
+
` : "") + `[Command failed. Report this error to the user. Do not retry with variant commands.]`
|
|
409
412
|
);
|
|
410
413
|
}
|
|
411
414
|
}
|
|
@@ -429,6 +432,31 @@ function fixWindowsDeleteCommand(command) {
|
|
|
429
432
|
}
|
|
430
433
|
);
|
|
431
434
|
}
|
|
435
|
+
function buildErrorHint(command, stderr) {
|
|
436
|
+
const hints = [];
|
|
437
|
+
if (IS_WINDOWS && /\|\|/.test(command) && /(\|\||is not a valid argument|不是此版本中的有效|ParserError|Unexpected token)/i.test(stderr)) {
|
|
438
|
+
hints.push(
|
|
439
|
+
`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.`
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
if (IS_WINDOWS && /\bpython3\b/.test(command) && /(not recognized|is not recognized|无法将.*识别)/i.test(stderr)) {
|
|
443
|
+
hints.push(
|
|
444
|
+
`Hint: On Windows the launcher is 'python' (or 'py'), not 'python3'. Replace 'python3' with 'python' and retry.`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
if (IS_WINDOWS && /^\s*ssh\b/.test(command) && /\\"/.test(command) && /(syntax error|ERROR:|ParserError|unexpected|parser|意外的|语法|不是此版本)/i.test(stderr)) {
|
|
448
|
+
hints.push(
|
|
449
|
+
`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.`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
const colMissing = stderr.match(/column "([^"]+)" does not exist/i);
|
|
453
|
+
if (colMissing) {
|
|
454
|
+
hints.push(
|
|
455
|
+
`Hint: PostgreSQL says column "${colMissing[1]}" does not exist. Do NOT retry with a guess \u2014 query the actual schema first: \`psql -c "\\d <table>"\` or \`SELECT column_name FROM information_schema.columns WHERE table_name='<table>';\`. Common pitfall: not every table has a 'deleted' soft-delete column.`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return hints.length > 0 ? hints.map((h) => `\u{1F4A1} ${h}`).join("\n\n") : null;
|
|
459
|
+
}
|
|
432
460
|
function snapshotDir(dir) {
|
|
433
461
|
try {
|
|
434
462
|
return new Set(readdirSync(dir).map((name) => resolve(dir, name)));
|
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-BXDHW7JO.js";
|
|
40
40
|
import {
|
|
41
41
|
hasSemanticIndex,
|
|
42
42
|
semanticSearch
|
|
@@ -604,10 +604,20 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
604
604
|
]
|
|
605
605
|
};
|
|
606
606
|
async initialize(apiKey, options) {
|
|
607
|
-
|
|
607
|
+
const clientOptions = {
|
|
608
608
|
apiKey,
|
|
609
609
|
baseURL: options?.baseUrl
|
|
610
|
-
}
|
|
610
|
+
};
|
|
611
|
+
const proxyUrl = options?.proxy;
|
|
612
|
+
try {
|
|
613
|
+
const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
|
|
614
|
+
const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
|
|
615
|
+
const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
|
|
616
|
+
const dispatcher = proxyUrl ? new ProxyAgent({ uri: proxyUrl, bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT }) : new Agent({ bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT });
|
|
617
|
+
clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
|
|
618
|
+
} catch {
|
|
619
|
+
}
|
|
620
|
+
this.client = new Anthropic(clientOptions);
|
|
611
621
|
}
|
|
612
622
|
/**
|
|
613
623
|
* 将内部 MessageContentPart[] 格式转换为 Anthropic SDK 期望的 ContentBlockParam[]。
|
|
@@ -1464,13 +1474,20 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
1464
1474
|
timeout: this.defaultTimeout
|
|
1465
1475
|
};
|
|
1466
1476
|
const proxyUrl = options?.proxy;
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1477
|
+
try {
|
|
1478
|
+
const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
|
|
1479
|
+
const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
|
|
1480
|
+
const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
|
|
1481
|
+
const dispatcher = proxyUrl ? new ProxyAgent({
|
|
1482
|
+
uri: proxyUrl,
|
|
1483
|
+
bodyTimeout: STREAM_BODY_TIMEOUT,
|
|
1484
|
+
headersTimeout: STREAM_HEADERS_TIMEOUT
|
|
1485
|
+
}) : new Agent({
|
|
1486
|
+
bodyTimeout: STREAM_BODY_TIMEOUT,
|
|
1487
|
+
headersTimeout: STREAM_HEADERS_TIMEOUT
|
|
1488
|
+
});
|
|
1489
|
+
clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
|
|
1490
|
+
} catch {
|
|
1474
1491
|
}
|
|
1475
1492
|
this.client = new OpenAI(clientOptions);
|
|
1476
1493
|
}
|
|
@@ -2268,6 +2285,40 @@ function peelMetaNarration(content) {
|
|
|
2268
2285
|
}
|
|
2269
2286
|
return out.trim();
|
|
2270
2287
|
}
|
|
2288
|
+
var META_NARRATION_HARD_MARKERS = [
|
|
2289
|
+
/\[⚠️\s*CONTENT GENERATION MODE\]/,
|
|
2290
|
+
/CONTENT_ONLY_STREAM_REMINDER\b/,
|
|
2291
|
+
/<system-reminder>/i
|
|
2292
|
+
];
|
|
2293
|
+
var META_NARRATION_HEURISTICS = [
|
|
2294
|
+
/\bthe user (?:is asking me|wants me|is requesting|expects me)\b/i,
|
|
2295
|
+
/\blet me (?:re-?read|re-?consider|reconsider|think about|carefully (?:re-?read|consider))\b/i,
|
|
2296
|
+
/\bI'?m (?:in (?:a )?content-only|in CONTENT-ONLY|currently in)\b/i,
|
|
2297
|
+
/\bI think (?:there might be|I should|I cannot|the (?:user|best)|maybe)\b/i,
|
|
2298
|
+
/\bWait,?\s+let me\b/i,
|
|
2299
|
+
/\bActually,?\s+I\b/i,
|
|
2300
|
+
/\bI need to be honest with the user\b/i,
|
|
2301
|
+
/\bI(?:'m| am) in a special mode\b/i,
|
|
2302
|
+
/\bGiven that I cannot\b/i
|
|
2303
|
+
];
|
|
2304
|
+
function detectMetaNarration(content) {
|
|
2305
|
+
if (!content) return null;
|
|
2306
|
+
const head = content.slice(0, 2e3);
|
|
2307
|
+
for (const re of META_NARRATION_HARD_MARKERS) {
|
|
2308
|
+
if (re.test(head)) return re.source;
|
|
2309
|
+
}
|
|
2310
|
+
if (/^#{1,3}\s+\S/m.test(head)) return null;
|
|
2311
|
+
let hits = 0;
|
|
2312
|
+
let firstMatch = "";
|
|
2313
|
+
for (const re of META_NARRATION_HEURISTICS) {
|
|
2314
|
+
if (re.test(head)) {
|
|
2315
|
+
hits++;
|
|
2316
|
+
if (!firstMatch) firstMatch = re.source;
|
|
2317
|
+
if (hits >= 2) return `meta-narration:${firstMatch}`;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
return null;
|
|
2321
|
+
}
|
|
2271
2322
|
function looksLikeDocumentBody(content) {
|
|
2272
2323
|
if (!content || content.length < 200) return false;
|
|
2273
2324
|
if (/^#{1,6}\s+\S/m.test(content)) return true;
|
|
@@ -3885,7 +3936,7 @@ Important rules:
|
|
|
3885
3936
|
}
|
|
3886
3937
|
updateCwdFromCommand(command, effectiveCwd);
|
|
3887
3938
|
pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
|
|
3888
|
-
const result =
|
|
3939
|
+
const result = Buffer.isBuffer(stdout) ? stdout.toString("utf-8") : String(stdout ?? "");
|
|
3889
3940
|
return result || "(command completed with no output)";
|
|
3890
3941
|
} catch (err) {
|
|
3891
3942
|
pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
|
|
@@ -3913,12 +3964,15 @@ How to recover (pick ONE \u2014 do NOT retry the same command):
|
|
|
3913
3964
|
const stderr = IS_WINDOWS && Buffer.isBuffer(execErr.stderr) ? execErr.stderr.toString("utf-8").trim() : execErr.stderr?.toString().trim() ?? "";
|
|
3914
3965
|
const stdout = IS_WINDOWS && Buffer.isBuffer(execErr.stdout) ? execErr.stdout.toString("utf-8").trim() : execErr.stdout?.toString().trim() ?? "";
|
|
3915
3966
|
const combined = [stdout, stderr].filter(Boolean).join("\n");
|
|
3967
|
+
const hint = buildErrorHint(command, combined);
|
|
3916
3968
|
throw new ToolError(
|
|
3917
3969
|
"bash",
|
|
3918
3970
|
`Exit code ${execErr.status}:
|
|
3919
3971
|
${combined || (execErr.message ?? "Unknown error")}
|
|
3920
3972
|
|
|
3921
|
-
|
|
3973
|
+
` + (hint ? `${hint}
|
|
3974
|
+
|
|
3975
|
+
` : "") + `[Command failed. Report this error to the user. Do not retry with variant commands.]`
|
|
3922
3976
|
);
|
|
3923
3977
|
}
|
|
3924
3978
|
}
|
|
@@ -3942,6 +3996,31 @@ function fixWindowsDeleteCommand(command) {
|
|
|
3942
3996
|
}
|
|
3943
3997
|
);
|
|
3944
3998
|
}
|
|
3999
|
+
function buildErrorHint(command, stderr) {
|
|
4000
|
+
const hints = [];
|
|
4001
|
+
if (IS_WINDOWS && /\|\|/.test(command) && /(\|\||is not a valid argument|不是此版本中的有效|ParserError|Unexpected token)/i.test(stderr)) {
|
|
4002
|
+
hints.push(
|
|
4003
|
+
`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.`
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
4006
|
+
if (IS_WINDOWS && /\bpython3\b/.test(command) && /(not recognized|is not recognized|无法将.*识别)/i.test(stderr)) {
|
|
4007
|
+
hints.push(
|
|
4008
|
+
`Hint: On Windows the launcher is 'python' (or 'py'), not 'python3'. Replace 'python3' with 'python' and retry.`
|
|
4009
|
+
);
|
|
4010
|
+
}
|
|
4011
|
+
if (IS_WINDOWS && /^\s*ssh\b/.test(command) && /\\"/.test(command) && /(syntax error|ERROR:|ParserError|unexpected|parser|意外的|语法|不是此版本)/i.test(stderr)) {
|
|
4012
|
+
hints.push(
|
|
4013
|
+
`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.`
|
|
4014
|
+
);
|
|
4015
|
+
}
|
|
4016
|
+
const colMissing = stderr.match(/column "([^"]+)" does not exist/i);
|
|
4017
|
+
if (colMissing) {
|
|
4018
|
+
hints.push(
|
|
4019
|
+
`Hint: PostgreSQL says column "${colMissing[1]}" does not exist. Do NOT retry with a guess \u2014 query the actual schema first: \`psql -c "\\d <table>"\` or \`SELECT column_name FROM information_schema.columns WHERE table_name='<table>';\`. Common pitfall: not every table has a 'deleted' soft-delete column.`
|
|
4020
|
+
);
|
|
4021
|
+
}
|
|
4022
|
+
return hints.length > 0 ? hints.map((h) => `\u{1F4A1} ${h}`).join("\n\n") : null;
|
|
4023
|
+
}
|
|
3945
4024
|
function snapshotDir(dir) {
|
|
3946
4025
|
try {
|
|
3947
4026
|
return new Set(readdirSync2(dir).map((name) => resolve(dir, name)));
|
|
@@ -10842,6 +10921,31 @@ ${summaryResult.content}`,
|
|
|
10842
10921
|
await new Promise((resolve7, reject) => {
|
|
10843
10922
|
fileStream.end((err) => err ? reject(err) : resolve7());
|
|
10844
10923
|
});
|
|
10924
|
+
const metaMatch = detectMetaNarration(fullContent);
|
|
10925
|
+
if (metaMatch) {
|
|
10926
|
+
try {
|
|
10927
|
+
unlinkSync4(saveToFile);
|
|
10928
|
+
} catch {
|
|
10929
|
+
}
|
|
10930
|
+
isError = true;
|
|
10931
|
+
summary = `[save_last_response REJECTED] Your output was internal reasoning / meta-narration (e.g. "Let me re-read\u2026", "the user is asking me to\u2026") instead of the requested document body (matched: ${metaMatch}). ${saveToFile} was NOT saved.
|
|
10932
|
+
|
|
10933
|
+
This fresh stream has NO tools. Produce ONLY the document body: start with a markdown heading and write the full content. Do NOT narrate that you will produce the document \u2014 produce it.`;
|
|
10934
|
+
if (teeUsage) {
|
|
10935
|
+
roundUsage.inputTokens += teeUsage.inputTokens;
|
|
10936
|
+
roundUsage.outputTokens += teeUsage.outputTokens;
|
|
10937
|
+
roundUsage.cacheCreationTokens += teeUsage.cacheCreationTokens ?? 0;
|
|
10938
|
+
roundUsage.cacheReadTokens += teeUsage.cacheReadTokens ?? 0;
|
|
10939
|
+
}
|
|
10940
|
+
this.send({
|
|
10941
|
+
type: "tool_call_result",
|
|
10942
|
+
callId: call.id,
|
|
10943
|
+
result: summary,
|
|
10944
|
+
isError: true,
|
|
10945
|
+
endTime: Date.now()
|
|
10946
|
+
});
|
|
10947
|
+
return { content: "", summary, isError: true };
|
|
10948
|
+
}
|
|
10845
10949
|
const pseudoMatch = detectPseudoToolCalls(fullContent);
|
|
10846
10950
|
if (pseudoMatch) {
|
|
10847
10951
|
const cleaned = stripPseudoToolCalls(fullContent);
|
|
@@ -10879,9 +10983,13 @@ ${summaryResult.content}`,
|
|
|
10879
10983
|
} catch {
|
|
10880
10984
|
}
|
|
10881
10985
|
}
|
|
10986
|
+
try {
|
|
10987
|
+
unlinkSync4(saveToFile);
|
|
10988
|
+
} catch {
|
|
10989
|
+
}
|
|
10882
10990
|
isError = true;
|
|
10883
10991
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10884
|
-
summary = `[save_last_response failed] ${msg}
|
|
10992
|
+
summary = `[save_last_response failed] streaming was interrupted: ${msg}. ${saveToFile} (partial) was deleted. Retry \u2014 and consider producing a more compact output (split very large reports across multiple save_last_response calls if the previous attempt timed out).`;
|
|
10885
10993
|
}
|
|
10886
10994
|
this.send({
|
|
10887
10995
|
type: "tool_call_result",
|
|
@@ -11960,7 +12068,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
11960
12068
|
case "test": {
|
|
11961
12069
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
11962
12070
|
try {
|
|
11963
|
-
const { executeTests } = await import("./run-tests-
|
|
12071
|
+
const { executeTests } = await import("./run-tests-OKQEMYZK.js");
|
|
11964
12072
|
const argStr = args.join(" ").trim();
|
|
11965
12073
|
let testArgs = {};
|
|
11966
12074
|
if (argStr) {
|
|
@@ -12944,11 +13052,12 @@ async function setupProxy(configProxy) {
|
|
|
12944
13052
|
}
|
|
12945
13053
|
|
|
12946
13054
|
// src/web/auth.ts
|
|
12947
|
-
import { existsSync as existsSync21, readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, readdirSync as readdirSync10, copyFileSync } from "fs";
|
|
13055
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, readdirSync as readdirSync10, copyFileSync, renameSync as renameSync2, unlinkSync as unlinkSync5 } from "fs";
|
|
12948
13056
|
import { join as join14 } from "path";
|
|
12949
13057
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
12950
13058
|
var USERS_FILE = "users.json";
|
|
12951
13059
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
13060
|
+
var TOKEN_EXPIRY_MS = TOKEN_EXPIRY_HOURS * 3600 * 1e3;
|
|
12952
13061
|
var USERS_DIR = "users";
|
|
12953
13062
|
var LOGIN_MAX_FAILS = 5;
|
|
12954
13063
|
var LOGIN_LOCKOUT_MS = 15 * 60 * 1e3;
|
|
@@ -13192,7 +13301,17 @@ var AuthManager = class {
|
|
|
13192
13301
|
}
|
|
13193
13302
|
saveDB(db) {
|
|
13194
13303
|
mkdirSync10(this.baseDir, { recursive: true });
|
|
13195
|
-
|
|
13304
|
+
const tmp = `${this.usersFile}.tmp`;
|
|
13305
|
+
try {
|
|
13306
|
+
writeFileSync9(tmp, JSON.stringify(db, null, 2), "utf-8");
|
|
13307
|
+
renameSync2(tmp, this.usersFile);
|
|
13308
|
+
} catch (err) {
|
|
13309
|
+
try {
|
|
13310
|
+
unlinkSync5(tmp);
|
|
13311
|
+
} catch {
|
|
13312
|
+
}
|
|
13313
|
+
throw err;
|
|
13314
|
+
}
|
|
13196
13315
|
}
|
|
13197
13316
|
/** Legacy hash — kept only for migrating old users (v0.2.x) */
|
|
13198
13317
|
hashPasswordLegacy(password, salt) {
|
|
@@ -13406,7 +13525,7 @@ async function startWebServer(options = {}) {
|
|
|
13406
13525
|
}
|
|
13407
13526
|
const token = authManager.login(username, password);
|
|
13408
13527
|
console.log(` \u2713 User registered via API: ${username}${firstRun ? " (first-run)" : ""}`);
|
|
13409
|
-
res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge:
|
|
13528
|
+
res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
|
|
13410
13529
|
res.json({ success: true, username });
|
|
13411
13530
|
});
|
|
13412
13531
|
app.post("/api/auth/login", (req, res) => {
|
|
@@ -13420,7 +13539,7 @@ async function startWebServer(options = {}) {
|
|
|
13420
13539
|
res.status(401).json({ error: "Invalid username or password" });
|
|
13421
13540
|
return;
|
|
13422
13541
|
}
|
|
13423
|
-
res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge:
|
|
13542
|
+
res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
|
|
13424
13543
|
res.json({ success: true, username });
|
|
13425
13544
|
});
|
|
13426
13545
|
app.post("/api/auth/logout", (_req, res) => {
|
|
@@ -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-SCH3JUKT.js");
|
|
390
390
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
391
391
|
let interrupted = false;
|
|
392
392
|
const onSigint = () => {
|