dskcode 0.1.15 → 0.1.17
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 +54 -16
- package/dist/{chunk-HWZZJB6I.js → chunk-BA7UJPKJ.js} +4 -1
- package/dist/{chunk-HWZZJB6I.js.map → chunk-BA7UJPKJ.js.map} +1 -1
- package/dist/{deepseek-OUOEENTK.js → deepseek-UJXV2D6K.js} +2 -2
- package/dist/index.js +1733 -266
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- /package/dist/{deepseek-OUOEENTK.js.map → deepseek-UJXV2D6K.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
DeepSeekProvider,
|
|
4
4
|
ModelNotSupportedError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-BA7UJPKJ.js";
|
|
6
6
|
import {
|
|
7
7
|
SUPPORTED_MODELS,
|
|
8
8
|
calculateCost,
|
|
@@ -451,7 +451,7 @@ async function promptForApiKey() {
|
|
|
451
451
|
input: process.stdin,
|
|
452
452
|
output: process.stdout
|
|
453
453
|
});
|
|
454
|
-
return new Promise((
|
|
454
|
+
return new Promise((resolve2) => {
|
|
455
455
|
let resolved = false;
|
|
456
456
|
const cleanup = () => {
|
|
457
457
|
if (resolved) return;
|
|
@@ -462,7 +462,7 @@ async function promptForApiKey() {
|
|
|
462
462
|
const onKeypress = (_, key) => {
|
|
463
463
|
if (key.ctrl && key.name === "c") {
|
|
464
464
|
cleanup();
|
|
465
|
-
|
|
465
|
+
resolve2(null);
|
|
466
466
|
}
|
|
467
467
|
};
|
|
468
468
|
process.stdin.on("keypress", onKeypress);
|
|
@@ -473,15 +473,15 @@ async function promptForApiKey() {
|
|
|
473
473
|
const trimmed = answer.trim();
|
|
474
474
|
if (!trimmed) {
|
|
475
475
|
console.log(chalk2.red(" \u2716 API Key \u4E0D\u80FD\u4E3A\u7A7A"));
|
|
476
|
-
|
|
476
|
+
resolve2(null);
|
|
477
477
|
return;
|
|
478
478
|
}
|
|
479
479
|
if (trimmed.length < MIN_API_KEY_LENGTH) {
|
|
480
480
|
console.log(chalk2.red(" \u2716 API Key \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u957F\u5EA6\u81F3\u5C11 10 \u4F4D"));
|
|
481
|
-
|
|
481
|
+
resolve2(null);
|
|
482
482
|
return;
|
|
483
483
|
}
|
|
484
|
-
|
|
484
|
+
resolve2(trimmed);
|
|
485
485
|
}
|
|
486
486
|
);
|
|
487
487
|
});
|
|
@@ -490,7 +490,7 @@ async function promptForApiKey() {
|
|
|
490
490
|
// src/cli/skill-import.ts
|
|
491
491
|
import { createInterface as createInterface2 } from "readline";
|
|
492
492
|
import { existsSync as existsSync2, statSync, lstatSync } from "fs";
|
|
493
|
-
import { mkdir as mkdir2, readdir, cp, access, realpath } from "fs/promises";
|
|
493
|
+
import { mkdir as mkdir2, readdir, cp, access, realpath, readFile as readFile2 } from "fs/promises";
|
|
494
494
|
import { join as join2 } from "path";
|
|
495
495
|
import chalk3 from "chalk";
|
|
496
496
|
function getClaudeSkillsDir() {
|
|
@@ -521,8 +521,8 @@ async function listClaudeSkills() {
|
|
|
521
521
|
const skills = [];
|
|
522
522
|
for (const name of entries) {
|
|
523
523
|
const full = join2(claudeDir, name);
|
|
524
|
-
const
|
|
525
|
-
if (
|
|
524
|
+
const stat5 = statSync(full);
|
|
525
|
+
if (stat5.isDirectory() && await isSkillDir(full)) {
|
|
526
526
|
skills.push(name);
|
|
527
527
|
}
|
|
528
528
|
}
|
|
@@ -533,7 +533,7 @@ function askConfirm(question) {
|
|
|
533
533
|
input: process.stdin,
|
|
534
534
|
output: process.stdout
|
|
535
535
|
});
|
|
536
|
-
return new Promise((
|
|
536
|
+
return new Promise((resolve2) => {
|
|
537
537
|
let resolved = false;
|
|
538
538
|
const cleanup = () => {
|
|
539
539
|
if (resolved) return;
|
|
@@ -544,14 +544,14 @@ function askConfirm(question) {
|
|
|
544
544
|
const onKeypress = (_, key) => {
|
|
545
545
|
if (key.ctrl && key.name === "c") {
|
|
546
546
|
cleanup();
|
|
547
|
-
|
|
547
|
+
resolve2(false);
|
|
548
548
|
}
|
|
549
549
|
};
|
|
550
550
|
process.stdin.on("keypress", onKeypress);
|
|
551
551
|
rl.question(question, (answer) => {
|
|
552
552
|
cleanup();
|
|
553
553
|
const trimmed = answer.trim().toLowerCase();
|
|
554
|
-
|
|
554
|
+
resolve2(trimmed === "y" || trimmed === "yes");
|
|
555
555
|
});
|
|
556
556
|
});
|
|
557
557
|
}
|
|
@@ -611,8 +611,8 @@ async function countProjectLocalSkills(cwd) {
|
|
|
611
611
|
let count = 0;
|
|
612
612
|
for (const name of entries) {
|
|
613
613
|
const full = join2(skillDir, name);
|
|
614
|
-
const
|
|
615
|
-
if (
|
|
614
|
+
const stat5 = statSync(full);
|
|
615
|
+
if (stat5.isDirectory() && await isSkillDir(full)) {
|
|
616
616
|
count++;
|
|
617
617
|
}
|
|
618
618
|
}
|
|
@@ -629,8 +629,8 @@ async function hasDskcodeSkills() {
|
|
|
629
629
|
}
|
|
630
630
|
for (const name of entries) {
|
|
631
631
|
const full = join2(dskcodeDir, name);
|
|
632
|
-
const
|
|
633
|
-
if (
|
|
632
|
+
const stat5 = statSync(full);
|
|
633
|
+
if (stat5.isDirectory() && await isSkillDir(full)) {
|
|
634
634
|
return true;
|
|
635
635
|
}
|
|
636
636
|
}
|
|
@@ -687,10 +687,94 @@ async function promptImportClaudeSkills(cwd) {
|
|
|
687
687
|
console.log(chalk3.dim(" \u4F60\u53EF\u4EE5\u7A0D\u540E\u624B\u52A8\u590D\u5236 ~/.claude/skills \u5230 ~/.dskcode/skills\n"));
|
|
688
688
|
}
|
|
689
689
|
}
|
|
690
|
+
async function listDskcodeSkills() {
|
|
691
|
+
const dskcodeDir = getDskcodeSkillsDir();
|
|
692
|
+
if (!existsSync2(dskcodeDir)) return [];
|
|
693
|
+
let entries;
|
|
694
|
+
try {
|
|
695
|
+
entries = await readdir(dskcodeDir);
|
|
696
|
+
} catch {
|
|
697
|
+
return [];
|
|
698
|
+
}
|
|
699
|
+
const skills = [];
|
|
700
|
+
for (const name of entries) {
|
|
701
|
+
const full = join2(dskcodeDir, name);
|
|
702
|
+
if (statSync(full).isDirectory() && await isSkillDir(full)) {
|
|
703
|
+
skills.push(name);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return skills;
|
|
707
|
+
}
|
|
708
|
+
async function listProjectSkills(cwd) {
|
|
709
|
+
const skillDir = getProjectSkillDir(cwd);
|
|
710
|
+
if (!existsSync2(skillDir)) return [];
|
|
711
|
+
let entries;
|
|
712
|
+
try {
|
|
713
|
+
entries = await readdir(skillDir);
|
|
714
|
+
} catch {
|
|
715
|
+
return [];
|
|
716
|
+
}
|
|
717
|
+
const skills = [];
|
|
718
|
+
for (const name of entries) {
|
|
719
|
+
const full = join2(skillDir, name);
|
|
720
|
+
if (statSync(full).isDirectory() && await isSkillDir(full)) {
|
|
721
|
+
skills.push(name);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return skills;
|
|
725
|
+
}
|
|
726
|
+
async function readSkillInfo(skillDir) {
|
|
727
|
+
try {
|
|
728
|
+
const content = await readFile2(join2(skillDir, "SKILL.md"), "utf-8");
|
|
729
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
730
|
+
if (!match) return null;
|
|
731
|
+
const frontMatter = match[1];
|
|
732
|
+
const nameMatch = frontMatter.match(/^name:\s*(.+)$/m);
|
|
733
|
+
const descMatch = frontMatter.match(/^description:\s*(.+)$/m);
|
|
734
|
+
return {
|
|
735
|
+
name: nameMatch?.[1]?.trim() ?? "",
|
|
736
|
+
description: descMatch?.[1]?.trim() ?? ""
|
|
737
|
+
};
|
|
738
|
+
} catch {
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
async function getAllSkills(cwd) {
|
|
743
|
+
const [globalNames, localNames] = await Promise.all([
|
|
744
|
+
listDskcodeSkills(),
|
|
745
|
+
listProjectSkills(cwd)
|
|
746
|
+
]);
|
|
747
|
+
const seen = /* @__PURE__ */ new Set();
|
|
748
|
+
const allNames = [];
|
|
749
|
+
for (const name of [...localNames, ...globalNames]) {
|
|
750
|
+
if (!seen.has(name)) {
|
|
751
|
+
seen.add(name);
|
|
752
|
+
allNames.push(name);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const dskcodeDir = getDskcodeSkillsDir();
|
|
756
|
+
const projectDir = getProjectSkillDir(cwd);
|
|
757
|
+
const results = [];
|
|
758
|
+
for (const name of allNames) {
|
|
759
|
+
const localPath = join2(projectDir, name);
|
|
760
|
+
let info = null;
|
|
761
|
+
if (existsSync2(localPath)) {
|
|
762
|
+
info = await readSkillInfo(localPath);
|
|
763
|
+
}
|
|
764
|
+
if (!info) {
|
|
765
|
+
const globalPath = join2(dskcodeDir, name);
|
|
766
|
+
if (existsSync2(globalPath)) {
|
|
767
|
+
info = await readSkillInfo(globalPath);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
results.push(info ?? { name, description: "" });
|
|
771
|
+
}
|
|
772
|
+
return results;
|
|
773
|
+
}
|
|
690
774
|
|
|
691
775
|
// src/cli/index.tsx
|
|
692
|
-
import { readFile as
|
|
693
|
-
import { join as
|
|
776
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
777
|
+
import { join as join8 } from "path";
|
|
694
778
|
|
|
695
779
|
// src/ui/RenderScope.tsx
|
|
696
780
|
import { render } from "ink";
|
|
@@ -732,7 +816,7 @@ var LOGO_LINES = [
|
|
|
732
816
|
];
|
|
733
817
|
|
|
734
818
|
// src/ui/ChatSession.tsx
|
|
735
|
-
import { Box as
|
|
819
|
+
import { Box as Box8, Text as Text9, useInput, Static } from "ink";
|
|
736
820
|
import TextInput from "ink-text-input";
|
|
737
821
|
import { useEffect as useEffect3, useState as useState3, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
738
822
|
|
|
@@ -768,7 +852,7 @@ function useDoubleCtrlC(onExit) {
|
|
|
768
852
|
import { Box as Box4, Text as Text5 } from "ink";
|
|
769
853
|
|
|
770
854
|
// src/provider/cost-tracker.ts
|
|
771
|
-
import { mkdir as mkdir3, readFile as
|
|
855
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
772
856
|
import { join as join3 } from "path";
|
|
773
857
|
var CostTracker = class {
|
|
774
858
|
#costDir;
|
|
@@ -1003,7 +1087,7 @@ var CostTracker = class {
|
|
|
1003
1087
|
async #loadStore() {
|
|
1004
1088
|
const filePath = join3(this.#costDir, "history.json");
|
|
1005
1089
|
try {
|
|
1006
|
-
const raw = await
|
|
1090
|
+
const raw = await readFile3(filePath, "utf-8");
|
|
1007
1091
|
return JSON.parse(raw);
|
|
1008
1092
|
} catch {
|
|
1009
1093
|
return { version: 1, daily: {} };
|
|
@@ -1196,6 +1280,95 @@ function AssistantMessage({
|
|
|
1196
1280
|
] });
|
|
1197
1281
|
}
|
|
1198
1282
|
|
|
1283
|
+
// src/ui/DiffPreview.tsx
|
|
1284
|
+
import { Box as Box5, Text as Text6 } from "ink";
|
|
1285
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1286
|
+
function DiffPreview({ diff }) {
|
|
1287
|
+
const { patch, additions, deletions, existedBefore, filePath } = diff;
|
|
1288
|
+
if (!patch || patch.length === 0) {
|
|
1289
|
+
return null;
|
|
1290
|
+
}
|
|
1291
|
+
const lines = patch.split("\n");
|
|
1292
|
+
const fileName = filePath.replace(/\\/g, "/").split("/").pop() ?? filePath;
|
|
1293
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
1294
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", gap: 1, children: [
|
|
1295
|
+
/* @__PURE__ */ jsxs5(Text6, { bold: true, color: "#00ffff", children: [
|
|
1296
|
+
"\u{1F4DD} ",
|
|
1297
|
+
existedBefore ? "\u4FEE\u6539" : "\u65B0\u5EFA",
|
|
1298
|
+
":"
|
|
1299
|
+
] }),
|
|
1300
|
+
/* @__PURE__ */ jsx5(Text6, { bold: true, children: fileName }),
|
|
1301
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "#555555", children: [
|
|
1302
|
+
"(+",
|
|
1303
|
+
additions,
|
|
1304
|
+
" -",
|
|
1305
|
+
deletions,
|
|
1306
|
+
")"
|
|
1307
|
+
] })
|
|
1308
|
+
] }),
|
|
1309
|
+
/* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginLeft: 2, children: lines.map((line, i) => /* @__PURE__ */ jsx5(DiffLine, { line }, i)) })
|
|
1310
|
+
] });
|
|
1311
|
+
}
|
|
1312
|
+
function DiffLine({ line }) {
|
|
1313
|
+
if (line.startsWith("---") || line.startsWith("+++")) {
|
|
1314
|
+
return /* @__PURE__ */ jsx5(Text6, { color: "#00cccc", bold: true, children: line });
|
|
1315
|
+
}
|
|
1316
|
+
if (line.startsWith("@@")) {
|
|
1317
|
+
return /* @__PURE__ */ jsx5(Text6, { color: "#00cccc", children: line });
|
|
1318
|
+
}
|
|
1319
|
+
if (line.startsWith("+")) {
|
|
1320
|
+
return /* @__PURE__ */ jsx5(Text6, { color: "#22c55e", children: line });
|
|
1321
|
+
}
|
|
1322
|
+
if (line.startsWith("-")) {
|
|
1323
|
+
return /* @__PURE__ */ jsx5(Text6, { color: "#ef4444", children: line });
|
|
1324
|
+
}
|
|
1325
|
+
return /* @__PURE__ */ jsx5(Text6, { color: "#6b7280", children: line });
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// src/ui/SkillSelector.tsx
|
|
1329
|
+
import { Box as Box6, Text as Text7 } from "ink";
|
|
1330
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1331
|
+
var HIGHLIGHT_COLOR = "#00bfff";
|
|
1332
|
+
function SkillSelector({ skills, input, selectedIndex }) {
|
|
1333
|
+
const match = input.match(/(?:^|\s)\/([^/]*)$/);
|
|
1334
|
+
if (!match) return null;
|
|
1335
|
+
const query = match[1].toLowerCase().trim();
|
|
1336
|
+
if (skills.length === 0) return null;
|
|
1337
|
+
const matched = !query && input.startsWith("/") ? skills.slice(0, 3) : skills.filter((s) => s.name.toLowerCase().includes(query)).slice(0, 3);
|
|
1338
|
+
if (query && matched.length > 0 && matched.some((s) => s.name.toLowerCase() === query)) return null;
|
|
1339
|
+
if (matched.length === 0) return null;
|
|
1340
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginLeft: 4, marginTop: 1, children: [
|
|
1341
|
+
/* @__PURE__ */ jsx6(Text7, { color: "#808080", dimColor: true, children: "\u652F\u6301\u7684 Skill\uFF1A" }),
|
|
1342
|
+
matched.map((skill, i) => /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsxs6(Text7, { color: i === selectedIndex ? HIGHLIGHT_COLOR : "#808080", children: [
|
|
1343
|
+
i === selectedIndex ? " \u203A " : " ",
|
|
1344
|
+
skill.name
|
|
1345
|
+
] }) }, skill.name)),
|
|
1346
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text7, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Tab \u8865\u5168" }) })
|
|
1347
|
+
] });
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// src/ui/FileSelector.tsx
|
|
1351
|
+
import { Box as Box7, Text as Text8 } from "ink";
|
|
1352
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1353
|
+
var HIGHLIGHT_COLOR2 = "#00ff41";
|
|
1354
|
+
function FileSelector({ files, input, selectedIndex }) {
|
|
1355
|
+
const match = input.match(/(?:^|\s)@([^@]*)$/);
|
|
1356
|
+
if (!match) return null;
|
|
1357
|
+
const query = match[1].toLowerCase().trim();
|
|
1358
|
+
if (files.length === 0) return null;
|
|
1359
|
+
const matched = !query && input.startsWith("@") ? files.slice(0, 5) : files.filter((f) => f.toLowerCase().includes(query)).slice(0, 5);
|
|
1360
|
+
if (query && matched.some((f) => f.toLowerCase() === query)) return null;
|
|
1361
|
+
if (matched.length === 0) return null;
|
|
1362
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginLeft: 4, marginTop: 1, children: [
|
|
1363
|
+
/* @__PURE__ */ jsx7(Text8, { color: "#808080", dimColor: true, children: "\u9879\u76EE\u6587\u4EF6\uFF1A" }),
|
|
1364
|
+
matched.map((file, i) => /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text8, { color: i === selectedIndex ? HIGHLIGHT_COLOR2 : "#808080", children: [
|
|
1365
|
+
i === selectedIndex ? " \u203A " : " ",
|
|
1366
|
+
file
|
|
1367
|
+
] }) }, file)),
|
|
1368
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text8, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Tab \u8865\u5168" }) })
|
|
1369
|
+
] });
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1199
1372
|
// src/provider/registry.ts
|
|
1200
1373
|
var ProviderRegistry = class {
|
|
1201
1374
|
#factories = /* @__PURE__ */ new Map();
|
|
@@ -1345,17 +1518,132 @@ ${opts.projectContext}`);
|
|
|
1345
1518
|
return sections.join("\n\n");
|
|
1346
1519
|
}
|
|
1347
1520
|
|
|
1521
|
+
// src/tool/registry.ts
|
|
1522
|
+
var ToolRegistry = class {
|
|
1523
|
+
#tools = /* @__PURE__ */ new Map();
|
|
1524
|
+
#disabledNames;
|
|
1525
|
+
constructor(opts) {
|
|
1526
|
+
this.#disabledNames = new Set(opts?.disabledTools ?? []);
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* 注册一个工具。
|
|
1530
|
+
* 如果同名工具已存在则抛出错误。
|
|
1531
|
+
*/
|
|
1532
|
+
register(tool) {
|
|
1533
|
+
if (this.#tools.has(tool.name)) {
|
|
1534
|
+
throw new Error(`\u5DE5\u5177 "${tool.name}" \u5DF2\u6CE8\u518C\uFF0C\u4E0D\u80FD\u91CD\u590D\u6CE8\u518C`);
|
|
1535
|
+
}
|
|
1536
|
+
this.#tools.set(tool.name, tool);
|
|
1537
|
+
return this;
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* 批量注册工具。
|
|
1541
|
+
*/
|
|
1542
|
+
registerAll(tools) {
|
|
1543
|
+
for (const tool of tools) {
|
|
1544
|
+
this.register(tool);
|
|
1545
|
+
}
|
|
1546
|
+
return this;
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* 注销一个工具。
|
|
1550
|
+
*/
|
|
1551
|
+
unregister(name) {
|
|
1552
|
+
return this.#tools.delete(name);
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* 按名称获取工具。
|
|
1556
|
+
* 如果工具被禁用或不存在,返回 undefined。
|
|
1557
|
+
*/
|
|
1558
|
+
get(name) {
|
|
1559
|
+
if (this.#disabledNames.has(name)) return void 0;
|
|
1560
|
+
return this.#tools.get(name);
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* 获取所有启用的工具列表。
|
|
1564
|
+
*/
|
|
1565
|
+
list() {
|
|
1566
|
+
const result = [];
|
|
1567
|
+
for (const [name, tool] of this.#tools) {
|
|
1568
|
+
if (!this.#disabledNames.has(name)) {
|
|
1569
|
+
result.push(tool);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
return result;
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* 获取所有已注册的工具名称(含禁用的)。
|
|
1576
|
+
*/
|
|
1577
|
+
names() {
|
|
1578
|
+
return [...this.#tools.keys()];
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* 检查工具是否已注册(含禁用的)。
|
|
1582
|
+
*/
|
|
1583
|
+
has(name) {
|
|
1584
|
+
return this.#tools.has(name);
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* 检查工具是否已启用。
|
|
1588
|
+
*/
|
|
1589
|
+
isEnabled(name) {
|
|
1590
|
+
return this.#tools.has(name) && !this.#disabledNames.has(name);
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* 禁用一个工具。
|
|
1594
|
+
*/
|
|
1595
|
+
disable(name) {
|
|
1596
|
+
this.#disabledNames.add(name);
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* 启用一个之前被禁用的工具。
|
|
1600
|
+
*/
|
|
1601
|
+
enable(name) {
|
|
1602
|
+
this.#disabledNames.delete(name);
|
|
1603
|
+
}
|
|
1604
|
+
/**
|
|
1605
|
+
* 执行指定工具。
|
|
1606
|
+
*
|
|
1607
|
+
* @returns 工具执行结果;如果工具不存在或被禁用,返回失败结果
|
|
1608
|
+
*/
|
|
1609
|
+
async execute(name, args, ctx) {
|
|
1610
|
+
const tool = this.get(name);
|
|
1611
|
+
if (!tool) {
|
|
1612
|
+
return {
|
|
1613
|
+
success: false,
|
|
1614
|
+
data: `\u5DE5\u5177 "${name}" \u4E0D\u5B58\u5728\u6216\u5DF2\u88AB\u7981\u7528`,
|
|
1615
|
+
error: "TOOL_NOT_FOUND"
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
try {
|
|
1619
|
+
return await tool.execute(args, ctx);
|
|
1620
|
+
} catch (err) {
|
|
1621
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1622
|
+
return {
|
|
1623
|
+
success: false,
|
|
1624
|
+
data: `\u5DE5\u5177 "${name}" \u6267\u884C\u5F02\u5E38\uFF1A${message}`,
|
|
1625
|
+
error: "EXECUTION_ERROR"
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
};
|
|
1630
|
+
|
|
1348
1631
|
// src/agent/index.ts
|
|
1349
1632
|
var Session = class {
|
|
1350
1633
|
#messages = [];
|
|
1351
1634
|
#provider;
|
|
1352
|
-
#
|
|
1635
|
+
#toolRegistry;
|
|
1353
1636
|
#costTracker;
|
|
1354
1637
|
#options;
|
|
1355
1638
|
#abortController = new AbortController();
|
|
1356
1639
|
constructor(provider, tools = [], costTracker, options) {
|
|
1357
1640
|
this.#provider = provider;
|
|
1358
|
-
|
|
1641
|
+
if (tools instanceof ToolRegistry) {
|
|
1642
|
+
this.#toolRegistry = tools;
|
|
1643
|
+
} else {
|
|
1644
|
+
this.#toolRegistry = new ToolRegistry();
|
|
1645
|
+
this.#toolRegistry.registerAll(tools);
|
|
1646
|
+
}
|
|
1359
1647
|
this.#costTracker = costTracker ?? new CostTracker();
|
|
1360
1648
|
this.#options = {
|
|
1361
1649
|
cwd: options?.cwd ?? process.cwd(),
|
|
@@ -1380,18 +1668,32 @@ var Session = class {
|
|
|
1380
1668
|
get model() {
|
|
1381
1669
|
return this.#provider.model();
|
|
1382
1670
|
}
|
|
1671
|
+
/** 获取工具注册表(只读视图) */
|
|
1672
|
+
get toolRegistry() {
|
|
1673
|
+
return this.#toolRegistry;
|
|
1674
|
+
}
|
|
1383
1675
|
// -------------------------------------------------------------------------
|
|
1384
1676
|
// 流式对话 — Agent 主循环
|
|
1385
1677
|
// -------------------------------------------------------------------------
|
|
1386
1678
|
/**
|
|
1387
|
-
*
|
|
1679
|
+
* 执行一轮用户对话,以 AsyncGenerator 形式逐步 yield 事件。
|
|
1680
|
+
*
|
|
1681
|
+
* 主循环流程:
|
|
1682
|
+
* 1. 追加用户消息
|
|
1683
|
+
* 2. 进入 Agent 循环(最多 maxToolRounds 轮)
|
|
1684
|
+
* a. 构建消息 → 裁剪 → 调用 Provider 流式接口
|
|
1685
|
+
* b. 解析响应:文本增量、工具调用、使用量
|
|
1686
|
+
* c. 如果有工具调用 → 执行工具 → 追加结果 → 继续循环
|
|
1687
|
+
* d. 如果没有工具调用 → 退出循环
|
|
1688
|
+
* 3. yield done 事件
|
|
1388
1689
|
*
|
|
1389
1690
|
* 调用方式:
|
|
1390
1691
|
* ```ts
|
|
1391
1692
|
* for await (const event of session.chat("你好")) {
|
|
1392
1693
|
* switch (event.type) {
|
|
1393
|
-
* case "text_delta":
|
|
1694
|
+
* case "text_delta": // 追加文本
|
|
1394
1695
|
* case "tool_calls": // 展示工具调用
|
|
1696
|
+
* case "tool_result": // 工具执行结果
|
|
1395
1697
|
* case "usage": // 记录使用量
|
|
1396
1698
|
* case "done": // 本轮完成
|
|
1397
1699
|
* case "error": // 处理错误
|
|
@@ -1401,75 +1703,91 @@ var Session = class {
|
|
|
1401
1703
|
*/
|
|
1402
1704
|
async *chat(userInput) {
|
|
1403
1705
|
this.#messages.push({ role: "user", content: userInput });
|
|
1404
|
-
const systemPrompt = this.#buildSystemPrompt();
|
|
1405
|
-
const [trimmed, wasTrimmed] = trimMessages(
|
|
1406
|
-
[...this.#messages],
|
|
1407
|
-
{
|
|
1408
|
-
model: this.#provider.model(),
|
|
1409
|
-
reservedForOutput: this.#options.reservedForOutput,
|
|
1410
|
-
systemPrompt,
|
|
1411
|
-
preserveRecentRounds: this.#options.preserveRecentRounds
|
|
1412
|
-
}
|
|
1413
|
-
);
|
|
1414
|
-
if (wasTrimmed) {
|
|
1415
|
-
}
|
|
1416
|
-
const apiMessages = buildApiMessages(systemPrompt, trimmed);
|
|
1417
|
-
const toolDefs = this.#buildToolDefinitions();
|
|
1418
1706
|
const startTime = Date.now();
|
|
1707
|
+
let toolRounds = 0;
|
|
1419
1708
|
try {
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1709
|
+
while (toolRounds < this.#options.maxToolRounds) {
|
|
1710
|
+
const systemPrompt = this.#buildSystemPrompt();
|
|
1711
|
+
const [trimmed] = trimMessages(
|
|
1712
|
+
[...this.#messages],
|
|
1713
|
+
{
|
|
1714
|
+
model: this.#provider.model(),
|
|
1715
|
+
reservedForOutput: this.#options.reservedForOutput,
|
|
1716
|
+
systemPrompt,
|
|
1717
|
+
preserveRecentRounds: this.#options.preserveRecentRounds
|
|
1718
|
+
}
|
|
1719
|
+
);
|
|
1720
|
+
const apiMessages = buildApiMessages(systemPrompt, trimmed);
|
|
1721
|
+
const toolDefs = this.#buildToolDefinitions();
|
|
1722
|
+
const stream = this.#provider.chat(apiMessages, {
|
|
1723
|
+
signal: this.#abortController.signal,
|
|
1724
|
+
tools: toolDefs.length > 0 ? toolDefs : void 0
|
|
1725
|
+
});
|
|
1726
|
+
let accumulatedText = "";
|
|
1727
|
+
let lastUsage;
|
|
1728
|
+
let lastToolCalls;
|
|
1729
|
+
let lastFinishReason = null;
|
|
1730
|
+
for await (const chunk of stream) {
|
|
1731
|
+
if (chunk.content) {
|
|
1732
|
+
accumulatedText += chunk.content;
|
|
1733
|
+
yield { type: "text_delta", content: chunk.content };
|
|
1734
|
+
}
|
|
1735
|
+
if (chunk.toolCalls && chunk.toolCalls.length > 0) {
|
|
1736
|
+
lastToolCalls = chunk.toolCalls;
|
|
1737
|
+
}
|
|
1738
|
+
if (chunk.usage) {
|
|
1739
|
+
lastUsage = chunk.usage;
|
|
1740
|
+
}
|
|
1741
|
+
if (chunk.finishReason) {
|
|
1742
|
+
lastFinishReason = chunk.finishReason;
|
|
1743
|
+
}
|
|
1437
1744
|
}
|
|
1438
|
-
if (
|
|
1439
|
-
|
|
1745
|
+
if (lastUsage) {
|
|
1746
|
+
const modelId = this.#provider.model();
|
|
1747
|
+
this.#costTracker.record(lastUsage, modelId);
|
|
1748
|
+
yield { type: "usage", usage: lastUsage, model: modelId };
|
|
1440
1749
|
}
|
|
1441
|
-
|
|
1442
|
-
|
|
1750
|
+
const assistantMsg = {
|
|
1751
|
+
role: "assistant",
|
|
1752
|
+
content: accumulatedText
|
|
1753
|
+
};
|
|
1754
|
+
if (lastToolCalls && lastToolCalls.length > 0) {
|
|
1755
|
+
assistantMsg.toolCalls = lastToolCalls;
|
|
1443
1756
|
}
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1757
|
+
this.#messages.push(assistantMsg);
|
|
1758
|
+
if (lastToolCalls && lastToolCalls.length > 0) {
|
|
1759
|
+
yield { type: "tool_calls", calls: lastToolCalls };
|
|
1760
|
+
const toolCtx = {
|
|
1761
|
+
cwd: this.#options.cwd,
|
|
1762
|
+
signal: this.#abortController.signal
|
|
1763
|
+
};
|
|
1764
|
+
for (const tc of lastToolCalls) {
|
|
1765
|
+
let toolArgs;
|
|
1766
|
+
try {
|
|
1767
|
+
toolArgs = tc.arguments ? JSON.parse(tc.arguments) : {};
|
|
1768
|
+
} catch {
|
|
1769
|
+
toolArgs = {};
|
|
1770
|
+
}
|
|
1771
|
+
const result = await this.#toolRegistry.execute(tc.name, toolArgs, toolCtx);
|
|
1772
|
+
yield { type: "tool_result", name: tc.name, result };
|
|
1773
|
+
let toolContent = result.data;
|
|
1774
|
+
if (result.diff && result.diff.patch) {
|
|
1775
|
+
toolContent += `
|
|
1776
|
+
|
|
1777
|
+
${result.diff.patch}`;
|
|
1778
|
+
}
|
|
1779
|
+
this.#messages.push({
|
|
1780
|
+
role: "tool",
|
|
1781
|
+
content: toolContent,
|
|
1782
|
+
toolCallId: tc.id,
|
|
1783
|
+
name: tc.name
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
toolRounds++;
|
|
1787
|
+
continue;
|
|
1469
1788
|
}
|
|
1789
|
+
break;
|
|
1470
1790
|
}
|
|
1471
|
-
const elapsed = Date.now() - startTime;
|
|
1472
|
-
yield { type: "done", elapsed };
|
|
1473
1791
|
} catch (err) {
|
|
1474
1792
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
1475
1793
|
return;
|
|
@@ -1481,7 +1799,10 @@ var Session = class {
|
|
|
1481
1799
|
type: "error",
|
|
1482
1800
|
error: err instanceof Error ? err : new Error(String(err))
|
|
1483
1801
|
};
|
|
1802
|
+
return;
|
|
1484
1803
|
}
|
|
1804
|
+
const elapsed = Date.now() - startTime;
|
|
1805
|
+
yield { type: "done", elapsed };
|
|
1485
1806
|
}
|
|
1486
1807
|
// -------------------------------------------------------------------------
|
|
1487
1808
|
// 会话管理
|
|
@@ -1500,7 +1821,8 @@ var Session = class {
|
|
|
1500
1821
|
// -------------------------------------------------------------------------
|
|
1501
1822
|
/** 构建系统提示词 */
|
|
1502
1823
|
#buildSystemPrompt() {
|
|
1503
|
-
const
|
|
1824
|
+
const enabledTools = this.#toolRegistry.list();
|
|
1825
|
+
const toolDescs = enabledTools.map((t) => ({
|
|
1504
1826
|
name: t.name,
|
|
1505
1827
|
description: t.description,
|
|
1506
1828
|
parameters: t.parameters
|
|
@@ -1515,7 +1837,7 @@ var Session = class {
|
|
|
1515
1837
|
}
|
|
1516
1838
|
/** 将注册的工具转为 ToolDefinition 格式(预留给 function calling) */
|
|
1517
1839
|
#buildToolDefinitions() {
|
|
1518
|
-
return this.#
|
|
1840
|
+
return this.#toolRegistry.list().map((t) => ({
|
|
1519
1841
|
type: "function",
|
|
1520
1842
|
function: {
|
|
1521
1843
|
name: t.name,
|
|
@@ -1526,6 +1848,956 @@ var Session = class {
|
|
|
1526
1848
|
}
|
|
1527
1849
|
};
|
|
1528
1850
|
|
|
1851
|
+
// src/tool/sandbox.ts
|
|
1852
|
+
import { resolve, relative, isAbsolute } from "path";
|
|
1853
|
+
import { spawn } from "child_process";
|
|
1854
|
+
import process2 from "process";
|
|
1855
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
1856
|
+
var DEFAULT_MAX_OUTPUT_LENGTH = 5e4;
|
|
1857
|
+
var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
1858
|
+
var isWindows = process2.platform === "win32";
|
|
1859
|
+
function resolvePath(inputPath, cwd) {
|
|
1860
|
+
const resolved = isAbsolute(inputPath) ? inputPath : resolve(cwd, inputPath);
|
|
1861
|
+
const normalized = resolve(resolved);
|
|
1862
|
+
const rel = relative(cwd, normalized);
|
|
1863
|
+
if (rel.startsWith("..") || normalized !== resolve(cwd) && !rel) {
|
|
1864
|
+
}
|
|
1865
|
+
return normalized;
|
|
1866
|
+
}
|
|
1867
|
+
function truncateOutput(content, maxLength = DEFAULT_MAX_OUTPUT_LENGTH) {
|
|
1868
|
+
if (content.length <= maxLength) return content;
|
|
1869
|
+
const truncated = content.slice(0, maxLength);
|
|
1870
|
+
return `${truncated}
|
|
1871
|
+
|
|
1872
|
+
... [\u8F93\u51FA\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF0C\u5171 ${content.length} \u5B57\u7B26]`;
|
|
1873
|
+
}
|
|
1874
|
+
function getDefaultTimeout() {
|
|
1875
|
+
return DEFAULT_TIMEOUT_MS;
|
|
1876
|
+
}
|
|
1877
|
+
async function execCommand(command, args, cwd, timeoutMs = DEFAULT_TIMEOUT_MS, signal, isShellCommand) {
|
|
1878
|
+
return new Promise((resolve2) => {
|
|
1879
|
+
let spawnCmd;
|
|
1880
|
+
let spawnArgs;
|
|
1881
|
+
let useShell;
|
|
1882
|
+
if (isShellCommand) {
|
|
1883
|
+
spawnCmd = command;
|
|
1884
|
+
spawnArgs = args;
|
|
1885
|
+
useShell = false;
|
|
1886
|
+
} else {
|
|
1887
|
+
useShell = !isWindows;
|
|
1888
|
+
spawnCmd = isWindows ? "cmd" : command;
|
|
1889
|
+
spawnArgs = isWindows ? ["/c", command, ...args] : args;
|
|
1890
|
+
}
|
|
1891
|
+
const child = spawn(spawnCmd, spawnArgs, {
|
|
1892
|
+
cwd,
|
|
1893
|
+
shell: useShell,
|
|
1894
|
+
env: { ...process2.env },
|
|
1895
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1896
|
+
});
|
|
1897
|
+
let stdout = "";
|
|
1898
|
+
let stderr = "";
|
|
1899
|
+
child.stdout.on("data", (data) => {
|
|
1900
|
+
stdout += data.toString();
|
|
1901
|
+
});
|
|
1902
|
+
child.stderr.on("data", (data) => {
|
|
1903
|
+
stderr += data.toString();
|
|
1904
|
+
});
|
|
1905
|
+
const timeout = setTimeout(() => {
|
|
1906
|
+
child.kill("SIGTERM");
|
|
1907
|
+
setTimeout(() => {
|
|
1908
|
+
child.kill("SIGKILL");
|
|
1909
|
+
}, 5e3);
|
|
1910
|
+
}, timeoutMs);
|
|
1911
|
+
if (signal) {
|
|
1912
|
+
if (signal.aborted) {
|
|
1913
|
+
child.kill("SIGTERM");
|
|
1914
|
+
}
|
|
1915
|
+
signal.addEventListener("abort", () => {
|
|
1916
|
+
child.kill("SIGTERM");
|
|
1917
|
+
}, { once: true });
|
|
1918
|
+
}
|
|
1919
|
+
child.on("close", (code) => {
|
|
1920
|
+
clearTimeout(timeout);
|
|
1921
|
+
resolve2({ stdout, stderr, exitCode: code });
|
|
1922
|
+
});
|
|
1923
|
+
child.on("error", (err) => {
|
|
1924
|
+
clearTimeout(timeout);
|
|
1925
|
+
stderr += `
|
|
1926
|
+
\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25\uFF1A${err.message}`;
|
|
1927
|
+
resolve2({ stdout, stderr, exitCode: null });
|
|
1928
|
+
});
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
// src/tool/diff.ts
|
|
1933
|
+
function computeFileDiff(oldContent, newContent, filePath) {
|
|
1934
|
+
if (oldContent === newContent) {
|
|
1935
|
+
return {
|
|
1936
|
+
filePath,
|
|
1937
|
+
patch: "",
|
|
1938
|
+
existedBefore: oldContent.length > 0,
|
|
1939
|
+
additions: 0,
|
|
1940
|
+
deletions: 0
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
if (oldContent.length === 0) {
|
|
1944
|
+
const newLines2 = splitLines(newContent);
|
|
1945
|
+
const patch2 = formatNewFileDiff(newLines2, filePath);
|
|
1946
|
+
return {
|
|
1947
|
+
filePath,
|
|
1948
|
+
patch: patch2,
|
|
1949
|
+
existedBefore: false,
|
|
1950
|
+
additions: newLines2.length,
|
|
1951
|
+
deletions: 0
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
if (newContent.length === 0) {
|
|
1955
|
+
const oldLines2 = splitLines(oldContent);
|
|
1956
|
+
const patch2 = formatDeletedFileDiff(oldLines2, filePath);
|
|
1957
|
+
return {
|
|
1958
|
+
filePath,
|
|
1959
|
+
patch: patch2,
|
|
1960
|
+
existedBefore: true,
|
|
1961
|
+
additions: 0,
|
|
1962
|
+
deletions: oldLines2.length
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
const oldLines = splitLines(oldContent);
|
|
1966
|
+
const newLines = splitLines(newContent);
|
|
1967
|
+
const diffLines = computeLineDiff(oldLines, newLines);
|
|
1968
|
+
let additions = 0;
|
|
1969
|
+
let deletions = 0;
|
|
1970
|
+
for (const line of diffLines) {
|
|
1971
|
+
if (line.op === "add") additions++;
|
|
1972
|
+
if (line.op === "remove") deletions++;
|
|
1973
|
+
}
|
|
1974
|
+
const hunks = groupIntoHunks(diffLines);
|
|
1975
|
+
const patch = formatUnifiedDiff(hunks, filePath);
|
|
1976
|
+
return {
|
|
1977
|
+
filePath,
|
|
1978
|
+
patch,
|
|
1979
|
+
existedBefore: true,
|
|
1980
|
+
additions,
|
|
1981
|
+
deletions
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
function splitLines(text) {
|
|
1985
|
+
const raw = text.split("\n");
|
|
1986
|
+
if (text.endsWith("\n") && raw.length > 0 && raw[raw.length - 1] === "") {
|
|
1987
|
+
return raw.slice(0, -1);
|
|
1988
|
+
}
|
|
1989
|
+
return raw;
|
|
1990
|
+
}
|
|
1991
|
+
function computeLineDiff(oldLines, newLines) {
|
|
1992
|
+
const N = oldLines.length;
|
|
1993
|
+
const M = newLines.length;
|
|
1994
|
+
const dp = [];
|
|
1995
|
+
for (let i2 = 0; i2 <= N; i2++) {
|
|
1996
|
+
dp[i2] = new Array(M + 1).fill(0);
|
|
1997
|
+
}
|
|
1998
|
+
for (let i2 = 1; i2 <= N; i2++) {
|
|
1999
|
+
for (let j2 = 1; j2 <= M; j2++) {
|
|
2000
|
+
if (oldLines[i2 - 1] === newLines[j2 - 1]) {
|
|
2001
|
+
dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
|
|
2002
|
+
} else {
|
|
2003
|
+
dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
const result = [];
|
|
2008
|
+
let i = N;
|
|
2009
|
+
let j = M;
|
|
2010
|
+
while (i > 0 || j > 0) {
|
|
2011
|
+
const oldLine = i > 0 ? oldLines[i - 1] : void 0;
|
|
2012
|
+
const newLine = j > 0 ? newLines[j - 1] : void 0;
|
|
2013
|
+
if (i > 0 && j > 0 && oldLine !== void 0 && newLine !== void 0 && oldLine === newLine) {
|
|
2014
|
+
result.unshift({ op: "equal", line: oldLine });
|
|
2015
|
+
i--;
|
|
2016
|
+
j--;
|
|
2017
|
+
} else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
|
|
2018
|
+
result.unshift({ op: "add", line: newLines[j - 1] });
|
|
2019
|
+
j--;
|
|
2020
|
+
} else {
|
|
2021
|
+
result.unshift({ op: "remove", line: oldLines[i - 1] });
|
|
2022
|
+
i--;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
return result;
|
|
2026
|
+
}
|
|
2027
|
+
var CONTEXT_LINES = 3;
|
|
2028
|
+
function groupIntoHunks(diffLines) {
|
|
2029
|
+
if (diffLines.length === 0) return [];
|
|
2030
|
+
const changeIndices = [];
|
|
2031
|
+
for (let i = 0; i < diffLines.length; i++) {
|
|
2032
|
+
if (diffLines[i].op !== "equal") {
|
|
2033
|
+
changeIndices.push(i);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
if (changeIndices.length === 0) return [];
|
|
2037
|
+
const groups = [[changeIndices[0]]];
|
|
2038
|
+
for (let idx = 1; idx < changeIndices.length; idx++) {
|
|
2039
|
+
const prevIdx = groups[groups.length - 1][groups[groups.length - 1].length - 1];
|
|
2040
|
+
const currIdx = changeIndices[idx];
|
|
2041
|
+
let gap = 0;
|
|
2042
|
+
for (let k = prevIdx + 1; k < currIdx; k++) {
|
|
2043
|
+
if (diffLines[k].op === "equal") gap++;
|
|
2044
|
+
}
|
|
2045
|
+
if (gap <= 2 * CONTEXT_LINES) {
|
|
2046
|
+
groups[groups.length - 1].push(currIdx);
|
|
2047
|
+
} else {
|
|
2048
|
+
groups.push([currIdx]);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
const hunks = [];
|
|
2052
|
+
for (const group of groups) {
|
|
2053
|
+
const firstChangeIdx = group[0];
|
|
2054
|
+
const lastChangeIdx = group[group.length - 1];
|
|
2055
|
+
const startIdx = Math.max(0, firstChangeIdx - CONTEXT_LINES);
|
|
2056
|
+
const endIdx = Math.min(diffLines.length - 1, lastChangeIdx + CONTEXT_LINES);
|
|
2057
|
+
const lines = diffLines.slice(startIdx, endIdx + 1);
|
|
2058
|
+
let oldLine = 0;
|
|
2059
|
+
let newLine = 0;
|
|
2060
|
+
for (let i = 0; i < startIdx; i++) {
|
|
2061
|
+
const op = diffLines[i].op;
|
|
2062
|
+
if (op === "equal" || op === "remove") oldLine++;
|
|
2063
|
+
if (op === "equal" || op === "add") newLine++;
|
|
2064
|
+
}
|
|
2065
|
+
let oldCount = 0;
|
|
2066
|
+
let newCount = 0;
|
|
2067
|
+
for (const line of lines) {
|
|
2068
|
+
if (line.op === "equal" || line.op === "remove") oldCount++;
|
|
2069
|
+
if (line.op === "equal" || line.op === "add") newCount++;
|
|
2070
|
+
}
|
|
2071
|
+
hunks.push({
|
|
2072
|
+
oldStart: oldLine + 1,
|
|
2073
|
+
oldCount,
|
|
2074
|
+
newStart: newLine + 1,
|
|
2075
|
+
newCount,
|
|
2076
|
+
lines
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
return hunks;
|
|
2080
|
+
}
|
|
2081
|
+
function formatUnifiedDiff(hunks, filePath) {
|
|
2082
|
+
if (hunks.length === 0) return "";
|
|
2083
|
+
const parts = [];
|
|
2084
|
+
const fileName = extractFileName(filePath);
|
|
2085
|
+
parts.push(`--- a/${fileName}`);
|
|
2086
|
+
parts.push(`+++ b/${fileName}`);
|
|
2087
|
+
for (const hunk of hunks) {
|
|
2088
|
+
parts.push(`@@ -${hunk.oldStart},${hunk.oldCount} +${hunk.newStart},${hunk.newCount} @@`);
|
|
2089
|
+
for (const line of hunk.lines) {
|
|
2090
|
+
switch (line.op) {
|
|
2091
|
+
case "equal":
|
|
2092
|
+
parts.push(` ${line.line}`);
|
|
2093
|
+
break;
|
|
2094
|
+
case "remove":
|
|
2095
|
+
parts.push(`-${line.line}`);
|
|
2096
|
+
break;
|
|
2097
|
+
case "add":
|
|
2098
|
+
parts.push(`+${line.line}`);
|
|
2099
|
+
break;
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
return parts.join("\n");
|
|
2104
|
+
}
|
|
2105
|
+
function formatNewFileDiff(lines, filePath) {
|
|
2106
|
+
const fileName = extractFileName(filePath);
|
|
2107
|
+
const parts = [];
|
|
2108
|
+
parts.push(`--- /dev/null`);
|
|
2109
|
+
parts.push(`+++ b/${fileName}`);
|
|
2110
|
+
parts.push(`@@ -0,0 +1,${lines.length} @@`);
|
|
2111
|
+
for (const line of lines) {
|
|
2112
|
+
parts.push(`+${line}`);
|
|
2113
|
+
}
|
|
2114
|
+
return parts.join("\n");
|
|
2115
|
+
}
|
|
2116
|
+
function formatDeletedFileDiff(lines, filePath) {
|
|
2117
|
+
const fileName = extractFileName(filePath);
|
|
2118
|
+
const parts = [];
|
|
2119
|
+
parts.push(`--- a/${fileName}`);
|
|
2120
|
+
parts.push(`+++ /dev/null`);
|
|
2121
|
+
parts.push(`@@ -1,${lines.length} +0,0 @@`);
|
|
2122
|
+
for (const line of lines) {
|
|
2123
|
+
parts.push(`-${line}`);
|
|
2124
|
+
}
|
|
2125
|
+
return parts.join("\n");
|
|
2126
|
+
}
|
|
2127
|
+
function extractFileName(filePath) {
|
|
2128
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
2129
|
+
return normalized.split("/").pop() ?? filePath;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
// src/tool/builtins/read-file.ts
|
|
2133
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
2134
|
+
import { stat } from "fs/promises";
|
|
2135
|
+
var readFileSchema = {
|
|
2136
|
+
type: "object",
|
|
2137
|
+
properties: {
|
|
2138
|
+
path: {
|
|
2139
|
+
type: "string",
|
|
2140
|
+
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2141
|
+
},
|
|
2142
|
+
start_line: {
|
|
2143
|
+
type: "number",
|
|
2144
|
+
description: "\u8D77\u59CB\u884C\u53F7\uFF08\u4ECE 1 \u5F00\u59CB\uFF09\uFF0C\u9ED8\u8BA4\u4E3A 1"
|
|
2145
|
+
},
|
|
2146
|
+
end_line: {
|
|
2147
|
+
type: "number",
|
|
2148
|
+
description: "\u7ED3\u675F\u884C\u53F7\uFF08\u5305\u542B\uFF09\uFF0C\u9ED8\u8BA4\u5230\u6587\u4EF6\u672B\u5C3E"
|
|
2149
|
+
}
|
|
2150
|
+
},
|
|
2151
|
+
required: ["path"],
|
|
2152
|
+
additionalProperties: false
|
|
2153
|
+
};
|
|
2154
|
+
var readFileTool = {
|
|
2155
|
+
name: "read_file",
|
|
2156
|
+
description: "\u8BFB\u53D6\u6307\u5B9A\u8DEF\u5F84\u7684\u6587\u4EF6\u5185\u5BB9\u3002\u652F\u6301\u884C\u53F7\u8303\u56F4\u9009\u62E9\uFF0C\u8F93\u51FA\u5E26\u884C\u53F7\u3002\u9002\u7528\u4E8E\u67E5\u770B\u6E90\u4EE3\u7801\u3001\u914D\u7F6E\u6587\u4EF6\u7B49\u6587\u672C\u6587\u4EF6\u3002",
|
|
2157
|
+
parameters: readFileSchema,
|
|
2158
|
+
async execute(args, ctx) {
|
|
2159
|
+
const params = args;
|
|
2160
|
+
if (!params?.path || typeof params.path !== "string") {
|
|
2161
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 path", error: "INVALID_ARGS" };
|
|
2162
|
+
}
|
|
2163
|
+
const filePath = resolvePath(params.path, ctx.cwd);
|
|
2164
|
+
try {
|
|
2165
|
+
const fileStat = await stat(filePath);
|
|
2166
|
+
const maxSize = 10 * 1024 * 1024;
|
|
2167
|
+
if (fileStat.size > maxSize) {
|
|
2168
|
+
return {
|
|
2169
|
+
success: false,
|
|
2170
|
+
data: `\u6587\u4EF6\u8FC7\u5927\uFF08${(fileStat.size / 1024 / 1024).toFixed(1)}MB\uFF09\uFF0C\u8D85\u8FC7 10MB \u9650\u5236`,
|
|
2171
|
+
error: "FILE_TOO_LARGE"
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
const content = await readFile4(filePath, "utf-8");
|
|
2175
|
+
const lines = content.split("\n");
|
|
2176
|
+
const startLine = Math.max(1, params.start_line ?? 1) - 1;
|
|
2177
|
+
const endLine = params.end_line ? Math.min(params.end_line, lines.length) : lines.length;
|
|
2178
|
+
const selectedLines = lines.slice(startLine, endLine);
|
|
2179
|
+
const lineNumWidth = String(endLine).length;
|
|
2180
|
+
const result = selectedLines.map((line, i) => {
|
|
2181
|
+
const lineNum = String(startLine + i + 1).padStart(lineNumWidth, " ");
|
|
2182
|
+
return `${lineNum} | ${line}`;
|
|
2183
|
+
}).join("\n");
|
|
2184
|
+
return {
|
|
2185
|
+
success: true,
|
|
2186
|
+
data: truncateOutput(result)
|
|
2187
|
+
};
|
|
2188
|
+
} catch (err) {
|
|
2189
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2190
|
+
return {
|
|
2191
|
+
success: false,
|
|
2192
|
+
data: `\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25\uFF1A${message}`,
|
|
2193
|
+
error: "READ_ERROR"
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
};
|
|
2198
|
+
|
|
2199
|
+
// src/tool/builtins/write-file.ts
|
|
2200
|
+
import { writeFile as writeFile3, mkdir as mkdir4, readFile as readFile5 } from "fs/promises";
|
|
2201
|
+
import { dirname } from "path";
|
|
2202
|
+
var writeFileSchema = {
|
|
2203
|
+
type: "object",
|
|
2204
|
+
properties: {
|
|
2205
|
+
path: {
|
|
2206
|
+
type: "string",
|
|
2207
|
+
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2208
|
+
},
|
|
2209
|
+
content: {
|
|
2210
|
+
type: "string",
|
|
2211
|
+
description: "\u8981\u5199\u5165\u7684\u6587\u4EF6\u5185\u5BB9"
|
|
2212
|
+
}
|
|
2213
|
+
},
|
|
2214
|
+
required: ["path", "content"],
|
|
2215
|
+
additionalProperties: false
|
|
2216
|
+
};
|
|
2217
|
+
var writeFileTool = {
|
|
2218
|
+
name: "write_file",
|
|
2219
|
+
description: "\u521B\u5EFA\u6216\u8986\u76D6\u6587\u4EF6\u3002\u5982\u679C\u7236\u76EE\u5F55\u4E0D\u5B58\u5728\u4F1A\u81EA\u52A8\u521B\u5EFA\u3002\u9002\u7528\u4E8E\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u5B8C\u5168\u66FF\u6362\u6587\u4EF6\u5185\u5BB9\u3002",
|
|
2220
|
+
parameters: writeFileSchema,
|
|
2221
|
+
async execute(args, ctx) {
|
|
2222
|
+
const params = args;
|
|
2223
|
+
if (!params?.path || typeof params.path !== "string") {
|
|
2224
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 path", error: "INVALID_ARGS" };
|
|
2225
|
+
}
|
|
2226
|
+
if (params.content === void 0 || params.content === null) {
|
|
2227
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 content", error: "INVALID_ARGS" };
|
|
2228
|
+
}
|
|
2229
|
+
const filePath = resolvePath(params.path, ctx.cwd);
|
|
2230
|
+
try {
|
|
2231
|
+
let oldContent = "";
|
|
2232
|
+
let existedBefore = false;
|
|
2233
|
+
try {
|
|
2234
|
+
oldContent = await readFile5(filePath, "utf-8");
|
|
2235
|
+
existedBefore = true;
|
|
2236
|
+
} catch {
|
|
2237
|
+
}
|
|
2238
|
+
await mkdir4(dirname(filePath), { recursive: true });
|
|
2239
|
+
const content = String(params.content);
|
|
2240
|
+
await writeFile3(filePath, content, "utf-8");
|
|
2241
|
+
const diff = computeFileDiff(oldContent, content, filePath);
|
|
2242
|
+
diff.existedBefore = existedBefore;
|
|
2243
|
+
const lineCount = content.split("\n").length;
|
|
2244
|
+
const byteSize = Buffer.byteLength(content, "utf-8");
|
|
2245
|
+
const action = existedBefore ? "\u5DF2\u4FEE\u6539" : "\u5DF2\u521B\u5EFA";
|
|
2246
|
+
const diffSummary = existedBefore ? `\uFF0C+${diff.additions} -${diff.deletions}` : `\uFF0C+${diff.additions} \u884C\uFF08\u65B0\u5EFA\uFF09`;
|
|
2247
|
+
return {
|
|
2248
|
+
success: true,
|
|
2249
|
+
data: `\u6587\u4EF6${action}\uFF1A${filePath}\uFF08${lineCount} \u884C\uFF0C${byteSize} \u5B57\u8282${diffSummary}\uFF09`,
|
|
2250
|
+
diff
|
|
2251
|
+
};
|
|
2252
|
+
} catch (err) {
|
|
2253
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2254
|
+
return {
|
|
2255
|
+
success: false,
|
|
2256
|
+
data: `\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A${message}`,
|
|
2257
|
+
error: "WRITE_ERROR"
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
// src/tool/builtins/edit-file.ts
|
|
2264
|
+
import { readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
2265
|
+
var editFileSchema = {
|
|
2266
|
+
type: "object",
|
|
2267
|
+
properties: {
|
|
2268
|
+
path: {
|
|
2269
|
+
type: "string",
|
|
2270
|
+
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2271
|
+
},
|
|
2272
|
+
old_text: {
|
|
2273
|
+
type: "string",
|
|
2274
|
+
description: "\u8981\u67E5\u627E\u7684\u539F\u59CB\u6587\u672C\uFF08\u7CBE\u786E\u5339\u914D\uFF09"
|
|
2275
|
+
},
|
|
2276
|
+
new_text: {
|
|
2277
|
+
type: "string",
|
|
2278
|
+
description: "\u66FF\u6362\u540E\u7684\u65B0\u6587\u672C"
|
|
2279
|
+
}
|
|
2280
|
+
},
|
|
2281
|
+
required: ["path", "old_text", "new_text"],
|
|
2282
|
+
additionalProperties: false
|
|
2283
|
+
};
|
|
2284
|
+
var editFileTool = {
|
|
2285
|
+
name: "edit_file",
|
|
2286
|
+
description: "\u5BF9\u6587\u4EF6\u8FDB\u884C\u7CBE\u786E\u5B57\u7B26\u4E32\u66FF\u6362\u3002\u67E5\u627E\u6587\u4EF6\u4E2D\u7684 old_text \u5E76\u66FF\u6362\u4E3A new_text\u3002\u5982\u679C old_text \u51FA\u73B0\u591A\u6B21\u6216\u672A\u627E\u5230\u5219\u62A5\u9519\u3002\u9002\u7528\u4E8E\u5C0F\u8303\u56F4\u7CBE\u786E\u4FEE\u6539\u3002",
|
|
2287
|
+
parameters: editFileSchema,
|
|
2288
|
+
async execute(args, ctx) {
|
|
2289
|
+
const params = args;
|
|
2290
|
+
if (!params?.path || typeof params.path !== "string") {
|
|
2291
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 path", error: "INVALID_ARGS" };
|
|
2292
|
+
}
|
|
2293
|
+
if (typeof params.old_text !== "string") {
|
|
2294
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 old_text", error: "INVALID_ARGS" };
|
|
2295
|
+
}
|
|
2296
|
+
if (typeof params.new_text !== "string") {
|
|
2297
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 new_text", error: "INVALID_ARGS" };
|
|
2298
|
+
}
|
|
2299
|
+
const filePath = resolvePath(params.path, ctx.cwd);
|
|
2300
|
+
try {
|
|
2301
|
+
const content = await readFile6(filePath, "utf-8");
|
|
2302
|
+
const firstIndex = content.indexOf(params.old_text);
|
|
2303
|
+
if (firstIndex === -1) {
|
|
2304
|
+
return {
|
|
2305
|
+
success: false,
|
|
2306
|
+
data: `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u6587\u672C\u3002\u8BF7\u786E\u8BA4 old_text \u4E0E\u6587\u4EF6\u5185\u5BB9\u5B8C\u5168\u4E00\u81F4\uFF08\u5305\u62EC\u7F29\u8FDB\u548C\u7A7A\u683C\uFF09\u3002`,
|
|
2307
|
+
error: "TEXT_NOT_FOUND"
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2310
|
+
const secondIndex = content.indexOf(params.old_text, firstIndex + 1);
|
|
2311
|
+
if (secondIndex !== -1) {
|
|
2312
|
+
return {
|
|
2313
|
+
success: false,
|
|
2314
|
+
data: `\u8981\u66FF\u6362\u7684\u6587\u672C\u5728\u6587\u4EF6\u4E2D\u51FA\u73B0\u591A\u6B21\uFF0C\u8BF7\u63D0\u4F9B\u66F4\u591A\u4E0A\u4E0B\u6587\u4EE5\u7CBE\u786E\u5B9A\u4F4D\u3002`,
|
|
2315
|
+
error: "TEXT_MULTIPLE_MATCHES"
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
const newContent = content.replace(params.old_text, params.new_text);
|
|
2319
|
+
await writeFile4(filePath, newContent, "utf-8");
|
|
2320
|
+
const diff = computeFileDiff(content, newContent, filePath);
|
|
2321
|
+
diff.existedBefore = true;
|
|
2322
|
+
const beforeText = content.slice(0, firstIndex);
|
|
2323
|
+
const startLine = beforeText.split("\n").length;
|
|
2324
|
+
const oldLines = params.old_text.split("\n").length;
|
|
2325
|
+
const newLines = params.new_text.split("\n").length;
|
|
2326
|
+
const diffSummary = `+${diff.additions} -${diff.deletions}`;
|
|
2327
|
+
return {
|
|
2328
|
+
success: true,
|
|
2329
|
+
data: `\u6587\u4EF6\u5DF2\u7F16\u8F91\uFF1A${filePath}
|
|
2330
|
+
\u66FF\u6362\u4F4D\u7F6E\uFF1A\u7B2C ${startLine} \u884C
|
|
2331
|
+
${oldLines} \u884C \u2192 ${newLines} \u884C
|
|
2332
|
+
\u53D8\u66F4\uFF1A${diffSummary}`,
|
|
2333
|
+
diff
|
|
2334
|
+
};
|
|
2335
|
+
} catch (err) {
|
|
2336
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2337
|
+
return {
|
|
2338
|
+
success: false,
|
|
2339
|
+
data: `\u7F16\u8F91\u6587\u4EF6\u5931\u8D25\uFF1A${message}`,
|
|
2340
|
+
error: "EDIT_ERROR"
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
};
|
|
2345
|
+
|
|
2346
|
+
// src/tool/builtins/bash.ts
|
|
2347
|
+
import process3 from "process";
|
|
2348
|
+
var isWindows2 = process3.platform === "win32";
|
|
2349
|
+
var bashSchema = {
|
|
2350
|
+
type: "object",
|
|
2351
|
+
properties: {
|
|
2352
|
+
command: {
|
|
2353
|
+
type: "string",
|
|
2354
|
+
description: "\u8981\u6267\u884C\u7684 shell \u547D\u4EE4"
|
|
2355
|
+
},
|
|
2356
|
+
timeout: {
|
|
2357
|
+
type: "number",
|
|
2358
|
+
description: "\u6267\u884C\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 30000"
|
|
2359
|
+
}
|
|
2360
|
+
},
|
|
2361
|
+
required: ["command"],
|
|
2362
|
+
additionalProperties: false
|
|
2363
|
+
};
|
|
2364
|
+
var bashTool = {
|
|
2365
|
+
name: "bash",
|
|
2366
|
+
description: "\u5728 shell \u4E2D\u6267\u884C\u547D\u4EE4\u3002\u8FD4\u56DE\u6807\u51C6\u8F93\u51FA\u3001\u6807\u51C6\u9519\u8BEF\u548C\u9000\u51FA\u7801\u3002\u652F\u6301\u8D85\u65F6\u63A7\u5236\u548C\u4FE1\u53F7\u4E2D\u6B62\u3002\u9002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001Git \u64CD\u4F5C\u7B49\u547D\u4EE4\u3002",
|
|
2367
|
+
parameters: bashSchema,
|
|
2368
|
+
async execute(args, ctx) {
|
|
2369
|
+
const params = args;
|
|
2370
|
+
if (!params?.command || typeof params.command !== "string") {
|
|
2371
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 command", error: "INVALID_ARGS" };
|
|
2372
|
+
}
|
|
2373
|
+
const timeout = params.timeout ?? ctx.timeout ?? getDefaultTimeout();
|
|
2374
|
+
try {
|
|
2375
|
+
const shellCommand = isWindows2 ? "cmd" : "sh";
|
|
2376
|
+
const shellArgs = isWindows2 ? ["/c", params.command] : ["-c", params.command];
|
|
2377
|
+
const result = await execCommand(
|
|
2378
|
+
shellCommand,
|
|
2379
|
+
shellArgs,
|
|
2380
|
+
ctx.cwd,
|
|
2381
|
+
timeout,
|
|
2382
|
+
ctx.signal,
|
|
2383
|
+
true
|
|
2384
|
+
// isShellCommand — 已指定 shell 程序,不需要二次包装
|
|
2385
|
+
);
|
|
2386
|
+
const parts = [];
|
|
2387
|
+
if (result.stdout) {
|
|
2388
|
+
parts.push(truncateOutput(result.stdout));
|
|
2389
|
+
}
|
|
2390
|
+
if (result.stderr) {
|
|
2391
|
+
parts.push(`[stderr]
|
|
2392
|
+
${truncateOutput(result.stderr)}`);
|
|
2393
|
+
}
|
|
2394
|
+
const success = result.exitCode === 0;
|
|
2395
|
+
const output = parts.length > 0 ? parts.join("\n") : "(\u65E0\u8F93\u51FA)";
|
|
2396
|
+
return {
|
|
2397
|
+
success,
|
|
2398
|
+
data: `${output}
|
|
2399
|
+
[\u9000\u51FA\u7801: ${result.exitCode ?? "\u672A\u77E5"}]`,
|
|
2400
|
+
error: success ? void 0 : `EXIT_CODE_${result.exitCode ?? "UNKNOWN"}`
|
|
2401
|
+
};
|
|
2402
|
+
} catch (err) {
|
|
2403
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2404
|
+
return {
|
|
2405
|
+
success: false,
|
|
2406
|
+
data: `\u547D\u4EE4\u6267\u884C\u5931\u8D25\uFF1A${message}`,
|
|
2407
|
+
error: "EXECUTION_ERROR"
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
};
|
|
2412
|
+
|
|
2413
|
+
// src/tool/builtins/glob.ts
|
|
2414
|
+
import { readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
2415
|
+
import { join as join4, relative as relative2, isAbsolute as isAbsolute2 } from "path";
|
|
2416
|
+
var globSchema = {
|
|
2417
|
+
type: "object",
|
|
2418
|
+
properties: {
|
|
2419
|
+
pattern: {
|
|
2420
|
+
type: "string",
|
|
2421
|
+
description: "\u641C\u7D22\u6A21\u5F0F\uFF08\u652F\u6301 * \u548C ** \u901A\u914D\u7B26\uFF0C\u5982 **/*.ts\u3001src/**/*.test.ts\uFF09"
|
|
2422
|
+
},
|
|
2423
|
+
directory: {
|
|
2424
|
+
type: "string",
|
|
2425
|
+
description: "\u641C\u7D22\u7684\u8D77\u59CB\u76EE\u5F55\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55"
|
|
2426
|
+
}
|
|
2427
|
+
},
|
|
2428
|
+
required: ["pattern"],
|
|
2429
|
+
additionalProperties: false
|
|
2430
|
+
};
|
|
2431
|
+
function globToRegex(pattern) {
|
|
2432
|
+
let regexStr = pattern;
|
|
2433
|
+
regexStr = regexStr.replace(/\*\*\//g, "<<GLOBSTAR_SLASH>>");
|
|
2434
|
+
regexStr = regexStr.replace(/\*\*/g, "<<GLOBSTAR>>");
|
|
2435
|
+
regexStr = regexStr.replace(/\?/g, "<<QUESTION>>");
|
|
2436
|
+
regexStr = regexStr.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
2437
|
+
regexStr = regexStr.replace(/\*/g, "[^/]*");
|
|
2438
|
+
regexStr = regexStr.replace(/<<GLOBSTAR_SLASH>>/g, "(.*/)?");
|
|
2439
|
+
regexStr = regexStr.replace(/<<GLOBSTAR>>/g, ".*");
|
|
2440
|
+
regexStr = regexStr.replace(/<<QUESTION>>/g, "[^/]");
|
|
2441
|
+
return new RegExp(`^${regexStr}$`, "i");
|
|
2442
|
+
}
|
|
2443
|
+
async function walkDir(dir, baseDir) {
|
|
2444
|
+
const results = [];
|
|
2445
|
+
let entries;
|
|
2446
|
+
try {
|
|
2447
|
+
entries = await readdir2(dir, { withFileTypes: true });
|
|
2448
|
+
} catch {
|
|
2449
|
+
return results;
|
|
2450
|
+
}
|
|
2451
|
+
for (const entry of entries) {
|
|
2452
|
+
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
2453
|
+
continue;
|
|
2454
|
+
}
|
|
2455
|
+
const fullPath = join4(dir, entry.name);
|
|
2456
|
+
const relPath = relative2(baseDir, fullPath);
|
|
2457
|
+
if (entry.isDirectory()) {
|
|
2458
|
+
results.push(...await walkDir(fullPath, baseDir));
|
|
2459
|
+
} else {
|
|
2460
|
+
results.push(relPath);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
return results;
|
|
2464
|
+
}
|
|
2465
|
+
var globTool = {
|
|
2466
|
+
name: "glob",
|
|
2467
|
+
description: "\u6309\u6A21\u5F0F\u641C\u7D22\u6587\u4EF6\u8DEF\u5F84\u3002\u652F\u6301 *\uFF08\u5339\u914D\u6587\u4EF6\u540D\u90E8\u5206\uFF09\u548C **\uFF08\u5339\u914D\u591A\u5C42\u76EE\u5F55\uFF09\u901A\u914D\u7B26\u3002\u81EA\u52A8\u8DF3\u8FC7 node_modules \u548C .git \u76EE\u5F55\u3002",
|
|
2468
|
+
parameters: globSchema,
|
|
2469
|
+
async execute(args, ctx) {
|
|
2470
|
+
const params = args;
|
|
2471
|
+
if (!params?.pattern || typeof params.pattern !== "string") {
|
|
2472
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
|
|
2473
|
+
}
|
|
2474
|
+
const searchDir = params.directory ? isAbsolute2(params.directory) ? params.directory : join4(ctx.cwd, params.directory) : ctx.cwd;
|
|
2475
|
+
const regex = globToRegex(params.pattern);
|
|
2476
|
+
try {
|
|
2477
|
+
const dirStat = await stat2(searchDir);
|
|
2478
|
+
if (!dirStat.isDirectory()) {
|
|
2479
|
+
return { success: false, data: `\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${searchDir}`, error: "NOT_DIRECTORY" };
|
|
2480
|
+
}
|
|
2481
|
+
const allFiles = await walkDir(searchDir, searchDir);
|
|
2482
|
+
const matched = allFiles.filter((f) => regex.test(f));
|
|
2483
|
+
if (matched.length === 0) {
|
|
2484
|
+
return {
|
|
2485
|
+
success: true,
|
|
2486
|
+
data: `\u672A\u627E\u5230\u5339\u914D "${params.pattern}" \u7684\u6587\u4EF6`
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2489
|
+
const limit = 200;
|
|
2490
|
+
const limited = matched.slice(0, limit);
|
|
2491
|
+
const output = limited.join("\n");
|
|
2492
|
+
const suffix = matched.length > limit ? `
|
|
2493
|
+
|
|
2494
|
+
... \u5171 ${matched.length} \u4E2A\u6587\u4EF6\uFF0C\u53EA\u663E\u793A\u524D ${limit} \u4E2A` : "";
|
|
2495
|
+
return {
|
|
2496
|
+
success: true,
|
|
2497
|
+
data: truncateOutput(output + suffix)
|
|
2498
|
+
};
|
|
2499
|
+
} catch (err) {
|
|
2500
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2501
|
+
return {
|
|
2502
|
+
success: false,
|
|
2503
|
+
data: `\u6587\u4EF6\u641C\u7D22\u5931\u8D25\uFF1A${message}`,
|
|
2504
|
+
error: "GLOB_ERROR"
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
|
|
2510
|
+
// src/tool/builtins/grep.ts
|
|
2511
|
+
import { readdir as readdir3, readFile as readFile7, stat as stat3 } from "fs/promises";
|
|
2512
|
+
import { join as join5, relative as relative3, isAbsolute as isAbsolute3 } from "path";
|
|
2513
|
+
var grepSchema = {
|
|
2514
|
+
type: "object",
|
|
2515
|
+
properties: {
|
|
2516
|
+
pattern: {
|
|
2517
|
+
type: "string",
|
|
2518
|
+
description: "\u6B63\u5219\u8868\u8FBE\u5F0F\u641C\u7D22\u6A21\u5F0F"
|
|
2519
|
+
},
|
|
2520
|
+
directory: {
|
|
2521
|
+
type: "string",
|
|
2522
|
+
description: "\u641C\u7D22\u7684\u76EE\u5F55\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55"
|
|
2523
|
+
},
|
|
2524
|
+
include: {
|
|
2525
|
+
type: "string",
|
|
2526
|
+
description: "\u6587\u4EF6\u6269\u5C55\u540D\u8FC7\u6EE4\uFF08\u5982 ts\u3001json\uFF09\uFF0C\u4E0D\u542B\u70B9\u53F7"
|
|
2527
|
+
},
|
|
2528
|
+
case_sensitive: {
|
|
2529
|
+
type: "boolean",
|
|
2530
|
+
description: "\u662F\u5426\u5927\u5C0F\u5199\u654F\u611F\uFF0C\u9ED8\u8BA4 false"
|
|
2531
|
+
},
|
|
2532
|
+
max_files: {
|
|
2533
|
+
type: "number",
|
|
2534
|
+
description: "\u6700\u5927\u641C\u7D22\u6587\u4EF6\u6570\uFF0C\u9ED8\u8BA4 200"
|
|
2535
|
+
}
|
|
2536
|
+
},
|
|
2537
|
+
required: ["pattern"],
|
|
2538
|
+
additionalProperties: false
|
|
2539
|
+
};
|
|
2540
|
+
async function collectFiles(dir, baseDir, extension, maxFiles = 200) {
|
|
2541
|
+
const results = [];
|
|
2542
|
+
let entries;
|
|
2543
|
+
try {
|
|
2544
|
+
entries = await readdir3(dir, { withFileTypes: true });
|
|
2545
|
+
} catch {
|
|
2546
|
+
return results;
|
|
2547
|
+
}
|
|
2548
|
+
for (const entry of entries) {
|
|
2549
|
+
if (results.length >= maxFiles) break;
|
|
2550
|
+
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist")) {
|
|
2551
|
+
continue;
|
|
2552
|
+
}
|
|
2553
|
+
const fullPath = join5(dir, entry.name);
|
|
2554
|
+
if (entry.isDirectory()) {
|
|
2555
|
+
results.push(...await collectFiles(fullPath, baseDir, extension, maxFiles - results.length));
|
|
2556
|
+
} else {
|
|
2557
|
+
if (extension && !entry.name.endsWith(`.${extension}`)) {
|
|
2558
|
+
continue;
|
|
2559
|
+
}
|
|
2560
|
+
results.push(fullPath);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
return results;
|
|
2564
|
+
}
|
|
2565
|
+
var grepTool = {
|
|
2566
|
+
name: "grep",
|
|
2567
|
+
description: "\u5728\u6587\u4EF6\u5185\u5BB9\u4E2D\u641C\u7D22\u6B63\u5219\u8868\u8FBE\u5F0F\u3002\u8FD4\u56DE\u5339\u914D\u884C\u7684\u6587\u4EF6\u8DEF\u5F84\u3001\u884C\u53F7\u548C\u5185\u5BB9\u3002\u652F\u6301\u5927\u5C0F\u5199\u654F\u611F\u3001\u6587\u4EF6\u6269\u5C55\u540D\u8FC7\u6EE4\u3002",
|
|
2568
|
+
parameters: grepSchema,
|
|
2569
|
+
async execute(args, ctx) {
|
|
2570
|
+
const params = args;
|
|
2571
|
+
if (!params?.pattern || typeof params.pattern !== "string") {
|
|
2572
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
|
|
2573
|
+
}
|
|
2574
|
+
const searchDir = params.directory ? isAbsolute3(params.directory) ? params.directory : join5(ctx.cwd, params.directory) : ctx.cwd;
|
|
2575
|
+
const maxFiles = params.max_files ?? 200;
|
|
2576
|
+
try {
|
|
2577
|
+
const flags = params.case_sensitive ? "g" : "gi";
|
|
2578
|
+
const regex = new RegExp(params.pattern, flags);
|
|
2579
|
+
const dirStat = await stat3(searchDir);
|
|
2580
|
+
if (!dirStat.isDirectory()) {
|
|
2581
|
+
return { success: false, data: `\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${searchDir}`, error: "NOT_DIRECTORY" };
|
|
2582
|
+
}
|
|
2583
|
+
const files = await collectFiles(searchDir, searchDir, params.include, maxFiles);
|
|
2584
|
+
const matches = [];
|
|
2585
|
+
for (const filePath of files) {
|
|
2586
|
+
try {
|
|
2587
|
+
const content = await readFile7(filePath, "utf-8");
|
|
2588
|
+
const lines = content.split("\n");
|
|
2589
|
+
const relPath = relative3(searchDir, filePath);
|
|
2590
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2591
|
+
if (regex.test(lines[i])) {
|
|
2592
|
+
regex.lastIndex = 0;
|
|
2593
|
+
matches.push({
|
|
2594
|
+
file: relPath,
|
|
2595
|
+
line: i + 1,
|
|
2596
|
+
content: lines[i]
|
|
2597
|
+
});
|
|
2598
|
+
if (matches.length >= 500) break;
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
if (matches.length >= 500) break;
|
|
2602
|
+
} catch {
|
|
2603
|
+
continue;
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
if (matches.length === 0) {
|
|
2607
|
+
return {
|
|
2608
|
+
success: true,
|
|
2609
|
+
data: `\u672A\u627E\u5230\u5339\u914D "${params.pattern}" \u7684\u5185\u5BB9`
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
const output = matches.map((m) => `${m.file}:${m.line}: ${m.content}`).join("\n");
|
|
2613
|
+
return {
|
|
2614
|
+
success: true,
|
|
2615
|
+
data: truncateOutput(output)
|
|
2616
|
+
};
|
|
2617
|
+
} catch (err) {
|
|
2618
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2619
|
+
return {
|
|
2620
|
+
success: false,
|
|
2621
|
+
data: `\u5185\u5BB9\u641C\u7D22\u5931\u8D25\uFF1A${message}`,
|
|
2622
|
+
error: "GREP_ERROR"
|
|
2623
|
+
};
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
};
|
|
2627
|
+
|
|
2628
|
+
// src/tool/builtins/ls.ts
|
|
2629
|
+
import { readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
2630
|
+
import { join as join6 } from "path";
|
|
2631
|
+
var lsSchema = {
|
|
2632
|
+
type: "object",
|
|
2633
|
+
properties: {
|
|
2634
|
+
path: {
|
|
2635
|
+
type: "string",
|
|
2636
|
+
description: "\u76EE\u5F55\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u76EE\u5F55"
|
|
2637
|
+
},
|
|
2638
|
+
all: {
|
|
2639
|
+
type: "boolean",
|
|
2640
|
+
description: "\u662F\u5426\u663E\u793A\u9690\u85CF\u6587\u4EF6\uFF08\u4EE5 . \u5F00\u5934\u7684\u6587\u4EF6\uFF09\uFF0C\u9ED8\u8BA4 false"
|
|
2641
|
+
}
|
|
2642
|
+
},
|
|
2643
|
+
required: [],
|
|
2644
|
+
additionalProperties: false
|
|
2645
|
+
};
|
|
2646
|
+
var lsTool = {
|
|
2647
|
+
name: "ls",
|
|
2648
|
+
description: "\u5217\u51FA\u76EE\u5F55\u5185\u5BB9\u3002\u663E\u793A\u6761\u76EE\u7C7B\u578B\uFF08\u6587\u4EF6/\u76EE\u5F55/\u94FE\u63A5\uFF09\u548C\u5927\u5C0F\u3002\u53EF\u9009\u62E9\u662F\u5426\u663E\u793A\u9690\u85CF\u6587\u4EF6\u3002",
|
|
2649
|
+
parameters: lsSchema,
|
|
2650
|
+
async execute(args, ctx) {
|
|
2651
|
+
const params = args ?? {};
|
|
2652
|
+
const dirPath = params.path ? resolvePath(params.path, ctx.cwd) : ctx.cwd;
|
|
2653
|
+
const showAll = params.all ?? false;
|
|
2654
|
+
try {
|
|
2655
|
+
const entries = await readdir4(dirPath, { withFileTypes: true });
|
|
2656
|
+
const lines = [];
|
|
2657
|
+
const sorted = [...entries].sort((a, b) => {
|
|
2658
|
+
if (a.isDirectory() !== b.isDirectory()) {
|
|
2659
|
+
return a.isDirectory() ? -1 : 1;
|
|
2660
|
+
}
|
|
2661
|
+
return a.name.localeCompare(b.name);
|
|
2662
|
+
});
|
|
2663
|
+
for (const entry of sorted) {
|
|
2664
|
+
if (!showAll && entry.name.startsWith(".")) continue;
|
|
2665
|
+
const typeMark = entry.isDirectory() ? "DIR" : entry.isSymbolicLink() ? "LINK" : "FILE";
|
|
2666
|
+
let sizeStr = "";
|
|
2667
|
+
if (typeMark === "FILE") {
|
|
2668
|
+
try {
|
|
2669
|
+
const fileStat = await stat4(join6(dirPath, entry.name));
|
|
2670
|
+
if (fileStat.size < 1024) {
|
|
2671
|
+
sizeStr = `${fileStat.size}B`;
|
|
2672
|
+
} else if (fileStat.size < 1024 * 1024) {
|
|
2673
|
+
sizeStr = `${(fileStat.size / 1024).toFixed(1)}KB`;
|
|
2674
|
+
} else {
|
|
2675
|
+
sizeStr = `${(fileStat.size / 1024 / 1024).toFixed(1)}MB`;
|
|
2676
|
+
}
|
|
2677
|
+
} catch {
|
|
2678
|
+
sizeStr = "?";
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
const typeLabel = typeMark === "DIR" ? "\u{1F4C1}" : typeMark === "LINK" ? "\u{1F517}" : "\u{1F4C4}";
|
|
2682
|
+
lines.push(`${typeLabel} ${entry.name}${sizeStr ? ` (${sizeStr})` : ""}`);
|
|
2683
|
+
}
|
|
2684
|
+
if (lines.length === 0) {
|
|
2685
|
+
return { success: true, data: "\u76EE\u5F55\u4E3A\u7A7A" };
|
|
2686
|
+
}
|
|
2687
|
+
return {
|
|
2688
|
+
success: true,
|
|
2689
|
+
data: truncateOutput(`\u76EE\u5F55\uFF1A${dirPath}
|
|
2690
|
+
${lines.join("\n")}`)
|
|
2691
|
+
};
|
|
2692
|
+
} catch (err) {
|
|
2693
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2694
|
+
return {
|
|
2695
|
+
success: false,
|
|
2696
|
+
data: `\u5217\u76EE\u5F55\u5931\u8D25\uFF1A${message}`,
|
|
2697
|
+
error: "LS_ERROR"
|
|
2698
|
+
};
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
};
|
|
2702
|
+
|
|
2703
|
+
// src/tool/builtins/fetch.ts
|
|
2704
|
+
var fetchSchema = {
|
|
2705
|
+
type: "object",
|
|
2706
|
+
properties: {
|
|
2707
|
+
url: {
|
|
2708
|
+
type: "string",
|
|
2709
|
+
description: "\u8BF7\u6C42\u7684 URL"
|
|
2710
|
+
},
|
|
2711
|
+
method: {
|
|
2712
|
+
type: "string",
|
|
2713
|
+
description: "HTTP \u65B9\u6CD5\uFF08GET\u3001POST\u3001PUT\u3001DELETE \u7B49\uFF09\uFF0C\u9ED8\u8BA4 GET"
|
|
2714
|
+
},
|
|
2715
|
+
headers: {
|
|
2716
|
+
type: "object",
|
|
2717
|
+
description: "\u8BF7\u6C42\u5934\u952E\u503C\u5BF9",
|
|
2718
|
+
additionalProperties: { type: "string" }
|
|
2719
|
+
},
|
|
2720
|
+
body: {
|
|
2721
|
+
type: "string",
|
|
2722
|
+
description: "\u8BF7\u6C42\u4F53\u5185\u5BB9\uFF08POST/PUT \u65F6\u4F7F\u7528\uFF09"
|
|
2723
|
+
},
|
|
2724
|
+
max_length: {
|
|
2725
|
+
type: "number",
|
|
2726
|
+
description: "\u54CD\u5E94\u5185\u5BB9\u6700\u5927\u957F\u5EA6\uFF08\u5B57\u7B26\uFF09\uFF0C\u9ED8\u8BA4 50000"
|
|
2727
|
+
}
|
|
2728
|
+
},
|
|
2729
|
+
required: ["url"],
|
|
2730
|
+
additionalProperties: false
|
|
2731
|
+
};
|
|
2732
|
+
var fetchTool = {
|
|
2733
|
+
name: "fetch",
|
|
2734
|
+
description: "\u53D1\u8D77 HTTP \u8BF7\u6C42\u5E76\u8FD4\u56DE\u54CD\u5E94\u5185\u5BB9\u3002\u652F\u6301\u81EA\u5B9A\u4E49\u65B9\u6CD5\u548C\u8BF7\u6C42\u5934\u3002\u9002\u7528\u4E8E\u83B7\u53D6\u7F51\u9875\u5185\u5BB9\u3001API \u8C03\u7528\u7B49\u573A\u666F\u3002",
|
|
2735
|
+
parameters: fetchSchema,
|
|
2736
|
+
async execute(args, ctx) {
|
|
2737
|
+
const params = args;
|
|
2738
|
+
if (!params?.url || typeof params.url !== "string") {
|
|
2739
|
+
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 url", error: "INVALID_ARGS" };
|
|
2740
|
+
}
|
|
2741
|
+
const method = (params.method ?? "GET").toUpperCase();
|
|
2742
|
+
const timeout = 3e4;
|
|
2743
|
+
const maxLength = params.max_length ?? 5e4;
|
|
2744
|
+
try {
|
|
2745
|
+
const fetchOptions = {
|
|
2746
|
+
method,
|
|
2747
|
+
headers: params.headers ?? {},
|
|
2748
|
+
signal: ctx.signal ?? AbortSignal.timeout(timeout)
|
|
2749
|
+
};
|
|
2750
|
+
if (params.body && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
2751
|
+
fetchOptions.headers["Content-Type"] ??= "application/json";
|
|
2752
|
+
fetchOptions.body = params.body;
|
|
2753
|
+
}
|
|
2754
|
+
const response = await fetch(params.url, fetchOptions);
|
|
2755
|
+
const contentType = response.headers.get("content-type") ?? "unknown";
|
|
2756
|
+
const statusText = `${response.status} ${response.statusText}`;
|
|
2757
|
+
let body;
|
|
2758
|
+
try {
|
|
2759
|
+
body = await response.text();
|
|
2760
|
+
} catch {
|
|
2761
|
+
body = "(\u65E0\u6CD5\u8BFB\u53D6\u54CD\u5E94\u4F53)";
|
|
2762
|
+
}
|
|
2763
|
+
const truncatedBody = truncateOutput(body, maxLength);
|
|
2764
|
+
const header = `\u72B6\u6001: ${statusText}
|
|
2765
|
+
\u5185\u5BB9\u7C7B\u578B: ${contentType}`;
|
|
2766
|
+
const separator = body.length > 0 ? "\n---\n" : "";
|
|
2767
|
+
return {
|
|
2768
|
+
success: response.ok,
|
|
2769
|
+
data: `${header}${separator}${truncatedBody}`,
|
|
2770
|
+
error: response.ok ? void 0 : `HTTP_${response.status}`
|
|
2771
|
+
};
|
|
2772
|
+
} catch (err) {
|
|
2773
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
2774
|
+
return { success: false, data: "\u8BF7\u6C42\u88AB\u4E2D\u6B62", error: "ABORTED" };
|
|
2775
|
+
}
|
|
2776
|
+
if (err instanceof TypeError && err.message.includes("fetch")) {
|
|
2777
|
+
return { success: false, data: `\u7F51\u7EDC\u9519\u8BEF\uFF1A${err.message}`, error: "NETWORK_ERROR" };
|
|
2778
|
+
}
|
|
2779
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2780
|
+
return {
|
|
2781
|
+
success: false,
|
|
2782
|
+
data: `HTTP \u8BF7\u6C42\u5931\u8D25\uFF1A${message}`,
|
|
2783
|
+
error: "FETCH_ERROR"
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
|
|
2789
|
+
// src/tool/builtins/index.ts
|
|
2790
|
+
var builtinTools = [
|
|
2791
|
+
readFileTool,
|
|
2792
|
+
writeFileTool,
|
|
2793
|
+
editFileTool,
|
|
2794
|
+
bashTool,
|
|
2795
|
+
globTool,
|
|
2796
|
+
grepTool,
|
|
2797
|
+
lsTool,
|
|
2798
|
+
fetchTool
|
|
2799
|
+
];
|
|
2800
|
+
|
|
1529
2801
|
// src/utils/gradient.ts
|
|
1530
2802
|
var IDLE_GRADIENT_STOPS = [
|
|
1531
2803
|
[0, 255, 65],
|
|
@@ -1590,7 +2862,7 @@ function getGradientColors(text, phase, stops) {
|
|
|
1590
2862
|
}
|
|
1591
2863
|
|
|
1592
2864
|
// src/ui/ChatSession.tsx
|
|
1593
|
-
import { Fragment, jsx as
|
|
2865
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1594
2866
|
var commandRegistry = /* @__PURE__ */ new Map();
|
|
1595
2867
|
function registerCommand(name, cmd) {
|
|
1596
2868
|
commandRegistry.set(name, cmd);
|
|
@@ -1637,6 +2909,8 @@ function pickRandom(arr) {
|
|
|
1637
2909
|
}
|
|
1638
2910
|
function ChatSession({
|
|
1639
2911
|
skillCount,
|
|
2912
|
+
skills = [],
|
|
2913
|
+
files = [],
|
|
1640
2914
|
toolCount,
|
|
1641
2915
|
verbose,
|
|
1642
2916
|
apiKey,
|
|
@@ -1674,6 +2948,9 @@ function ChatSession({
|
|
|
1674
2948
|
const [cmdTipIndex, setCmdTipIndex] = useState3(0);
|
|
1675
2949
|
const [cmdTipGradientColors, setCmdTipGradientColors] = useState3([]);
|
|
1676
2950
|
const cmdTipPhaseRef = useRef2(0);
|
|
2951
|
+
const [skillSelectIndex, setSkillSelectIndex] = useState3(0);
|
|
2952
|
+
const [fileSelectIndex, setFileSelectIndex] = useState3(0);
|
|
2953
|
+
const [inputKey, setInputKey] = useState3(0);
|
|
1677
2954
|
const [selectingModel, setSelectingModel] = useState3(false);
|
|
1678
2955
|
const [modelSelectIndex, setModelSelectIndex] = useState3(0);
|
|
1679
2956
|
const modelOptions = ["deepseek-v4-flash", "deepseek-v4-pro"];
|
|
@@ -1686,6 +2963,40 @@ function ChatSession({
|
|
|
1686
2963
|
const currentCostRef = useRef2(void 0);
|
|
1687
2964
|
const currentModelRef = useRef2(void 0);
|
|
1688
2965
|
const streamErrorRef = useRef2(void 0);
|
|
2966
|
+
useEffect3(() => {
|
|
2967
|
+
setSkillSelectIndex(0);
|
|
2968
|
+
setFileSelectIndex(0);
|
|
2969
|
+
}, [input]);
|
|
2970
|
+
const getFilteredSkills = useCallback2(
|
|
2971
|
+
(value) => {
|
|
2972
|
+
const match = value.match(/(?:^|\s)\/([^/]*)$/);
|
|
2973
|
+
if (!match) return [];
|
|
2974
|
+
const q = match[1].toLowerCase().trim();
|
|
2975
|
+
if (!q) {
|
|
2976
|
+
if (value.startsWith("/")) return skills.slice(0, 3);
|
|
2977
|
+
return [];
|
|
2978
|
+
}
|
|
2979
|
+
const matched = skills.filter((s) => s.name.toLowerCase().includes(q)).slice(0, 3);
|
|
2980
|
+
if (matched.some((s) => s.name.toLowerCase() === q)) return [];
|
|
2981
|
+
return matched;
|
|
2982
|
+
},
|
|
2983
|
+
[skills]
|
|
2984
|
+
);
|
|
2985
|
+
const getFilteredFiles = useCallback2(
|
|
2986
|
+
(value) => {
|
|
2987
|
+
const match = value.match(/(?:^|\s)@([^@]*)$/);
|
|
2988
|
+
if (!match) return [];
|
|
2989
|
+
const q = match[1].toLowerCase().trim();
|
|
2990
|
+
if (!q) {
|
|
2991
|
+
if (value.startsWith("@")) return files.slice(0, 5);
|
|
2992
|
+
return [];
|
|
2993
|
+
}
|
|
2994
|
+
const matched = files.filter((f) => f.toLowerCase().includes(q)).slice(0, 5);
|
|
2995
|
+
if (matched.some((f) => f.toLowerCase() === q)) return [];
|
|
2996
|
+
return matched;
|
|
2997
|
+
},
|
|
2998
|
+
[files]
|
|
2999
|
+
);
|
|
1689
3000
|
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {
|
|
1690
3001
|
process.exit(0);
|
|
1691
3002
|
});
|
|
@@ -1720,6 +3031,50 @@ function ChatSession({
|
|
|
1720
3031
|
}
|
|
1721
3032
|
return;
|
|
1722
3033
|
}
|
|
3034
|
+
const fileList = getFilteredFiles(input);
|
|
3035
|
+
const skillList = fileList.length === 0 ? getFilteredSkills(input) : [];
|
|
3036
|
+
if (fileList.length > 0) {
|
|
3037
|
+
if (key.upArrow) {
|
|
3038
|
+
setFileSelectIndex((prev) => (prev - 1 + fileList.length) % fileList.length);
|
|
3039
|
+
return;
|
|
3040
|
+
}
|
|
3041
|
+
if (key.downArrow) {
|
|
3042
|
+
setFileSelectIndex((prev) => (prev + 1) % fileList.length);
|
|
3043
|
+
return;
|
|
3044
|
+
}
|
|
3045
|
+
if (key.tab) {
|
|
3046
|
+
const selected = fileList[fileSelectIndex];
|
|
3047
|
+
if (selected) {
|
|
3048
|
+
const atIdx = input.lastIndexOf("@");
|
|
3049
|
+
if (atIdx >= 0) {
|
|
3050
|
+
setInput(input.slice(0, atIdx) + "@" + selected + " ");
|
|
3051
|
+
setInputKey((k) => k + 1);
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
return;
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
if (skillList.length > 0) {
|
|
3058
|
+
if (key.upArrow) {
|
|
3059
|
+
setSkillSelectIndex((prev) => (prev - 1 + skillList.length) % skillList.length);
|
|
3060
|
+
return;
|
|
3061
|
+
}
|
|
3062
|
+
if (key.downArrow) {
|
|
3063
|
+
setSkillSelectIndex((prev) => (prev + 1) % skillList.length);
|
|
3064
|
+
return;
|
|
3065
|
+
}
|
|
3066
|
+
if (key.tab) {
|
|
3067
|
+
const selected = skillList[skillSelectIndex];
|
|
3068
|
+
if (selected) {
|
|
3069
|
+
const slashIdx = input.lastIndexOf("/");
|
|
3070
|
+
if (slashIdx >= 0) {
|
|
3071
|
+
setInput(input.slice(0, slashIdx) + "/" + selected.name + " ");
|
|
3072
|
+
setInputKey((k) => k + 1);
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
1723
3078
|
if (key.ctrl && _input === "c") {
|
|
1724
3079
|
if (isStreaming) {
|
|
1725
3080
|
abortRef.current?.abort();
|
|
@@ -1732,7 +3087,7 @@ function ChatSession({
|
|
|
1732
3087
|
setInput(_input);
|
|
1733
3088
|
}
|
|
1734
3089
|
},
|
|
1735
|
-
[selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input]
|
|
3090
|
+
[selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input, skills, skillSelectIndex, fileSelectIndex, getFilteredSkills, getFilteredFiles]
|
|
1736
3091
|
)
|
|
1737
3092
|
);
|
|
1738
3093
|
useEffect3(() => {
|
|
@@ -1772,7 +3127,9 @@ function ChatSession({
|
|
|
1772
3127
|
model: activeModel
|
|
1773
3128
|
});
|
|
1774
3129
|
const tracker = externalCostTracker ?? new CostTracker();
|
|
1775
|
-
const
|
|
3130
|
+
const toolRegistry = new ToolRegistry();
|
|
3131
|
+
toolRegistry.registerAll(builtinTools);
|
|
3132
|
+
const session = new Session(provider, toolRegistry, tracker, {
|
|
1776
3133
|
cwd: process.cwd()
|
|
1777
3134
|
});
|
|
1778
3135
|
sessionRef.current = session;
|
|
@@ -1784,7 +3141,7 @@ function ChatSession({
|
|
|
1784
3141
|
if (!apiKey || !baseUrl) return;
|
|
1785
3142
|
let cancelled = false;
|
|
1786
3143
|
setBalanceLoading(true);
|
|
1787
|
-
import("./deepseek-
|
|
3144
|
+
import("./deepseek-UJXV2D6K.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
|
|
1788
3145
|
const provider = new DeepSeekProvider2({
|
|
1789
3146
|
apiKey,
|
|
1790
3147
|
baseUrl,
|
|
@@ -1852,7 +3209,7 @@ function ChatSession({
|
|
|
1852
3209
|
const handleSubmit = useCallback2(async (value) => {
|
|
1853
3210
|
const trimmed = value.trim();
|
|
1854
3211
|
if (!trimmed) return;
|
|
1855
|
-
if (trimmed.startsWith("/")) {
|
|
3212
|
+
if (trimmed.startsWith("/") && trimmed.length > 1) {
|
|
1856
3213
|
if (trimmed.toLowerCase() === "/model") {
|
|
1857
3214
|
const curIdx = modelOptions.indexOf(activeModel);
|
|
1858
3215
|
setModelSelectIndex(curIdx >= 0 ? curIdx : 0);
|
|
@@ -1952,6 +3309,20 @@ function ChatSession({
|
|
|
1952
3309
|
return next;
|
|
1953
3310
|
});
|
|
1954
3311
|
break;
|
|
3312
|
+
case "tool_result":
|
|
3313
|
+
setCurrentContent("");
|
|
3314
|
+
currentContentRef.current = "";
|
|
3315
|
+
setCurrentToolCalls([]);
|
|
3316
|
+
currentToolCallsRef.current = [];
|
|
3317
|
+
setDisplayMessages((prev) => [
|
|
3318
|
+
...prev,
|
|
3319
|
+
{
|
|
3320
|
+
role: "tool",
|
|
3321
|
+
content: event.result.success ? `\u2705 ${event.name}: ${event.result.data.slice(0, 500)}${event.result.data.length > 500 ? "..." : ""}` : `\u274C ${event.name}: ${event.result.error ?? "\u6267\u884C\u5931\u8D25"}`,
|
|
3322
|
+
diff: event.result.diff
|
|
3323
|
+
}
|
|
3324
|
+
]);
|
|
3325
|
+
break;
|
|
1955
3326
|
case "usage":
|
|
1956
3327
|
setCurrentUsage(event.usage);
|
|
1957
3328
|
setStreamingModel(event.model);
|
|
@@ -1998,7 +3369,7 @@ function ChatSession({
|
|
|
1998
3369
|
]);
|
|
1999
3370
|
}
|
|
2000
3371
|
}
|
|
2001
|
-
}, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls]);
|
|
3372
|
+
}, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills]);
|
|
2002
3373
|
useEffect3(() => {
|
|
2003
3374
|
if (!isStreaming && externalCostTracker) {
|
|
2004
3375
|
setTodayCost(externalCostTracker.todayTotalCost);
|
|
@@ -2013,26 +3384,26 @@ function ChatSession({
|
|
|
2013
3384
|
});
|
|
2014
3385
|
}
|
|
2015
3386
|
}, [currentUsage, streamingModel, currentCost]);
|
|
2016
|
-
return /* @__PURE__ */
|
|
2017
|
-
/* @__PURE__ */
|
|
2018
|
-
/* @__PURE__ */
|
|
3387
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
|
|
3388
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", marginBottom: 1, children: [
|
|
3389
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
|
|
2019
3390
|
const colorIndex = (i + offset) % CYBER_PALETTE.length;
|
|
2020
|
-
return /* @__PURE__ */
|
|
3391
|
+
return /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text9, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }) }, i);
|
|
2021
3392
|
}) }),
|
|
2022
|
-
/* @__PURE__ */
|
|
2023
|
-
/* @__PURE__ */
|
|
3393
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", justifyContent: "center", children: [
|
|
3394
|
+
/* @__PURE__ */ jsxs8(Text9, { color: "#00ff41", children: [
|
|
2024
3395
|
" \u2714 ",
|
|
2025
|
-
"\u5DF2\
|
|
3396
|
+
"\u5DF2\u5C31\u7EEA ",
|
|
2026
3397
|
skillCount,
|
|
2027
3398
|
" \u4E2A Skill"
|
|
2028
3399
|
] }),
|
|
2029
|
-
/* @__PURE__ */
|
|
3400
|
+
/* @__PURE__ */ jsxs8(Text9, { color: "#00ffff", children: [
|
|
2030
3401
|
" \u2139 ",
|
|
2031
3402
|
"\u5DF2\u5C31\u7EEA ",
|
|
2032
3403
|
toolCount,
|
|
2033
3404
|
" \u4E2A\u5DE5\u5177"
|
|
2034
3405
|
] }),
|
|
2035
|
-
/* @__PURE__ */
|
|
3406
|
+
/* @__PURE__ */ jsxs8(Text9, { color: "#00ffff", children: [
|
|
2036
3407
|
" \u{1F527} \u6A21\u578B ",
|
|
2037
3408
|
SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel
|
|
2038
3409
|
] }),
|
|
@@ -2040,40 +3411,49 @@ function ChatSession({
|
|
|
2040
3411
|
const tip = cmdTips[cmdTipIndex % cmdTips.length];
|
|
2041
3412
|
if (!tip) return null;
|
|
2042
3413
|
const text = `${tip.name} ${tip.desc}`;
|
|
2043
|
-
return /* @__PURE__ */
|
|
2044
|
-
/* @__PURE__ */
|
|
2045
|
-
cmdTipGradientColors.length > 0 ? text.split("").map((ch, i) => /* @__PURE__ */
|
|
3414
|
+
return /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
3415
|
+
/* @__PURE__ */ jsx8(Text9, { color: "#808080", children: " \u{1F4A1} " }),
|
|
3416
|
+
cmdTipGradientColors.length > 0 ? text.split("").map((ch, i) => /* @__PURE__ */ jsx8(Text9, { color: cmdTipGradientColors[i] || void 0, children: ch }, i)) : /* @__PURE__ */ jsx8(Text9, { color: "#808080", children: text })
|
|
2046
3417
|
] });
|
|
2047
3418
|
})(),
|
|
2048
|
-
verbose ? /* @__PURE__ */
|
|
3419
|
+
verbose ? /* @__PURE__ */ jsx8(Text9, { color: "#ff1493", children: " \u26A1 Verbose" }) : null
|
|
2049
3420
|
] }),
|
|
2050
|
-
/* @__PURE__ */
|
|
2051
|
-
balanceLoading && balance === null ? /* @__PURE__ */
|
|
2052
|
-
/* @__PURE__ */
|
|
2053
|
-
/* @__PURE__ */
|
|
3421
|
+
/* @__PURE__ */ jsxs8(Box8, { flexGrow: 1, flexDirection: "column", justifyContent: "center", alignItems: "flex-end", children: [
|
|
3422
|
+
balanceLoading && balance === null ? /* @__PURE__ */ jsx8(Text9, { color: "yellow", children: " \u23F3 \u67E5\u8BE2\u4F59\u989D..." }) : balance !== null ? /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", children: [
|
|
3423
|
+
/* @__PURE__ */ jsx8(Text9, { color: "yellow", children: "\u{1F4B0} " }),
|
|
3424
|
+
/* @__PURE__ */ jsxs8(Text9, { color: "yellow", children: [
|
|
2054
3425
|
"\u4F59\u989D \xA5",
|
|
2055
3426
|
balance.toFixed(2)
|
|
2056
3427
|
] })
|
|
2057
3428
|
] }) : null,
|
|
2058
|
-
todayCost !== null ? /* @__PURE__ */
|
|
2059
|
-
/* @__PURE__ */
|
|
2060
|
-
/* @__PURE__ */
|
|
3429
|
+
todayCost !== null ? /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", children: [
|
|
3430
|
+
/* @__PURE__ */ jsx8(Text9, { color: "cyan", children: "\u{1F4CA} " }),
|
|
3431
|
+
/* @__PURE__ */ jsxs8(Text9, { color: "cyan", children: [
|
|
2061
3432
|
"\u4ECA\u65E5 \xA5",
|
|
2062
3433
|
todayCost.toFixed(2)
|
|
2063
3434
|
] })
|
|
2064
3435
|
] }) : null
|
|
2065
3436
|
] })
|
|
2066
3437
|
] }),
|
|
2067
|
-
/* @__PURE__ */
|
|
2068
|
-
/* @__PURE__ */
|
|
3438
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
3439
|
+
/* @__PURE__ */ jsx8(Static, { items: displayMessages, children: (msg, i) => {
|
|
2069
3440
|
if (msg.role === "user") {
|
|
2070
|
-
return /* @__PURE__ */
|
|
2071
|
-
/* @__PURE__ */
|
|
2072
|
-
/* @__PURE__ */
|
|
3441
|
+
return /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
3442
|
+
/* @__PURE__ */ jsx8(Box8, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text9, { bold: true, color: "#00ff41", children: "\u{1F464}" }) }),
|
|
3443
|
+
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx8(Text9, { wrap: "wrap", children: msg.content }) })
|
|
3444
|
+
] }, i);
|
|
3445
|
+
}
|
|
3446
|
+
if (msg.role === "tool") {
|
|
3447
|
+
return /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
3448
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", children: [
|
|
3449
|
+
/* @__PURE__ */ jsx8(Box8, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text9, { bold: true, color: "#f59e0b", children: "\u{1F527}" }) }),
|
|
3450
|
+
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx8(Text9, { wrap: "wrap", children: msg.content }) })
|
|
3451
|
+
] }),
|
|
3452
|
+
msg.diff && /* @__PURE__ */ jsx8(DiffPreview, { diff: msg.diff })
|
|
2073
3453
|
] }, i);
|
|
2074
3454
|
}
|
|
2075
3455
|
const detail = msg.assistantDetail;
|
|
2076
|
-
return /* @__PURE__ */
|
|
3456
|
+
return /* @__PURE__ */ jsx8(
|
|
2077
3457
|
AssistantMessage,
|
|
2078
3458
|
{
|
|
2079
3459
|
content: msg.content,
|
|
@@ -2087,7 +3467,7 @@ function ChatSession({
|
|
|
2087
3467
|
i
|
|
2088
3468
|
);
|
|
2089
3469
|
} }, staticKey),
|
|
2090
|
-
isStreaming && /* @__PURE__ */
|
|
3470
|
+
isStreaming && /* @__PURE__ */ jsx8(
|
|
2091
3471
|
AssistantMessage,
|
|
2092
3472
|
{
|
|
2093
3473
|
content: currentContent,
|
|
@@ -2095,25 +3475,25 @@ function ChatSession({
|
|
|
2095
3475
|
isStreaming: true
|
|
2096
3476
|
}
|
|
2097
3477
|
),
|
|
2098
|
-
isStreaming && !currentContent && currentToolCalls.length === 0 && /* @__PURE__ */
|
|
2099
|
-
!isStreaming && streamError && /* @__PURE__ */
|
|
3478
|
+
isStreaming && !currentContent && currentToolCalls.length === 0 && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, marginLeft: 4, children: /* @__PURE__ */ jsx8(Spinner, { type: "dots", label: "\u601D\u8003\u4E2D..." }) }),
|
|
3479
|
+
!isStreaming && streamError && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs8(Text9, { color: "red", children: [
|
|
2100
3480
|
"\u26A0 ",
|
|
2101
3481
|
streamError
|
|
2102
3482
|
] }) })
|
|
2103
3483
|
] }),
|
|
2104
|
-
selectingModel ? /* @__PURE__ */
|
|
2105
|
-
/* @__PURE__ */
|
|
2106
|
-
/* @__PURE__ */
|
|
2107
|
-
/* @__PURE__ */
|
|
3484
|
+
selectingModel ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
3485
|
+
/* @__PURE__ */ jsx8(Text9, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }),
|
|
3486
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
3487
|
+
/* @__PURE__ */ jsx8(Text9, { bold: true, color: "#00ffff", children: "\u9009\u62E9\u6A21\u578B\uFF1A" }),
|
|
2108
3488
|
modelOptions.map((id, i) => {
|
|
2109
3489
|
const meta = SUPPORTED_MODELS[id];
|
|
2110
3490
|
const isCurrent = id === activeModel;
|
|
2111
3491
|
const isSelected = i === modelSelectIndex;
|
|
2112
3492
|
const marker = isSelected ? " > " : " ";
|
|
2113
3493
|
const suffix = isCurrent ? " (\u5F53\u524D)" : "";
|
|
2114
|
-
return /* @__PURE__ */
|
|
2115
|
-
/* @__PURE__ */
|
|
2116
|
-
|
|
3494
|
+
return /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3495
|
+
/* @__PURE__ */ jsxs8(
|
|
3496
|
+
Text9,
|
|
2117
3497
|
{
|
|
2118
3498
|
color: isSelected ? "#00ff41" : void 0,
|
|
2119
3499
|
bold: isSelected,
|
|
@@ -2124,40 +3504,43 @@ function ChatSession({
|
|
|
2124
3504
|
]
|
|
2125
3505
|
}
|
|
2126
3506
|
),
|
|
2127
|
-
isSelected && /* @__PURE__ */
|
|
3507
|
+
isSelected && /* @__PURE__ */ jsxs8(Text9, { color: "#808080", children: [
|
|
2128
3508
|
" \u2014 ",
|
|
2129
3509
|
id
|
|
2130
3510
|
] })
|
|
2131
3511
|
] }, id);
|
|
2132
3512
|
}),
|
|
2133
|
-
/* @__PURE__ */
|
|
3513
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text9, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
|
|
2134
3514
|
] }),
|
|
2135
|
-
/* @__PURE__ */
|
|
2136
|
-
] }) : /* @__PURE__ */
|
|
2137
|
-
/* @__PURE__ */
|
|
2138
|
-
/* @__PURE__ */
|
|
2139
|
-
/* @__PURE__ */
|
|
2140
|
-
/* @__PURE__ */
|
|
3515
|
+
/* @__PURE__ */ jsx8(Text9, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
|
|
3516
|
+
] }) : /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
3517
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text9, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
|
|
3518
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3519
|
+
/* @__PURE__ */ jsx8(Box8, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text9, { bold: true, color: "#00ff41", children: "\u26A1" }) }),
|
|
3520
|
+
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, children: !input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? /* @__PURE__ */ jsx8(Text9, { children: idlePlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx8(Text9, { color: gradientColors[i] ?? void 0, children: ch }, i)) }) : !input && isStreaming && streamingPlaceholder && streamingGradientColors.length > 0 ? /* @__PURE__ */ jsx8(Text9, { children: streamingPlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx8(Text9, { color: streamingGradientColors[i] ?? void 0, children: ch }, i)) }) : /* @__PURE__ */ jsx8(
|
|
2141
3521
|
TextInput,
|
|
2142
3522
|
{
|
|
2143
3523
|
value: input,
|
|
2144
3524
|
onChange: setInput,
|
|
2145
3525
|
onSubmit: handleSubmit,
|
|
2146
3526
|
placeholder: ""
|
|
2147
|
-
}
|
|
3527
|
+
},
|
|
3528
|
+
inputKey
|
|
2148
3529
|
) })
|
|
2149
3530
|
] }),
|
|
2150
|
-
/* @__PURE__ */
|
|
3531
|
+
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text9, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
|
|
3532
|
+
/* @__PURE__ */ jsx8(SkillSelector, { skills, input, selectedIndex: skillSelectIndex }),
|
|
3533
|
+
/* @__PURE__ */ jsx8(FileSelector, { files, input, selectedIndex: fileSelectIndex })
|
|
2151
3534
|
] }),
|
|
2152
|
-
doubleCtrlC && !isStreaming && /* @__PURE__ */
|
|
2153
|
-
isStreaming && /* @__PURE__ */
|
|
3535
|
+
doubleCtrlC && !isStreaming && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text9, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) }),
|
|
3536
|
+
isStreaming && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text9, { color: "yellow", dimColor: true, children: " \u63D0\u793A\uFF1A\u6309 Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42" }) })
|
|
2154
3537
|
] });
|
|
2155
3538
|
}
|
|
2156
3539
|
|
|
2157
3540
|
// src/ui/GamePicker.tsx
|
|
2158
|
-
import { Box as
|
|
3541
|
+
import { Box as Box9, Text as Text10, useInput as useInput2 } from "ink";
|
|
2159
3542
|
import { useState as useState4, useCallback as useCallback3 } from "react";
|
|
2160
|
-
import { jsx as
|
|
3543
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2161
3544
|
function GamePicker({ games, onSelect, onExit, onBackToChat }) {
|
|
2162
3545
|
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
2163
3546
|
const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));
|
|
@@ -2185,18 +3568,18 @@ function GamePicker({ games, onSelect, onExit, onBackToChat }) {
|
|
|
2185
3568
|
[games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC]
|
|
2186
3569
|
)
|
|
2187
3570
|
);
|
|
2188
|
-
return /* @__PURE__ */
|
|
2189
|
-
/* @__PURE__ */
|
|
2190
|
-
/* @__PURE__ */
|
|
3571
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
3572
|
+
/* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#00ffff", children: "\u{1F3AE} \u6E38\u620F\u5217\u8868" }) }),
|
|
3573
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", children: games.map((game, index) => {
|
|
2191
3574
|
const isSelected = index === selectedIndex;
|
|
2192
|
-
return /* @__PURE__ */
|
|
2193
|
-
/* @__PURE__ */
|
|
2194
|
-
/* @__PURE__ */
|
|
2195
|
-
/* @__PURE__ */
|
|
3575
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", children: [
|
|
3576
|
+
/* @__PURE__ */ jsx9(Box9, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#00ff41", children: "\u25B8 " }) : /* @__PURE__ */ jsx9(Text10, { children: " " }) }),
|
|
3577
|
+
/* @__PURE__ */ jsx9(Box9, { width: 20, flexShrink: 0, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: isSelected ? "#00ff41" : "#ffffff", children: game.name }) }),
|
|
3578
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { color: "#888888", children: game.description }) })
|
|
2196
3579
|
] }, game.id);
|
|
2197
3580
|
}) }),
|
|
2198
|
-
/* @__PURE__ */
|
|
2199
|
-
doubleCtrlC && /* @__PURE__ */
|
|
3581
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) }),
|
|
3582
|
+
doubleCtrlC && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text10, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
|
|
2200
3583
|
] });
|
|
2201
3584
|
}
|
|
2202
3585
|
|
|
@@ -2213,9 +3596,9 @@ function listGames() {
|
|
|
2213
3596
|
}
|
|
2214
3597
|
|
|
2215
3598
|
// src/game/brick-breaker/index.tsx
|
|
2216
|
-
import { Box as
|
|
3599
|
+
import { Box as Box10, Text as Text11, useInput as useInput3, render as render2 } from "ink";
|
|
2217
3600
|
import { useState as useState5, useEffect as useEffect4, useRef as useRef3, useCallback as useCallback4 } from "react";
|
|
2218
|
-
import { jsx as
|
|
3601
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2219
3602
|
var GAME_WIDTH = 40;
|
|
2220
3603
|
var GAME_HEIGHT = 18;
|
|
2221
3604
|
var PADDLE_WIDTH = 9;
|
|
@@ -2405,49 +3788,49 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
2405
3788
|
const board = buildBoard(s);
|
|
2406
3789
|
const def = getLevel(s.level);
|
|
2407
3790
|
void tick;
|
|
2408
|
-
return /* @__PURE__ */
|
|
2409
|
-
/* @__PURE__ */
|
|
2410
|
-
/* @__PURE__ */
|
|
3791
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
3792
|
+
/* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
|
|
3793
|
+
/* @__PURE__ */ jsx10(Box10, { width: 20, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2411
3794
|
"\u5173\u5361 ",
|
|
2412
3795
|
s.level,
|
|
2413
3796
|
": ",
|
|
2414
|
-
/* @__PURE__ */
|
|
3797
|
+
/* @__PURE__ */ jsx10(Text11, { color: "cyan", children: def.desc })
|
|
2415
3798
|
] }) }),
|
|
2416
|
-
/* @__PURE__ */
|
|
3799
|
+
/* @__PURE__ */ jsx10(Box10, { width: 12, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2417
3800
|
"\u5206\u6570: ",
|
|
2418
|
-
/* @__PURE__ */
|
|
3801
|
+
/* @__PURE__ */ jsx10(Text11, { color: "yellow", children: String(s.score).padStart(3, "0") })
|
|
2419
3802
|
] }) }),
|
|
2420
|
-
/* @__PURE__ */
|
|
3803
|
+
/* @__PURE__ */ jsx10(Box10, { width: 12, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2421
3804
|
"\u751F\u547D: ",
|
|
2422
|
-
/* @__PURE__ */
|
|
3805
|
+
/* @__PURE__ */ jsx10(Text11, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) })
|
|
2423
3806
|
] }) }),
|
|
2424
|
-
/* @__PURE__ */
|
|
3807
|
+
/* @__PURE__ */ jsx10(Box10, { width: 10, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2425
3808
|
"\u7816\u5757: ",
|
|
2426
|
-
/* @__PURE__ */
|
|
3809
|
+
/* @__PURE__ */ jsx10(Text11, { color: "cyan", children: aliveCount })
|
|
2427
3810
|
] }) }),
|
|
2428
|
-
/* @__PURE__ */
|
|
3811
|
+
/* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsxs10(Text11, { color: s.paused ? "gray" : "green", children: [
|
|
2429
3812
|
"[",
|
|
2430
3813
|
s.paused ? "\u6682\u505C" : "\u8FD0\u884C\u4E2D",
|
|
2431
3814
|
"]"
|
|
2432
3815
|
] }) })
|
|
2433
3816
|
] }),
|
|
2434
|
-
/* @__PURE__ */
|
|
2435
|
-
/* @__PURE__ */
|
|
3817
|
+
/* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
3818
|
+
/* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2436
3819
|
"\u250C",
|
|
2437
3820
|
"\u2500".repeat(GAME_WIDTH),
|
|
2438
3821
|
"\u2510"
|
|
2439
3822
|
] }),
|
|
2440
|
-
/* @__PURE__ */
|
|
2441
|
-
/* @__PURE__ */
|
|
3823
|
+
/* @__PURE__ */ jsx10(Text11, { children: selectingLevel ? " \x1B[90m\u9009\u62E9\u5173\u5361\u540E\u5F00\u59CB...\x1B[0m" : board }),
|
|
3824
|
+
/* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2442
3825
|
"\u2514",
|
|
2443
3826
|
"\u2500".repeat(GAME_WIDTH),
|
|
2444
3827
|
"\u2518"
|
|
2445
3828
|
] })
|
|
2446
3829
|
] }),
|
|
2447
|
-
selectingLevel && /* @__PURE__ */
|
|
2448
|
-
/* @__PURE__ */
|
|
2449
|
-
/* @__PURE__ */
|
|
2450
|
-
/* @__PURE__ */
|
|
3830
|
+
selectingLevel && /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
|
|
3831
|
+
/* @__PURE__ */ jsx10(Text11, { bold: true, color: "yellow", children: "\u9009\u62E9\u5173\u5361" }),
|
|
3832
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "row", flexWrap: "wrap", children: LEVELS.map((lv, i) => /* @__PURE__ */ jsx10(Box10, { width: 22, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
3833
|
+
/* @__PURE__ */ jsx10(Text11, { color: initialLevel === i + 1 ? "green" : "white", children: i + 1 === 10 ? "0" : String(i + 1) }),
|
|
2451
3834
|
". ",
|
|
2452
3835
|
lv.desc,
|
|
2453
3836
|
" (",
|
|
@@ -2456,16 +3839,16 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
2456
3839
|
lv.cols,
|
|
2457
3840
|
")"
|
|
2458
3841
|
] }) }, i)) }),
|
|
2459
|
-
/* @__PURE__ */
|
|
3842
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text11, { dimColor: true, children: "\u6309\u6570\u5B57\u9009\u5173 q \u9000\u51FA" }) })
|
|
2460
3843
|
] }),
|
|
2461
|
-
!selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */
|
|
2462
|
-
/* @__PURE__ */
|
|
2463
|
-
/* @__PURE__ */
|
|
3844
|
+
!selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
|
|
3845
|
+
/* @__PURE__ */ jsx10(Text11, { bold: true, color: s.gameOver ? "red" : "green", children: s.gameOver ? "\u6E38\u620F\u7ED3\u675F\uFF01" : "\u606D\u559C\u901A\u5173\uFF01" }),
|
|
3846
|
+
/* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2464
3847
|
" \u5206\u6570: ",
|
|
2465
|
-
/* @__PURE__ */
|
|
3848
|
+
/* @__PURE__ */ jsx10(Text11, { color: "yellow", children: s.score })
|
|
2466
3849
|
] })
|
|
2467
3850
|
] }),
|
|
2468
|
-
!selectingLevel && /* @__PURE__ */
|
|
3851
|
+
!selectingLevel && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: s.gameOver || s.win ? /* @__PURE__ */ jsx10(Text11, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 r \u91CD\u5F00 l \u9009\u5173 q \u9000\u51FA" }) : /* @__PURE__ */ jsx10(Text11, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 p \u6682\u505C q \u9000\u51FA" }) })
|
|
2469
3852
|
] });
|
|
2470
3853
|
}
|
|
2471
3854
|
var brick_breaker_default = {
|
|
@@ -2473,11 +3856,11 @@ var brick_breaker_default = {
|
|
|
2473
3856
|
name: "Brick Breaker",
|
|
2474
3857
|
description: "\u7ECF\u5178\u6253\u7816\u5757\u6E38\u620F\uFF0C10 \u4E2A\u5173\u5361\u53EF\u9009\uFF01",
|
|
2475
3858
|
play: async () => {
|
|
2476
|
-
await new Promise((
|
|
3859
|
+
await new Promise((resolve2) => {
|
|
2477
3860
|
const { unmount } = render2(
|
|
2478
|
-
/* @__PURE__ */
|
|
3861
|
+
/* @__PURE__ */ jsx10(BrickBreakerGame, { onExit: () => {
|
|
2479
3862
|
unmount();
|
|
2480
|
-
|
|
3863
|
+
resolve2();
|
|
2481
3864
|
} })
|
|
2482
3865
|
);
|
|
2483
3866
|
});
|
|
@@ -2485,9 +3868,9 @@ var brick_breaker_default = {
|
|
|
2485
3868
|
};
|
|
2486
3869
|
|
|
2487
3870
|
// src/game/coder-check/index.tsx
|
|
2488
|
-
import { Box as
|
|
3871
|
+
import { Box as Box11, Text as Text12, useInput as useInput4, render as render3 } from "ink";
|
|
2489
3872
|
import { useState as useState6, useEffect as useEffect5, useRef as useRef4, useCallback as useCallback5 } from "react";
|
|
2490
|
-
import { jsx as
|
|
3873
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2491
3874
|
var GAME_W = 66;
|
|
2492
3875
|
var GAME_H = 20;
|
|
2493
3876
|
var SCORE_H = 6;
|
|
@@ -2958,44 +4341,44 @@ function CoderCheck({ onExit: _onExit }) {
|
|
|
2958
4341
|
const scoreLines = buildScoreLines(scoreStr);
|
|
2959
4342
|
const view = buildGameView(s, scoreLines, scoreColor, s.message);
|
|
2960
4343
|
void tick;
|
|
2961
|
-
return /* @__PURE__ */
|
|
2962
|
-
/* @__PURE__ */
|
|
4344
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
|
|
4345
|
+
/* @__PURE__ */ jsx11(Box11, { flexDirection: "row", children: /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2963
4346
|
"\u751F\u547D ",
|
|
2964
|
-
/* @__PURE__ */
|
|
4347
|
+
/* @__PURE__ */ jsx11(Text12, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) }),
|
|
2965
4348
|
" ",
|
|
2966
4349
|
"\u901F\u5EA6 ",
|
|
2967
|
-
/* @__PURE__ */
|
|
4350
|
+
/* @__PURE__ */ jsxs11(Text12, { color: "cyan", children: [
|
|
2968
4351
|
"Lv.",
|
|
2969
4352
|
Math.floor(s.speed * 10)
|
|
2970
4353
|
] })
|
|
2971
4354
|
] }) }),
|
|
2972
|
-
!s.gameOver && s.target && /* @__PURE__ */
|
|
4355
|
+
!s.gameOver && s.target && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2973
4356
|
"\u6253\u5B57: ",
|
|
2974
|
-
/* @__PURE__ */
|
|
2975
|
-
/* @__PURE__ */
|
|
4357
|
+
/* @__PURE__ */ jsx11(Text12, { color: "green", children: s.typed }),
|
|
4358
|
+
/* @__PURE__ */ jsx11(Text12, { color: "white", children: s.target.slice(s.typed.length) })
|
|
2976
4359
|
] }) }),
|
|
2977
|
-
/* @__PURE__ */
|
|
2978
|
-
/* @__PURE__ */
|
|
4360
|
+
/* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
4361
|
+
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2979
4362
|
"\u250C",
|
|
2980
4363
|
"\u2500".repeat(GAME_W),
|
|
2981
4364
|
"\u2510"
|
|
2982
4365
|
] }),
|
|
2983
|
-
view.map((row, i) => /* @__PURE__ */
|
|
2984
|
-
/* @__PURE__ */
|
|
4366
|
+
view.map((row, i) => /* @__PURE__ */ jsx11(Text12, { children: `\u2502${row}\u2502` }, i)),
|
|
4367
|
+
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2985
4368
|
"\u2514",
|
|
2986
4369
|
"\u2500".repeat(GAME_W),
|
|
2987
4370
|
"\u2518"
|
|
2988
4371
|
] })
|
|
2989
4372
|
] }),
|
|
2990
|
-
s.gameOver && /* @__PURE__ */
|
|
2991
|
-
/* @__PURE__ */
|
|
2992
|
-
/* @__PURE__ */
|
|
4373
|
+
s.gameOver && /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
|
|
4374
|
+
/* @__PURE__ */ jsx11(Text12, { bold: true, color: "red", children: "\u6E38\u620F\u7ED3\u675F\uFF01" }),
|
|
4375
|
+
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2993
4376
|
" \u5F97\u5206: ",
|
|
2994
|
-
/* @__PURE__ */
|
|
4377
|
+
/* @__PURE__ */ jsx11(Text12, { color: "yellow", children: s.score }),
|
|
2995
4378
|
" r \u91CD\u5F00 q \u9000\u51FA"
|
|
2996
4379
|
] })
|
|
2997
4380
|
] }),
|
|
2998
|
-
/* @__PURE__ */
|
|
4381
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { dimColor: true, children: "\u6253\u5B57\u6D88\u9664\u5355\u8BCD \u7A7A\u683C/Ctrl+P\u6682\u505C Ctrl+Q\u9000\u51FA" }) })
|
|
2999
4382
|
] });
|
|
3000
4383
|
}
|
|
3001
4384
|
var coder_check_default = {
|
|
@@ -3003,11 +4386,11 @@ var coder_check_default = {
|
|
|
3003
4386
|
name: "Coder Check",
|
|
3004
4387
|
description: "\u6781\u901F\u6253\u5B57\u6E38\u620F\uFF0C\u8F93\u5165\u5355\u8BCD\u6D88\u9664\u5B83\u4EEC\uFF01",
|
|
3005
4388
|
play: async () => {
|
|
3006
|
-
await new Promise((
|
|
4389
|
+
await new Promise((resolve2) => {
|
|
3007
4390
|
const { unmount } = render3(
|
|
3008
|
-
/* @__PURE__ */
|
|
4391
|
+
/* @__PURE__ */ jsx11(CoderCheck, { onExit: () => {
|
|
3009
4392
|
unmount();
|
|
3010
|
-
|
|
4393
|
+
resolve2();
|
|
3011
4394
|
} })
|
|
3012
4395
|
);
|
|
3013
4396
|
});
|
|
@@ -3026,10 +4409,10 @@ import { render as render4 } from "ink";
|
|
|
3026
4409
|
import chalk4 from "chalk";
|
|
3027
4410
|
|
|
3028
4411
|
// src/stock/StockList.tsx
|
|
3029
|
-
import { Box as
|
|
4412
|
+
import { Box as Box12, Text as Text13, useInput as useInput5 } from "ink";
|
|
3030
4413
|
import { useState as useState7, useCallback as useCallback6, useEffect as useEffect6 } from "react";
|
|
3031
4414
|
import asciichart from "asciichart";
|
|
3032
|
-
import { jsx as
|
|
4415
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3033
4416
|
var MINUTE_API = "https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1";
|
|
3034
4417
|
function normalizeApiCode(code) {
|
|
3035
4418
|
if (code.startsWith("sh") || code.startsWith("sz")) return code;
|
|
@@ -3226,57 +4609,57 @@ function StockList({ codes, onExit, onBackToChat }) {
|
|
|
3226
4609
|
);
|
|
3227
4610
|
if (detailView) {
|
|
3228
4611
|
if (detailLoading) {
|
|
3229
|
-
return /* @__PURE__ */
|
|
4612
|
+
return /* @__PURE__ */ jsx12(Box12, { paddingLeft: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
|
|
3230
4613
|
}
|
|
3231
4614
|
return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown, currentTime);
|
|
3232
4615
|
}
|
|
3233
|
-
return /* @__PURE__ */
|
|
3234
|
-
/* @__PURE__ */
|
|
3235
|
-
/* @__PURE__ */
|
|
3236
|
-
/* @__PURE__ */
|
|
4616
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
4617
|
+
/* @__PURE__ */ jsxs12(Box12, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
4618
|
+
/* @__PURE__ */ jsx12(Text13, { bold: true, color: "#00ffff", children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
|
|
4619
|
+
/* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
3237
4620
|
" \u{1F550} ",
|
|
3238
4621
|
currentTime
|
|
3239
4622
|
] }),
|
|
3240
|
-
/* @__PURE__ */
|
|
4623
|
+
/* @__PURE__ */ jsx12(Text13, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` ${countdown}s \u540E\u81EA\u52A8\u5237\u65B0` })
|
|
3241
4624
|
] }),
|
|
3242
|
-
/* @__PURE__ */
|
|
3243
|
-
/* @__PURE__ */
|
|
3244
|
-
/* @__PURE__ */
|
|
3245
|
-
/* @__PURE__ */
|
|
3246
|
-
/* @__PURE__ */
|
|
3247
|
-
/* @__PURE__ */
|
|
3248
|
-
/* @__PURE__ */
|
|
3249
|
-
/* @__PURE__ */
|
|
3250
|
-
/* @__PURE__ */
|
|
3251
|
-
/* @__PURE__ */
|
|
4625
|
+
/* @__PURE__ */ jsxs12(Box12, { children: [
|
|
4626
|
+
/* @__PURE__ */ jsx12(Box12, { width: 3 }),
|
|
4627
|
+
/* @__PURE__ */ jsx12(Box12, { width: 9, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u4EE3\u7801" }) }),
|
|
4628
|
+
/* @__PURE__ */ jsx12(Box12, { width: 16, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u540D\u79F0" }) }),
|
|
4629
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
|
|
4630
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u6DA8\u8DCC\u5E45" }) }),
|
|
4631
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
|
|
4632
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u6700\u9AD8" }) }),
|
|
4633
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u6700\u4F4E" }) }),
|
|
4634
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u6210\u4EA4\u989D" }) })
|
|
3252
4635
|
] }),
|
|
3253
|
-
/* @__PURE__ */
|
|
3254
|
-
/* @__PURE__ */
|
|
4636
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
|
|
4637
|
+
/* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: stocks.map((stock, index) => {
|
|
3255
4638
|
const isSelected = index === selectedIndex;
|
|
3256
4639
|
const isUp = stock.changePercent >= 0;
|
|
3257
4640
|
const color = isUp ? "#ff1493" : "#00ff41";
|
|
3258
|
-
return /* @__PURE__ */
|
|
3259
|
-
/* @__PURE__ */
|
|
3260
|
-
/* @__PURE__ */
|
|
3261
|
-
/* @__PURE__ */
|
|
3262
|
-
/* @__PURE__ */
|
|
3263
|
-
/* @__PURE__ */
|
|
4641
|
+
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
4642
|
+
/* @__PURE__ */ jsx12(Box12, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx12(Text13, { bold: true, color: "#00ffff", children: "\u25B8 " }) : /* @__PURE__ */ jsx12(Text13, { children: " " }) }),
|
|
4643
|
+
/* @__PURE__ */ jsx12(Box12, { width: 9, children: /* @__PURE__ */ jsx12(Text13, { bold: true, color: isSelected ? "#00ffff" : "#ffffff", children: stock.code }) }),
|
|
4644
|
+
/* @__PURE__ */ jsx12(Box12, { width: 16, children: /* @__PURE__ */ jsx12(Text13, { color: isSelected ? "#ffffff" : "#cccccc", children: stock.name }) }),
|
|
4645
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { bold: true, color, children: formatPrice(stock.price) }) }),
|
|
4646
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { color, children: [
|
|
3264
4647
|
isUp ? "+" : "",
|
|
3265
4648
|
stock.changePercent.toFixed(2),
|
|
3266
4649
|
"%"
|
|
3267
4650
|
] }) }),
|
|
3268
|
-
/* @__PURE__ */
|
|
4651
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { color, children: [
|
|
3269
4652
|
isUp ? "+" : "",
|
|
3270
4653
|
stock.changeAmount.toFixed(3)
|
|
3271
4654
|
] }) }),
|
|
3272
|
-
/* @__PURE__ */
|
|
3273
|
-
/* @__PURE__ */
|
|
3274
|
-
/* @__PURE__ */
|
|
4655
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { color: "#cccccc", children: formatPrice(stock.high) }) }),
|
|
4656
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { color: "#888888", children: formatPrice(stock.low) }) }),
|
|
4657
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text13, { color: "#888888", children: formatAmount(stock.amount) }) })
|
|
3275
4658
|
] }, stock.code);
|
|
3276
4659
|
}) }),
|
|
3277
|
-
/* @__PURE__ */
|
|
3278
|
-
/* @__PURE__ */
|
|
3279
|
-
doubleCtrlC && /* @__PURE__ */
|
|
4660
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 q \u8FD4\u56DE` }) }),
|
|
4661
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ~/.dskcode/settings.json` }) }),
|
|
4662
|
+
doubleCtrlC && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
|
|
3280
4663
|
] });
|
|
3281
4664
|
}
|
|
3282
4665
|
function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
@@ -3294,33 +4677,33 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
|
3294
4677
|
raw = raw.replaceAll("\u256D", "\u250C").replaceAll("\u256E", "\u2510").replaceAll("\u2570", "\u2514").replaceAll("\u256F", "\u2518");
|
|
3295
4678
|
chartLines = raw.split("\n");
|
|
3296
4679
|
}
|
|
3297
|
-
return /* @__PURE__ */
|
|
3298
|
-
/* @__PURE__ */
|
|
3299
|
-
/* @__PURE__ */
|
|
3300
|
-
/* @__PURE__ */
|
|
4680
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingLeft: 1, children: [
|
|
4681
|
+
/* @__PURE__ */ jsxs12(Box12, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
4682
|
+
/* @__PURE__ */ jsxs12(Box12, { children: [
|
|
4683
|
+
/* @__PURE__ */ jsxs12(Text13, { bold: true, color: "#00ffff", children: [
|
|
3301
4684
|
" \u{1F4CA} ",
|
|
3302
4685
|
stock.name,
|
|
3303
4686
|
" "
|
|
3304
4687
|
] }),
|
|
3305
|
-
/* @__PURE__ */
|
|
3306
|
-
currentTime && /* @__PURE__ */
|
|
4688
|
+
/* @__PURE__ */ jsx12(Text13, { dimColor: true, children: stock.code }),
|
|
4689
|
+
currentTime && /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
3307
4690
|
" \u{1F550} ",
|
|
3308
4691
|
currentTime
|
|
3309
4692
|
] })
|
|
3310
4693
|
] }),
|
|
3311
|
-
/* @__PURE__ */
|
|
4694
|
+
/* @__PURE__ */ jsx12(Text13, { dimColor: true, children: `${countdown}s \u540E\u5237\u65B0` })
|
|
3312
4695
|
] }),
|
|
3313
|
-
/* @__PURE__ */
|
|
3314
|
-
/* @__PURE__ */
|
|
3315
|
-
/* @__PURE__ */
|
|
4696
|
+
/* @__PURE__ */ jsxs12(Box12, { children: [
|
|
4697
|
+
/* @__PURE__ */ jsx12(Box12, { width: 16, children: /* @__PURE__ */ jsx12(Text13, { bold: true, color: "#888888", children: "\u5F53\u524D\u4EF7" }) }),
|
|
4698
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { bold: true, color: colorCode, children: [
|
|
3316
4699
|
arrow,
|
|
3317
4700
|
" ",
|
|
3318
4701
|
formatPrice(stock.price)
|
|
3319
4702
|
] }) })
|
|
3320
4703
|
] }),
|
|
3321
|
-
/* @__PURE__ */
|
|
3322
|
-
/* @__PURE__ */
|
|
3323
|
-
/* @__PURE__ */
|
|
4704
|
+
/* @__PURE__ */ jsxs12(Box12, { children: [
|
|
4705
|
+
/* @__PURE__ */ jsx12(Box12, { width: 16, children: /* @__PURE__ */ jsx12(Text13, { color: "#888888", children: "\u6DA8\u8DCC\u5E45" }) }),
|
|
4706
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: colorCode, children: [
|
|
3324
4707
|
isUp ? "+" : "",
|
|
3325
4708
|
stock.changePercent.toFixed(2),
|
|
3326
4709
|
"%",
|
|
@@ -3329,13 +4712,93 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
|
3329
4712
|
stock.changeAmount.toFixed(3)
|
|
3330
4713
|
] }) })
|
|
3331
4714
|
] }),
|
|
3332
|
-
chartLines.length > 0 && /* @__PURE__ */
|
|
3333
|
-
/* @__PURE__ */
|
|
4715
|
+
chartLines.length > 0 && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, flexDirection: "column", children: chartLines.map((line, i) => /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text13, { color: colorCode, children: line || " " }) }, i)) }),
|
|
4716
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: " Space/q \u8FD4\u56DE\u5217\u8868" }) })
|
|
3334
4717
|
] });
|
|
3335
4718
|
}
|
|
3336
4719
|
|
|
4720
|
+
// src/utils/scan-files.ts
|
|
4721
|
+
import { readdir as readdir5 } from "fs/promises";
|
|
4722
|
+
import { join as join7, relative as relative4 } from "path";
|
|
4723
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
4724
|
+
"node_modules",
|
|
4725
|
+
".git",
|
|
4726
|
+
".svn",
|
|
4727
|
+
".hg",
|
|
4728
|
+
".dskcode",
|
|
4729
|
+
".claude",
|
|
4730
|
+
"dist",
|
|
4731
|
+
"build",
|
|
4732
|
+
".next",
|
|
4733
|
+
".turbo",
|
|
4734
|
+
".nx",
|
|
4735
|
+
"coverage",
|
|
4736
|
+
".cache",
|
|
4737
|
+
".nyc_output",
|
|
4738
|
+
".vscode",
|
|
4739
|
+
".idea",
|
|
4740
|
+
"__pycache__",
|
|
4741
|
+
".venv",
|
|
4742
|
+
"venv",
|
|
4743
|
+
".tox",
|
|
4744
|
+
"target",
|
|
4745
|
+
"vendor",
|
|
4746
|
+
"bower_components",
|
|
4747
|
+
"jspm_packages"
|
|
4748
|
+
]);
|
|
4749
|
+
var SOURCE_EXTS = /* @__PURE__ */ new Set([
|
|
4750
|
+
".ts",
|
|
4751
|
+
".tsx",
|
|
4752
|
+
".js",
|
|
4753
|
+
".jsx",
|
|
4754
|
+
".mjs",
|
|
4755
|
+
".cjs",
|
|
4756
|
+
".vue",
|
|
4757
|
+
".svelte",
|
|
4758
|
+
".astro",
|
|
4759
|
+
".css",
|
|
4760
|
+
".scss",
|
|
4761
|
+
".less",
|
|
4762
|
+
".html",
|
|
4763
|
+
".json",
|
|
4764
|
+
".md",
|
|
4765
|
+
".yaml",
|
|
4766
|
+
".yml",
|
|
4767
|
+
".toml"
|
|
4768
|
+
]);
|
|
4769
|
+
async function scanProjectFiles(baseDir, dir) {
|
|
4770
|
+
const currentDir = dir ?? baseDir;
|
|
4771
|
+
let entries;
|
|
4772
|
+
try {
|
|
4773
|
+
entries = await readdir5(currentDir, { withFileTypes: true });
|
|
4774
|
+
} catch {
|
|
4775
|
+
return [];
|
|
4776
|
+
}
|
|
4777
|
+
const files = [];
|
|
4778
|
+
const dirs = [];
|
|
4779
|
+
for (const entry of entries) {
|
|
4780
|
+
const name = entry.name;
|
|
4781
|
+
if (IGNORE_DIRS.has(name) || name.startsWith(".")) continue;
|
|
4782
|
+
if (entry.isDirectory()) {
|
|
4783
|
+
dirs.push(name);
|
|
4784
|
+
} else if (entry.isFile()) {
|
|
4785
|
+
const ext = name.slice(name.lastIndexOf(".")).toLowerCase();
|
|
4786
|
+
if (SOURCE_EXTS.has(ext)) {
|
|
4787
|
+
files.push(relative4(baseDir, join7(currentDir, name)));
|
|
4788
|
+
}
|
|
4789
|
+
}
|
|
4790
|
+
}
|
|
4791
|
+
const nested = await Promise.all(
|
|
4792
|
+
dirs.map((d) => scanProjectFiles(baseDir, join7(currentDir, d)))
|
|
4793
|
+
);
|
|
4794
|
+
for (const n of nested) {
|
|
4795
|
+
files.push(...n);
|
|
4796
|
+
}
|
|
4797
|
+
return files;
|
|
4798
|
+
}
|
|
4799
|
+
|
|
3337
4800
|
// src/cli/index.tsx
|
|
3338
|
-
import { jsx as
|
|
4801
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
3339
4802
|
var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion", "game", "stock"];
|
|
3340
4803
|
function createCli() {
|
|
3341
4804
|
const program2 = new Command();
|
|
@@ -3369,9 +4832,11 @@ function createCli() {
|
|
|
3369
4832
|
startChat(ctx, costTracker);
|
|
3370
4833
|
});
|
|
3371
4834
|
async function startChat(ctx, costTracker) {
|
|
3372
|
-
const [globalSkillCount, localSkillCount] = await Promise.all([
|
|
4835
|
+
const [globalSkillCount, localSkillCount, skills, files] = await Promise.all([
|
|
3373
4836
|
countDskcodeSkills(),
|
|
3374
|
-
countProjectLocalSkills(process.cwd())
|
|
4837
|
+
countProjectLocalSkills(process.cwd()),
|
|
4838
|
+
getAllSkills(process.cwd()),
|
|
4839
|
+
scanProjectFiles(process.cwd())
|
|
3375
4840
|
]);
|
|
3376
4841
|
const skillCount = globalSkillCount + localSkillCount;
|
|
3377
4842
|
const defaultProvider = ctx?.config.providers.find(
|
|
@@ -3379,10 +4844,12 @@ function createCli() {
|
|
|
3379
4844
|
);
|
|
3380
4845
|
const model = defaultProvider?.model ?? "deepseek-v4-flash";
|
|
3381
4846
|
const chatApp = renderApp(
|
|
3382
|
-
/* @__PURE__ */
|
|
4847
|
+
/* @__PURE__ */ jsx13(
|
|
3383
4848
|
ChatSession,
|
|
3384
4849
|
{
|
|
3385
4850
|
skillCount,
|
|
4851
|
+
skills,
|
|
4852
|
+
files,
|
|
3386
4853
|
toolCount: ctx?.config.tools.length ?? 0,
|
|
3387
4854
|
verbose: ctx?.verbose ?? false,
|
|
3388
4855
|
apiKey: defaultProvider?.apiKey,
|
|
@@ -3395,7 +4862,7 @@ function createCli() {
|
|
|
3395
4862
|
initGames();
|
|
3396
4863
|
const games = listGames();
|
|
3397
4864
|
const { unmount } = render4(
|
|
3398
|
-
/* @__PURE__ */
|
|
4865
|
+
/* @__PURE__ */ jsx13(
|
|
3399
4866
|
GamePicker,
|
|
3400
4867
|
{
|
|
3401
4868
|
games,
|
|
@@ -3419,7 +4886,7 @@ function createCli() {
|
|
|
3419
4886
|
setImmediate(() => {
|
|
3420
4887
|
const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
3421
4888
|
const stockApp = renderApp(
|
|
3422
|
-
/* @__PURE__ */
|
|
4889
|
+
/* @__PURE__ */ jsx13(
|
|
3423
4890
|
StockList,
|
|
3424
4891
|
{
|
|
3425
4892
|
codes: defaultStockCodes,
|
|
@@ -3483,10 +4950,10 @@ compdef _dskcode_completion dskcode`);
|
|
|
3483
4950
|
program2.command("stock").description("\u67E5\u770B\u81EA\u9009\u80A1\u5B9E\u65F6\u884C\u60C5").argument("[codes...]", "\u80A1\u7968\u4EE3\u7801\uFF08\u7A7A\u683C\u5206\u9694\uFF09\uFF0C\u5982 513090 600519").action(async function(codes) {
|
|
3484
4951
|
const ctx = this.dskcodeCtx;
|
|
3485
4952
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
3486
|
-
const globalConfigPath =
|
|
4953
|
+
const globalConfigPath = join8(home, ".dskcode", "settings.json");
|
|
3487
4954
|
let globalConfigHasStock = false;
|
|
3488
4955
|
try {
|
|
3489
|
-
const raw = await
|
|
4956
|
+
const raw = await readFile8(globalConfigPath, "utf-8");
|
|
3490
4957
|
const parsed = JSON.parse(raw);
|
|
3491
4958
|
const stock = parsed.stock;
|
|
3492
4959
|
globalConfigHasStock = Array.isArray(stock?.symbols) && stock.symbols.length > 0;
|
|
@@ -3506,7 +4973,7 @@ compdef _dskcode_completion dskcode`);
|
|
|
3506
4973
|
const freshResult = await loadAndValidate();
|
|
3507
4974
|
const codeList = codes && codes.length > 0 ? codes : freshResult.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
3508
4975
|
const app = renderApp(
|
|
3509
|
-
/* @__PURE__ */
|
|
4976
|
+
/* @__PURE__ */ jsx13(
|
|
3510
4977
|
StockList,
|
|
3511
4978
|
{
|
|
3512
4979
|
codes: codeList,
|
|
@@ -3533,19 +5000,19 @@ compdef _dskcode_completion dskcode`);
|
|
|
3533
5000
|
console.log("\u6682\u65E0\u53EF\u7528\u6E38\u620F\u3002");
|
|
3534
5001
|
return;
|
|
3535
5002
|
}
|
|
3536
|
-
const selectedGame = await new Promise((
|
|
5003
|
+
const selectedGame = await new Promise((resolve2) => {
|
|
3537
5004
|
const { unmount } = render4(
|
|
3538
|
-
/* @__PURE__ */
|
|
5005
|
+
/* @__PURE__ */ jsx13(
|
|
3539
5006
|
GamePicker,
|
|
3540
5007
|
{
|
|
3541
5008
|
games,
|
|
3542
5009
|
onSelect: (game) => {
|
|
3543
5010
|
unmount();
|
|
3544
|
-
|
|
5011
|
+
resolve2(game);
|
|
3545
5012
|
},
|
|
3546
5013
|
onExit: () => {
|
|
3547
5014
|
unmount();
|
|
3548
|
-
|
|
5015
|
+
resolve2(null);
|
|
3549
5016
|
}
|
|
3550
5017
|
}
|
|
3551
5018
|
),
|