dskcode 0.1.16 → 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 +1443 -274
- 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
|
});
|
|
@@ -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
|
}
|
|
@@ -773,8 +773,8 @@ async function getAllSkills(cwd) {
|
|
|
773
773
|
}
|
|
774
774
|
|
|
775
775
|
// src/cli/index.tsx
|
|
776
|
-
import { readFile as
|
|
777
|
-
import { join as
|
|
776
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
777
|
+
import { join as join8 } from "path";
|
|
778
778
|
|
|
779
779
|
// src/ui/RenderScope.tsx
|
|
780
780
|
import { render } from "ink";
|
|
@@ -816,7 +816,7 @@ var LOGO_LINES = [
|
|
|
816
816
|
];
|
|
817
817
|
|
|
818
818
|
// src/ui/ChatSession.tsx
|
|
819
|
-
import { Box as
|
|
819
|
+
import { Box as Box8, Text as Text9, useInput, Static } from "ink";
|
|
820
820
|
import TextInput from "ink-text-input";
|
|
821
821
|
import { useEffect as useEffect3, useState as useState3, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
822
822
|
|
|
@@ -1280,9 +1280,54 @@ function AssistantMessage({
|
|
|
1280
1280
|
] });
|
|
1281
1281
|
}
|
|
1282
1282
|
|
|
1283
|
-
// src/ui/
|
|
1283
|
+
// src/ui/DiffPreview.tsx
|
|
1284
1284
|
import { Box as Box5, Text as Text6 } from "ink";
|
|
1285
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";
|
|
1286
1331
|
var HIGHLIGHT_COLOR = "#00bfff";
|
|
1287
1332
|
function SkillSelector({ skills, input, selectedIndex }) {
|
|
1288
1333
|
const match = input.match(/(?:^|\s)\/([^/]*)$/);
|
|
@@ -1292,19 +1337,19 @@ function SkillSelector({ skills, input, selectedIndex }) {
|
|
|
1292
1337
|
const matched = !query && input.startsWith("/") ? skills.slice(0, 3) : skills.filter((s) => s.name.toLowerCase().includes(query)).slice(0, 3);
|
|
1293
1338
|
if (query && matched.length > 0 && matched.some((s) => s.name.toLowerCase() === query)) return null;
|
|
1294
1339
|
if (matched.length === 0) return null;
|
|
1295
|
-
return /* @__PURE__ */
|
|
1296
|
-
/* @__PURE__ */
|
|
1297
|
-
matched.map((skill, i) => /* @__PURE__ */
|
|
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: [
|
|
1298
1343
|
i === selectedIndex ? " \u203A " : " ",
|
|
1299
1344
|
skill.name
|
|
1300
1345
|
] }) }, skill.name)),
|
|
1301
|
-
/* @__PURE__ */
|
|
1346
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text7, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Tab \u8865\u5168" }) })
|
|
1302
1347
|
] });
|
|
1303
1348
|
}
|
|
1304
1349
|
|
|
1305
1350
|
// src/ui/FileSelector.tsx
|
|
1306
|
-
import { Box as
|
|
1307
|
-
import { jsx as
|
|
1351
|
+
import { Box as Box7, Text as Text8 } from "ink";
|
|
1352
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1308
1353
|
var HIGHLIGHT_COLOR2 = "#00ff41";
|
|
1309
1354
|
function FileSelector({ files, input, selectedIndex }) {
|
|
1310
1355
|
const match = input.match(/(?:^|\s)@([^@]*)$/);
|
|
@@ -1314,13 +1359,13 @@ function FileSelector({ files, input, selectedIndex }) {
|
|
|
1314
1359
|
const matched = !query && input.startsWith("@") ? files.slice(0, 5) : files.filter((f) => f.toLowerCase().includes(query)).slice(0, 5);
|
|
1315
1360
|
if (query && matched.some((f) => f.toLowerCase() === query)) return null;
|
|
1316
1361
|
if (matched.length === 0) return null;
|
|
1317
|
-
return /* @__PURE__ */
|
|
1318
|
-
/* @__PURE__ */
|
|
1319
|
-
matched.map((file, i) => /* @__PURE__ */
|
|
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: [
|
|
1320
1365
|
i === selectedIndex ? " \u203A " : " ",
|
|
1321
1366
|
file
|
|
1322
1367
|
] }) }, file)),
|
|
1323
|
-
/* @__PURE__ */
|
|
1368
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text8, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Tab \u8865\u5168" }) })
|
|
1324
1369
|
] });
|
|
1325
1370
|
}
|
|
1326
1371
|
|
|
@@ -1473,17 +1518,132 @@ ${opts.projectContext}`);
|
|
|
1473
1518
|
return sections.join("\n\n");
|
|
1474
1519
|
}
|
|
1475
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
|
+
|
|
1476
1631
|
// src/agent/index.ts
|
|
1477
1632
|
var Session = class {
|
|
1478
1633
|
#messages = [];
|
|
1479
1634
|
#provider;
|
|
1480
|
-
#
|
|
1635
|
+
#toolRegistry;
|
|
1481
1636
|
#costTracker;
|
|
1482
1637
|
#options;
|
|
1483
1638
|
#abortController = new AbortController();
|
|
1484
1639
|
constructor(provider, tools = [], costTracker, options) {
|
|
1485
1640
|
this.#provider = provider;
|
|
1486
|
-
|
|
1641
|
+
if (tools instanceof ToolRegistry) {
|
|
1642
|
+
this.#toolRegistry = tools;
|
|
1643
|
+
} else {
|
|
1644
|
+
this.#toolRegistry = new ToolRegistry();
|
|
1645
|
+
this.#toolRegistry.registerAll(tools);
|
|
1646
|
+
}
|
|
1487
1647
|
this.#costTracker = costTracker ?? new CostTracker();
|
|
1488
1648
|
this.#options = {
|
|
1489
1649
|
cwd: options?.cwd ?? process.cwd(),
|
|
@@ -1508,18 +1668,32 @@ var Session = class {
|
|
|
1508
1668
|
get model() {
|
|
1509
1669
|
return this.#provider.model();
|
|
1510
1670
|
}
|
|
1671
|
+
/** 获取工具注册表(只读视图) */
|
|
1672
|
+
get toolRegistry() {
|
|
1673
|
+
return this.#toolRegistry;
|
|
1674
|
+
}
|
|
1511
1675
|
// -------------------------------------------------------------------------
|
|
1512
1676
|
// 流式对话 — Agent 主循环
|
|
1513
1677
|
// -------------------------------------------------------------------------
|
|
1514
1678
|
/**
|
|
1515
|
-
*
|
|
1679
|
+
* 执行一轮用户对话,以 AsyncGenerator 形式逐步 yield 事件。
|
|
1680
|
+
*
|
|
1681
|
+
* 主循环流程:
|
|
1682
|
+
* 1. 追加用户消息
|
|
1683
|
+
* 2. 进入 Agent 循环(最多 maxToolRounds 轮)
|
|
1684
|
+
* a. 构建消息 → 裁剪 → 调用 Provider 流式接口
|
|
1685
|
+
* b. 解析响应:文本增量、工具调用、使用量
|
|
1686
|
+
* c. 如果有工具调用 → 执行工具 → 追加结果 → 继续循环
|
|
1687
|
+
* d. 如果没有工具调用 → 退出循环
|
|
1688
|
+
* 3. yield done 事件
|
|
1516
1689
|
*
|
|
1517
1690
|
* 调用方式:
|
|
1518
1691
|
* ```ts
|
|
1519
1692
|
* for await (const event of session.chat("你好")) {
|
|
1520
1693
|
* switch (event.type) {
|
|
1521
|
-
* case "text_delta":
|
|
1694
|
+
* case "text_delta": // 追加文本
|
|
1522
1695
|
* case "tool_calls": // 展示工具调用
|
|
1696
|
+
* case "tool_result": // 工具执行结果
|
|
1523
1697
|
* case "usage": // 记录使用量
|
|
1524
1698
|
* case "done": // 本轮完成
|
|
1525
1699
|
* case "error": // 处理错误
|
|
@@ -1529,75 +1703,91 @@ var Session = class {
|
|
|
1529
1703
|
*/
|
|
1530
1704
|
async *chat(userInput) {
|
|
1531
1705
|
this.#messages.push({ role: "user", content: userInput });
|
|
1532
|
-
const systemPrompt = this.#buildSystemPrompt();
|
|
1533
|
-
const [trimmed, wasTrimmed] = trimMessages(
|
|
1534
|
-
[...this.#messages],
|
|
1535
|
-
{
|
|
1536
|
-
model: this.#provider.model(),
|
|
1537
|
-
reservedForOutput: this.#options.reservedForOutput,
|
|
1538
|
-
systemPrompt,
|
|
1539
|
-
preserveRecentRounds: this.#options.preserveRecentRounds
|
|
1540
|
-
}
|
|
1541
|
-
);
|
|
1542
|
-
if (wasTrimmed) {
|
|
1543
|
-
}
|
|
1544
|
-
const apiMessages = buildApiMessages(systemPrompt, trimmed);
|
|
1545
|
-
const toolDefs = this.#buildToolDefinitions();
|
|
1546
1706
|
const startTime = Date.now();
|
|
1707
|
+
let toolRounds = 0;
|
|
1547
1708
|
try {
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
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
|
+
}
|
|
1565
1744
|
}
|
|
1566
|
-
if (
|
|
1567
|
-
|
|
1745
|
+
if (lastUsage) {
|
|
1746
|
+
const modelId = this.#provider.model();
|
|
1747
|
+
this.#costTracker.record(lastUsage, modelId);
|
|
1748
|
+
yield { type: "usage", usage: lastUsage, model: modelId };
|
|
1568
1749
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1750
|
+
const assistantMsg = {
|
|
1751
|
+
role: "assistant",
|
|
1752
|
+
content: accumulatedText
|
|
1753
|
+
};
|
|
1754
|
+
if (lastToolCalls && lastToolCalls.length > 0) {
|
|
1755
|
+
assistantMsg.toolCalls = lastToolCalls;
|
|
1571
1756
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
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;
|
|
1597
1788
|
}
|
|
1789
|
+
break;
|
|
1598
1790
|
}
|
|
1599
|
-
const elapsed = Date.now() - startTime;
|
|
1600
|
-
yield { type: "done", elapsed };
|
|
1601
1791
|
} catch (err) {
|
|
1602
1792
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
1603
1793
|
return;
|
|
@@ -1609,7 +1799,10 @@ var Session = class {
|
|
|
1609
1799
|
type: "error",
|
|
1610
1800
|
error: err instanceof Error ? err : new Error(String(err))
|
|
1611
1801
|
};
|
|
1802
|
+
return;
|
|
1612
1803
|
}
|
|
1804
|
+
const elapsed = Date.now() - startTime;
|
|
1805
|
+
yield { type: "done", elapsed };
|
|
1613
1806
|
}
|
|
1614
1807
|
// -------------------------------------------------------------------------
|
|
1615
1808
|
// 会话管理
|
|
@@ -1628,7 +1821,8 @@ var Session = class {
|
|
|
1628
1821
|
// -------------------------------------------------------------------------
|
|
1629
1822
|
/** 构建系统提示词 */
|
|
1630
1823
|
#buildSystemPrompt() {
|
|
1631
|
-
const
|
|
1824
|
+
const enabledTools = this.#toolRegistry.list();
|
|
1825
|
+
const toolDescs = enabledTools.map((t) => ({
|
|
1632
1826
|
name: t.name,
|
|
1633
1827
|
description: t.description,
|
|
1634
1828
|
parameters: t.parameters
|
|
@@ -1643,7 +1837,7 @@ var Session = class {
|
|
|
1643
1837
|
}
|
|
1644
1838
|
/** 将注册的工具转为 ToolDefinition 格式(预留给 function calling) */
|
|
1645
1839
|
#buildToolDefinitions() {
|
|
1646
|
-
return this.#
|
|
1840
|
+
return this.#toolRegistry.list().map((t) => ({
|
|
1647
1841
|
type: "function",
|
|
1648
1842
|
function: {
|
|
1649
1843
|
name: t.name,
|
|
@@ -1654,6 +1848,956 @@ var Session = class {
|
|
|
1654
1848
|
}
|
|
1655
1849
|
};
|
|
1656
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
|
+
|
|
1657
2801
|
// src/utils/gradient.ts
|
|
1658
2802
|
var IDLE_GRADIENT_STOPS = [
|
|
1659
2803
|
[0, 255, 65],
|
|
@@ -1718,7 +2862,7 @@ function getGradientColors(text, phase, stops) {
|
|
|
1718
2862
|
}
|
|
1719
2863
|
|
|
1720
2864
|
// src/ui/ChatSession.tsx
|
|
1721
|
-
import { Fragment, jsx as
|
|
2865
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1722
2866
|
var commandRegistry = /* @__PURE__ */ new Map();
|
|
1723
2867
|
function registerCommand(name, cmd) {
|
|
1724
2868
|
commandRegistry.set(name, cmd);
|
|
@@ -1983,7 +3127,9 @@ function ChatSession({
|
|
|
1983
3127
|
model: activeModel
|
|
1984
3128
|
});
|
|
1985
3129
|
const tracker = externalCostTracker ?? new CostTracker();
|
|
1986
|
-
const
|
|
3130
|
+
const toolRegistry = new ToolRegistry();
|
|
3131
|
+
toolRegistry.registerAll(builtinTools);
|
|
3132
|
+
const session = new Session(provider, toolRegistry, tracker, {
|
|
1987
3133
|
cwd: process.cwd()
|
|
1988
3134
|
});
|
|
1989
3135
|
sessionRef.current = session;
|
|
@@ -1995,7 +3141,7 @@ function ChatSession({
|
|
|
1995
3141
|
if (!apiKey || !baseUrl) return;
|
|
1996
3142
|
let cancelled = false;
|
|
1997
3143
|
setBalanceLoading(true);
|
|
1998
|
-
import("./deepseek-
|
|
3144
|
+
import("./deepseek-UJXV2D6K.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
|
|
1999
3145
|
const provider = new DeepSeekProvider2({
|
|
2000
3146
|
apiKey,
|
|
2001
3147
|
baseUrl,
|
|
@@ -2163,6 +3309,20 @@ function ChatSession({
|
|
|
2163
3309
|
return next;
|
|
2164
3310
|
});
|
|
2165
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;
|
|
2166
3326
|
case "usage":
|
|
2167
3327
|
setCurrentUsage(event.usage);
|
|
2168
3328
|
setStreamingModel(event.model);
|
|
@@ -2224,26 +3384,26 @@ function ChatSession({
|
|
|
2224
3384
|
});
|
|
2225
3385
|
}
|
|
2226
3386
|
}, [currentUsage, streamingModel, currentCost]);
|
|
2227
|
-
return /* @__PURE__ */
|
|
2228
|
-
/* @__PURE__ */
|
|
2229
|
-
/* @__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) => {
|
|
2230
3390
|
const colorIndex = (i + offset) % CYBER_PALETTE.length;
|
|
2231
|
-
return /* @__PURE__ */
|
|
3391
|
+
return /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text9, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }) }, i);
|
|
2232
3392
|
}) }),
|
|
2233
|
-
/* @__PURE__ */
|
|
2234
|
-
/* @__PURE__ */
|
|
3393
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", justifyContent: "center", children: [
|
|
3394
|
+
/* @__PURE__ */ jsxs8(Text9, { color: "#00ff41", children: [
|
|
2235
3395
|
" \u2714 ",
|
|
2236
3396
|
"\u5DF2\u5C31\u7EEA ",
|
|
2237
3397
|
skillCount,
|
|
2238
3398
|
" \u4E2A Skill"
|
|
2239
3399
|
] }),
|
|
2240
|
-
/* @__PURE__ */
|
|
3400
|
+
/* @__PURE__ */ jsxs8(Text9, { color: "#00ffff", children: [
|
|
2241
3401
|
" \u2139 ",
|
|
2242
3402
|
"\u5DF2\u5C31\u7EEA ",
|
|
2243
3403
|
toolCount,
|
|
2244
3404
|
" \u4E2A\u5DE5\u5177"
|
|
2245
3405
|
] }),
|
|
2246
|
-
/* @__PURE__ */
|
|
3406
|
+
/* @__PURE__ */ jsxs8(Text9, { color: "#00ffff", children: [
|
|
2247
3407
|
" \u{1F527} \u6A21\u578B ",
|
|
2248
3408
|
SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel
|
|
2249
3409
|
] }),
|
|
@@ -2251,40 +3411,49 @@ function ChatSession({
|
|
|
2251
3411
|
const tip = cmdTips[cmdTipIndex % cmdTips.length];
|
|
2252
3412
|
if (!tip) return null;
|
|
2253
3413
|
const text = `${tip.name} ${tip.desc}`;
|
|
2254
|
-
return /* @__PURE__ */
|
|
2255
|
-
/* @__PURE__ */
|
|
2256
|
-
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 })
|
|
2257
3417
|
] });
|
|
2258
3418
|
})(),
|
|
2259
|
-
verbose ? /* @__PURE__ */
|
|
3419
|
+
verbose ? /* @__PURE__ */ jsx8(Text9, { color: "#ff1493", children: " \u26A1 Verbose" }) : null
|
|
2260
3420
|
] }),
|
|
2261
|
-
/* @__PURE__ */
|
|
2262
|
-
balanceLoading && balance === null ? /* @__PURE__ */
|
|
2263
|
-
/* @__PURE__ */
|
|
2264
|
-
/* @__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: [
|
|
2265
3425
|
"\u4F59\u989D \xA5",
|
|
2266
3426
|
balance.toFixed(2)
|
|
2267
3427
|
] })
|
|
2268
3428
|
] }) : null,
|
|
2269
|
-
todayCost !== null ? /* @__PURE__ */
|
|
2270
|
-
/* @__PURE__ */
|
|
2271
|
-
/* @__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: [
|
|
2272
3432
|
"\u4ECA\u65E5 \xA5",
|
|
2273
3433
|
todayCost.toFixed(2)
|
|
2274
3434
|
] })
|
|
2275
3435
|
] }) : null
|
|
2276
3436
|
] })
|
|
2277
3437
|
] }),
|
|
2278
|
-
/* @__PURE__ */
|
|
2279
|
-
/* @__PURE__ */
|
|
3438
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
3439
|
+
/* @__PURE__ */ jsx8(Static, { items: displayMessages, children: (msg, i) => {
|
|
2280
3440
|
if (msg.role === "user") {
|
|
2281
|
-
return /* @__PURE__ */
|
|
2282
|
-
/* @__PURE__ */
|
|
2283
|
-
/* @__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 })
|
|
2284
3453
|
] }, i);
|
|
2285
3454
|
}
|
|
2286
3455
|
const detail = msg.assistantDetail;
|
|
2287
|
-
return /* @__PURE__ */
|
|
3456
|
+
return /* @__PURE__ */ jsx8(
|
|
2288
3457
|
AssistantMessage,
|
|
2289
3458
|
{
|
|
2290
3459
|
content: msg.content,
|
|
@@ -2298,7 +3467,7 @@ function ChatSession({
|
|
|
2298
3467
|
i
|
|
2299
3468
|
);
|
|
2300
3469
|
} }, staticKey),
|
|
2301
|
-
isStreaming && /* @__PURE__ */
|
|
3470
|
+
isStreaming && /* @__PURE__ */ jsx8(
|
|
2302
3471
|
AssistantMessage,
|
|
2303
3472
|
{
|
|
2304
3473
|
content: currentContent,
|
|
@@ -2306,25 +3475,25 @@ function ChatSession({
|
|
|
2306
3475
|
isStreaming: true
|
|
2307
3476
|
}
|
|
2308
3477
|
),
|
|
2309
|
-
isStreaming && !currentContent && currentToolCalls.length === 0 && /* @__PURE__ */
|
|
2310
|
-
!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: [
|
|
2311
3480
|
"\u26A0 ",
|
|
2312
3481
|
streamError
|
|
2313
3482
|
] }) })
|
|
2314
3483
|
] }),
|
|
2315
|
-
selectingModel ? /* @__PURE__ */
|
|
2316
|
-
/* @__PURE__ */
|
|
2317
|
-
/* @__PURE__ */
|
|
2318
|
-
/* @__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" }),
|
|
2319
3488
|
modelOptions.map((id, i) => {
|
|
2320
3489
|
const meta = SUPPORTED_MODELS[id];
|
|
2321
3490
|
const isCurrent = id === activeModel;
|
|
2322
3491
|
const isSelected = i === modelSelectIndex;
|
|
2323
3492
|
const marker = isSelected ? " > " : " ";
|
|
2324
3493
|
const suffix = isCurrent ? " (\u5F53\u524D)" : "";
|
|
2325
|
-
return /* @__PURE__ */
|
|
2326
|
-
/* @__PURE__ */
|
|
2327
|
-
|
|
3494
|
+
return /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3495
|
+
/* @__PURE__ */ jsxs8(
|
|
3496
|
+
Text9,
|
|
2328
3497
|
{
|
|
2329
3498
|
color: isSelected ? "#00ff41" : void 0,
|
|
2330
3499
|
bold: isSelected,
|
|
@@ -2335,20 +3504,20 @@ function ChatSession({
|
|
|
2335
3504
|
]
|
|
2336
3505
|
}
|
|
2337
3506
|
),
|
|
2338
|
-
isSelected && /* @__PURE__ */
|
|
3507
|
+
isSelected && /* @__PURE__ */ jsxs8(Text9, { color: "#808080", children: [
|
|
2339
3508
|
" \u2014 ",
|
|
2340
3509
|
id
|
|
2341
3510
|
] })
|
|
2342
3511
|
] }, id);
|
|
2343
3512
|
}),
|
|
2344
|
-
/* @__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" }) })
|
|
2345
3514
|
] }),
|
|
2346
|
-
/* @__PURE__ */
|
|
2347
|
-
] }) : /* @__PURE__ */
|
|
2348
|
-
/* @__PURE__ */
|
|
2349
|
-
/* @__PURE__ */
|
|
2350
|
-
/* @__PURE__ */
|
|
2351
|
-
/* @__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(
|
|
2352
3521
|
TextInput,
|
|
2353
3522
|
{
|
|
2354
3523
|
value: input,
|
|
@@ -2359,19 +3528,19 @@ function ChatSession({
|
|
|
2359
3528
|
inputKey
|
|
2360
3529
|
) })
|
|
2361
3530
|
] }),
|
|
2362
|
-
/* @__PURE__ */
|
|
2363
|
-
/* @__PURE__ */
|
|
2364
|
-
/* @__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 })
|
|
2365
3534
|
] }),
|
|
2366
|
-
doubleCtrlC && !isStreaming && /* @__PURE__ */
|
|
2367
|
-
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" }) })
|
|
2368
3537
|
] });
|
|
2369
3538
|
}
|
|
2370
3539
|
|
|
2371
3540
|
// src/ui/GamePicker.tsx
|
|
2372
|
-
import { Box as
|
|
3541
|
+
import { Box as Box9, Text as Text10, useInput as useInput2 } from "ink";
|
|
2373
3542
|
import { useState as useState4, useCallback as useCallback3 } from "react";
|
|
2374
|
-
import { jsx as
|
|
3543
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2375
3544
|
function GamePicker({ games, onSelect, onExit, onBackToChat }) {
|
|
2376
3545
|
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
2377
3546
|
const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));
|
|
@@ -2399,18 +3568,18 @@ function GamePicker({ games, onSelect, onExit, onBackToChat }) {
|
|
|
2399
3568
|
[games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC]
|
|
2400
3569
|
)
|
|
2401
3570
|
);
|
|
2402
|
-
return /* @__PURE__ */
|
|
2403
|
-
/* @__PURE__ */
|
|
2404
|
-
/* @__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) => {
|
|
2405
3574
|
const isSelected = index === selectedIndex;
|
|
2406
|
-
return /* @__PURE__ */
|
|
2407
|
-
/* @__PURE__ */
|
|
2408
|
-
/* @__PURE__ */
|
|
2409
|
-
/* @__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 }) })
|
|
2410
3579
|
] }, game.id);
|
|
2411
3580
|
}) }),
|
|
2412
|
-
/* @__PURE__ */
|
|
2413
|
-
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" }) })
|
|
2414
3583
|
] });
|
|
2415
3584
|
}
|
|
2416
3585
|
|
|
@@ -2427,9 +3596,9 @@ function listGames() {
|
|
|
2427
3596
|
}
|
|
2428
3597
|
|
|
2429
3598
|
// src/game/brick-breaker/index.tsx
|
|
2430
|
-
import { Box as
|
|
3599
|
+
import { Box as Box10, Text as Text11, useInput as useInput3, render as render2 } from "ink";
|
|
2431
3600
|
import { useState as useState5, useEffect as useEffect4, useRef as useRef3, useCallback as useCallback4 } from "react";
|
|
2432
|
-
import { jsx as
|
|
3601
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2433
3602
|
var GAME_WIDTH = 40;
|
|
2434
3603
|
var GAME_HEIGHT = 18;
|
|
2435
3604
|
var PADDLE_WIDTH = 9;
|
|
@@ -2619,49 +3788,49 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
2619
3788
|
const board = buildBoard(s);
|
|
2620
3789
|
const def = getLevel(s.level);
|
|
2621
3790
|
void tick;
|
|
2622
|
-
return /* @__PURE__ */
|
|
2623
|
-
/* @__PURE__ */
|
|
2624
|
-
/* @__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: [
|
|
2625
3794
|
"\u5173\u5361 ",
|
|
2626
3795
|
s.level,
|
|
2627
3796
|
": ",
|
|
2628
|
-
/* @__PURE__ */
|
|
3797
|
+
/* @__PURE__ */ jsx10(Text11, { color: "cyan", children: def.desc })
|
|
2629
3798
|
] }) }),
|
|
2630
|
-
/* @__PURE__ */
|
|
3799
|
+
/* @__PURE__ */ jsx10(Box10, { width: 12, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2631
3800
|
"\u5206\u6570: ",
|
|
2632
|
-
/* @__PURE__ */
|
|
3801
|
+
/* @__PURE__ */ jsx10(Text11, { color: "yellow", children: String(s.score).padStart(3, "0") })
|
|
2633
3802
|
] }) }),
|
|
2634
|
-
/* @__PURE__ */
|
|
3803
|
+
/* @__PURE__ */ jsx10(Box10, { width: 12, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2635
3804
|
"\u751F\u547D: ",
|
|
2636
|
-
/* @__PURE__ */
|
|
3805
|
+
/* @__PURE__ */ jsx10(Text11, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) })
|
|
2637
3806
|
] }) }),
|
|
2638
|
-
/* @__PURE__ */
|
|
3807
|
+
/* @__PURE__ */ jsx10(Box10, { width: 10, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2639
3808
|
"\u7816\u5757: ",
|
|
2640
|
-
/* @__PURE__ */
|
|
3809
|
+
/* @__PURE__ */ jsx10(Text11, { color: "cyan", children: aliveCount })
|
|
2641
3810
|
] }) }),
|
|
2642
|
-
/* @__PURE__ */
|
|
3811
|
+
/* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsxs10(Text11, { color: s.paused ? "gray" : "green", children: [
|
|
2643
3812
|
"[",
|
|
2644
3813
|
s.paused ? "\u6682\u505C" : "\u8FD0\u884C\u4E2D",
|
|
2645
3814
|
"]"
|
|
2646
3815
|
] }) })
|
|
2647
3816
|
] }),
|
|
2648
|
-
/* @__PURE__ */
|
|
2649
|
-
/* @__PURE__ */
|
|
3817
|
+
/* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
3818
|
+
/* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2650
3819
|
"\u250C",
|
|
2651
3820
|
"\u2500".repeat(GAME_WIDTH),
|
|
2652
3821
|
"\u2510"
|
|
2653
3822
|
] }),
|
|
2654
|
-
/* @__PURE__ */
|
|
2655
|
-
/* @__PURE__ */
|
|
3823
|
+
/* @__PURE__ */ jsx10(Text11, { children: selectingLevel ? " \x1B[90m\u9009\u62E9\u5173\u5361\u540E\u5F00\u59CB...\x1B[0m" : board }),
|
|
3824
|
+
/* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2656
3825
|
"\u2514",
|
|
2657
3826
|
"\u2500".repeat(GAME_WIDTH),
|
|
2658
3827
|
"\u2518"
|
|
2659
3828
|
] })
|
|
2660
3829
|
] }),
|
|
2661
|
-
selectingLevel && /* @__PURE__ */
|
|
2662
|
-
/* @__PURE__ */
|
|
2663
|
-
/* @__PURE__ */
|
|
2664
|
-
/* @__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) }),
|
|
2665
3834
|
". ",
|
|
2666
3835
|
lv.desc,
|
|
2667
3836
|
" (",
|
|
@@ -2670,16 +3839,16 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
2670
3839
|
lv.cols,
|
|
2671
3840
|
")"
|
|
2672
3841
|
] }) }, i)) }),
|
|
2673
|
-
/* @__PURE__ */
|
|
3842
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text11, { dimColor: true, children: "\u6309\u6570\u5B57\u9009\u5173 q \u9000\u51FA" }) })
|
|
2674
3843
|
] }),
|
|
2675
|
-
!selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */
|
|
2676
|
-
/* @__PURE__ */
|
|
2677
|
-
/* @__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: [
|
|
2678
3847
|
" \u5206\u6570: ",
|
|
2679
|
-
/* @__PURE__ */
|
|
3848
|
+
/* @__PURE__ */ jsx10(Text11, { color: "yellow", children: s.score })
|
|
2680
3849
|
] })
|
|
2681
3850
|
] }),
|
|
2682
|
-
!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" }) })
|
|
2683
3852
|
] });
|
|
2684
3853
|
}
|
|
2685
3854
|
var brick_breaker_default = {
|
|
@@ -2687,11 +3856,11 @@ var brick_breaker_default = {
|
|
|
2687
3856
|
name: "Brick Breaker",
|
|
2688
3857
|
description: "\u7ECF\u5178\u6253\u7816\u5757\u6E38\u620F\uFF0C10 \u4E2A\u5173\u5361\u53EF\u9009\uFF01",
|
|
2689
3858
|
play: async () => {
|
|
2690
|
-
await new Promise((
|
|
3859
|
+
await new Promise((resolve2) => {
|
|
2691
3860
|
const { unmount } = render2(
|
|
2692
|
-
/* @__PURE__ */
|
|
3861
|
+
/* @__PURE__ */ jsx10(BrickBreakerGame, { onExit: () => {
|
|
2693
3862
|
unmount();
|
|
2694
|
-
|
|
3863
|
+
resolve2();
|
|
2695
3864
|
} })
|
|
2696
3865
|
);
|
|
2697
3866
|
});
|
|
@@ -2699,9 +3868,9 @@ var brick_breaker_default = {
|
|
|
2699
3868
|
};
|
|
2700
3869
|
|
|
2701
3870
|
// src/game/coder-check/index.tsx
|
|
2702
|
-
import { Box as
|
|
3871
|
+
import { Box as Box11, Text as Text12, useInput as useInput4, render as render3 } from "ink";
|
|
2703
3872
|
import { useState as useState6, useEffect as useEffect5, useRef as useRef4, useCallback as useCallback5 } from "react";
|
|
2704
|
-
import { jsx as
|
|
3873
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2705
3874
|
var GAME_W = 66;
|
|
2706
3875
|
var GAME_H = 20;
|
|
2707
3876
|
var SCORE_H = 6;
|
|
@@ -3172,44 +4341,44 @@ function CoderCheck({ onExit: _onExit }) {
|
|
|
3172
4341
|
const scoreLines = buildScoreLines(scoreStr);
|
|
3173
4342
|
const view = buildGameView(s, scoreLines, scoreColor, s.message);
|
|
3174
4343
|
void tick;
|
|
3175
|
-
return /* @__PURE__ */
|
|
3176
|
-
/* @__PURE__ */
|
|
4344
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
|
|
4345
|
+
/* @__PURE__ */ jsx11(Box11, { flexDirection: "row", children: /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
3177
4346
|
"\u751F\u547D ",
|
|
3178
|
-
/* @__PURE__ */
|
|
4347
|
+
/* @__PURE__ */ jsx11(Text12, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) }),
|
|
3179
4348
|
" ",
|
|
3180
4349
|
"\u901F\u5EA6 ",
|
|
3181
|
-
/* @__PURE__ */
|
|
4350
|
+
/* @__PURE__ */ jsxs11(Text12, { color: "cyan", children: [
|
|
3182
4351
|
"Lv.",
|
|
3183
4352
|
Math.floor(s.speed * 10)
|
|
3184
4353
|
] })
|
|
3185
4354
|
] }) }),
|
|
3186
|
-
!s.gameOver && s.target && /* @__PURE__ */
|
|
4355
|
+
!s.gameOver && s.target && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
3187
4356
|
"\u6253\u5B57: ",
|
|
3188
|
-
/* @__PURE__ */
|
|
3189
|
-
/* @__PURE__ */
|
|
4357
|
+
/* @__PURE__ */ jsx11(Text12, { color: "green", children: s.typed }),
|
|
4358
|
+
/* @__PURE__ */ jsx11(Text12, { color: "white", children: s.target.slice(s.typed.length) })
|
|
3190
4359
|
] }) }),
|
|
3191
|
-
/* @__PURE__ */
|
|
3192
|
-
/* @__PURE__ */
|
|
4360
|
+
/* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
4361
|
+
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
3193
4362
|
"\u250C",
|
|
3194
4363
|
"\u2500".repeat(GAME_W),
|
|
3195
4364
|
"\u2510"
|
|
3196
4365
|
] }),
|
|
3197
|
-
view.map((row, i) => /* @__PURE__ */
|
|
3198
|
-
/* @__PURE__ */
|
|
4366
|
+
view.map((row, i) => /* @__PURE__ */ jsx11(Text12, { children: `\u2502${row}\u2502` }, i)),
|
|
4367
|
+
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
3199
4368
|
"\u2514",
|
|
3200
4369
|
"\u2500".repeat(GAME_W),
|
|
3201
4370
|
"\u2518"
|
|
3202
4371
|
] })
|
|
3203
4372
|
] }),
|
|
3204
|
-
s.gameOver && /* @__PURE__ */
|
|
3205
|
-
/* @__PURE__ */
|
|
3206
|
-
/* @__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: [
|
|
3207
4376
|
" \u5F97\u5206: ",
|
|
3208
|
-
/* @__PURE__ */
|
|
4377
|
+
/* @__PURE__ */ jsx11(Text12, { color: "yellow", children: s.score }),
|
|
3209
4378
|
" r \u91CD\u5F00 q \u9000\u51FA"
|
|
3210
4379
|
] })
|
|
3211
4380
|
] }),
|
|
3212
|
-
/* @__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" }) })
|
|
3213
4382
|
] });
|
|
3214
4383
|
}
|
|
3215
4384
|
var coder_check_default = {
|
|
@@ -3217,11 +4386,11 @@ var coder_check_default = {
|
|
|
3217
4386
|
name: "Coder Check",
|
|
3218
4387
|
description: "\u6781\u901F\u6253\u5B57\u6E38\u620F\uFF0C\u8F93\u5165\u5355\u8BCD\u6D88\u9664\u5B83\u4EEC\uFF01",
|
|
3219
4388
|
play: async () => {
|
|
3220
|
-
await new Promise((
|
|
4389
|
+
await new Promise((resolve2) => {
|
|
3221
4390
|
const { unmount } = render3(
|
|
3222
|
-
/* @__PURE__ */
|
|
4391
|
+
/* @__PURE__ */ jsx11(CoderCheck, { onExit: () => {
|
|
3223
4392
|
unmount();
|
|
3224
|
-
|
|
4393
|
+
resolve2();
|
|
3225
4394
|
} })
|
|
3226
4395
|
);
|
|
3227
4396
|
});
|
|
@@ -3240,10 +4409,10 @@ import { render as render4 } from "ink";
|
|
|
3240
4409
|
import chalk4 from "chalk";
|
|
3241
4410
|
|
|
3242
4411
|
// src/stock/StockList.tsx
|
|
3243
|
-
import { Box as
|
|
4412
|
+
import { Box as Box12, Text as Text13, useInput as useInput5 } from "ink";
|
|
3244
4413
|
import { useState as useState7, useCallback as useCallback6, useEffect as useEffect6 } from "react";
|
|
3245
4414
|
import asciichart from "asciichart";
|
|
3246
|
-
import { jsx as
|
|
4415
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3247
4416
|
var MINUTE_API = "https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1";
|
|
3248
4417
|
function normalizeApiCode(code) {
|
|
3249
4418
|
if (code.startsWith("sh") || code.startsWith("sz")) return code;
|
|
@@ -3440,57 +4609,57 @@ function StockList({ codes, onExit, onBackToChat }) {
|
|
|
3440
4609
|
);
|
|
3441
4610
|
if (detailView) {
|
|
3442
4611
|
if (detailLoading) {
|
|
3443
|
-
return /* @__PURE__ */
|
|
4612
|
+
return /* @__PURE__ */ jsx12(Box12, { paddingLeft: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
|
|
3444
4613
|
}
|
|
3445
4614
|
return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown, currentTime);
|
|
3446
4615
|
}
|
|
3447
|
-
return /* @__PURE__ */
|
|
3448
|
-
/* @__PURE__ */
|
|
3449
|
-
/* @__PURE__ */
|
|
3450
|
-
/* @__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: [
|
|
3451
4620
|
" \u{1F550} ",
|
|
3452
4621
|
currentTime
|
|
3453
4622
|
] }),
|
|
3454
|
-
/* @__PURE__ */
|
|
4623
|
+
/* @__PURE__ */ jsx12(Text13, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` ${countdown}s \u540E\u81EA\u52A8\u5237\u65B0` })
|
|
3455
4624
|
] }),
|
|
3456
|
-
/* @__PURE__ */
|
|
3457
|
-
/* @__PURE__ */
|
|
3458
|
-
/* @__PURE__ */
|
|
3459
|
-
/* @__PURE__ */
|
|
3460
|
-
/* @__PURE__ */
|
|
3461
|
-
/* @__PURE__ */
|
|
3462
|
-
/* @__PURE__ */
|
|
3463
|
-
/* @__PURE__ */
|
|
3464
|
-
/* @__PURE__ */
|
|
3465
|
-
/* @__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" }) })
|
|
3466
4635
|
] }),
|
|
3467
|
-
/* @__PURE__ */
|
|
3468
|
-
/* @__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) => {
|
|
3469
4638
|
const isSelected = index === selectedIndex;
|
|
3470
4639
|
const isUp = stock.changePercent >= 0;
|
|
3471
4640
|
const color = isUp ? "#ff1493" : "#00ff41";
|
|
3472
|
-
return /* @__PURE__ */
|
|
3473
|
-
/* @__PURE__ */
|
|
3474
|
-
/* @__PURE__ */
|
|
3475
|
-
/* @__PURE__ */
|
|
3476
|
-
/* @__PURE__ */
|
|
3477
|
-
/* @__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: [
|
|
3478
4647
|
isUp ? "+" : "",
|
|
3479
4648
|
stock.changePercent.toFixed(2),
|
|
3480
4649
|
"%"
|
|
3481
4650
|
] }) }),
|
|
3482
|
-
/* @__PURE__ */
|
|
4651
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { color, children: [
|
|
3483
4652
|
isUp ? "+" : "",
|
|
3484
4653
|
stock.changeAmount.toFixed(3)
|
|
3485
4654
|
] }) }),
|
|
3486
|
-
/* @__PURE__ */
|
|
3487
|
-
/* @__PURE__ */
|
|
3488
|
-
/* @__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) }) })
|
|
3489
4658
|
] }, stock.code);
|
|
3490
4659
|
}) }),
|
|
3491
|
-
/* @__PURE__ */
|
|
3492
|
-
/* @__PURE__ */
|
|
3493
|
-
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" }) })
|
|
3494
4663
|
] });
|
|
3495
4664
|
}
|
|
3496
4665
|
function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
@@ -3508,33 +4677,33 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
|
3508
4677
|
raw = raw.replaceAll("\u256D", "\u250C").replaceAll("\u256E", "\u2510").replaceAll("\u2570", "\u2514").replaceAll("\u256F", "\u2518");
|
|
3509
4678
|
chartLines = raw.split("\n");
|
|
3510
4679
|
}
|
|
3511
|
-
return /* @__PURE__ */
|
|
3512
|
-
/* @__PURE__ */
|
|
3513
|
-
/* @__PURE__ */
|
|
3514
|
-
/* @__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: [
|
|
3515
4684
|
" \u{1F4CA} ",
|
|
3516
4685
|
stock.name,
|
|
3517
4686
|
" "
|
|
3518
4687
|
] }),
|
|
3519
|
-
/* @__PURE__ */
|
|
3520
|
-
currentTime && /* @__PURE__ */
|
|
4688
|
+
/* @__PURE__ */ jsx12(Text13, { dimColor: true, children: stock.code }),
|
|
4689
|
+
currentTime && /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
3521
4690
|
" \u{1F550} ",
|
|
3522
4691
|
currentTime
|
|
3523
4692
|
] })
|
|
3524
4693
|
] }),
|
|
3525
|
-
/* @__PURE__ */
|
|
4694
|
+
/* @__PURE__ */ jsx12(Text13, { dimColor: true, children: `${countdown}s \u540E\u5237\u65B0` })
|
|
3526
4695
|
] }),
|
|
3527
|
-
/* @__PURE__ */
|
|
3528
|
-
/* @__PURE__ */
|
|
3529
|
-
/* @__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: [
|
|
3530
4699
|
arrow,
|
|
3531
4700
|
" ",
|
|
3532
4701
|
formatPrice(stock.price)
|
|
3533
4702
|
] }) })
|
|
3534
4703
|
] }),
|
|
3535
|
-
/* @__PURE__ */
|
|
3536
|
-
/* @__PURE__ */
|
|
3537
|
-
/* @__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: [
|
|
3538
4707
|
isUp ? "+" : "",
|
|
3539
4708
|
stock.changePercent.toFixed(2),
|
|
3540
4709
|
"%",
|
|
@@ -3543,14 +4712,14 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
|
3543
4712
|
stock.changeAmount.toFixed(3)
|
|
3544
4713
|
] }) })
|
|
3545
4714
|
] }),
|
|
3546
|
-
chartLines.length > 0 && /* @__PURE__ */
|
|
3547
|
-
/* @__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" }) })
|
|
3548
4717
|
] });
|
|
3549
4718
|
}
|
|
3550
4719
|
|
|
3551
4720
|
// src/utils/scan-files.ts
|
|
3552
|
-
import { readdir as
|
|
3553
|
-
import { join as
|
|
4721
|
+
import { readdir as readdir5 } from "fs/promises";
|
|
4722
|
+
import { join as join7, relative as relative4 } from "path";
|
|
3554
4723
|
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
3555
4724
|
"node_modules",
|
|
3556
4725
|
".git",
|
|
@@ -3601,7 +4770,7 @@ async function scanProjectFiles(baseDir, dir) {
|
|
|
3601
4770
|
const currentDir = dir ?? baseDir;
|
|
3602
4771
|
let entries;
|
|
3603
4772
|
try {
|
|
3604
|
-
entries = await
|
|
4773
|
+
entries = await readdir5(currentDir, { withFileTypes: true });
|
|
3605
4774
|
} catch {
|
|
3606
4775
|
return [];
|
|
3607
4776
|
}
|
|
@@ -3615,12 +4784,12 @@ async function scanProjectFiles(baseDir, dir) {
|
|
|
3615
4784
|
} else if (entry.isFile()) {
|
|
3616
4785
|
const ext = name.slice(name.lastIndexOf(".")).toLowerCase();
|
|
3617
4786
|
if (SOURCE_EXTS.has(ext)) {
|
|
3618
|
-
files.push(
|
|
4787
|
+
files.push(relative4(baseDir, join7(currentDir, name)));
|
|
3619
4788
|
}
|
|
3620
4789
|
}
|
|
3621
4790
|
}
|
|
3622
4791
|
const nested = await Promise.all(
|
|
3623
|
-
dirs.map((d) => scanProjectFiles(baseDir,
|
|
4792
|
+
dirs.map((d) => scanProjectFiles(baseDir, join7(currentDir, d)))
|
|
3624
4793
|
);
|
|
3625
4794
|
for (const n of nested) {
|
|
3626
4795
|
files.push(...n);
|
|
@@ -3629,7 +4798,7 @@ async function scanProjectFiles(baseDir, dir) {
|
|
|
3629
4798
|
}
|
|
3630
4799
|
|
|
3631
4800
|
// src/cli/index.tsx
|
|
3632
|
-
import { jsx as
|
|
4801
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
3633
4802
|
var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion", "game", "stock"];
|
|
3634
4803
|
function createCli() {
|
|
3635
4804
|
const program2 = new Command();
|
|
@@ -3675,7 +4844,7 @@ function createCli() {
|
|
|
3675
4844
|
);
|
|
3676
4845
|
const model = defaultProvider?.model ?? "deepseek-v4-flash";
|
|
3677
4846
|
const chatApp = renderApp(
|
|
3678
|
-
/* @__PURE__ */
|
|
4847
|
+
/* @__PURE__ */ jsx13(
|
|
3679
4848
|
ChatSession,
|
|
3680
4849
|
{
|
|
3681
4850
|
skillCount,
|
|
@@ -3693,7 +4862,7 @@ function createCli() {
|
|
|
3693
4862
|
initGames();
|
|
3694
4863
|
const games = listGames();
|
|
3695
4864
|
const { unmount } = render4(
|
|
3696
|
-
/* @__PURE__ */
|
|
4865
|
+
/* @__PURE__ */ jsx13(
|
|
3697
4866
|
GamePicker,
|
|
3698
4867
|
{
|
|
3699
4868
|
games,
|
|
@@ -3717,7 +4886,7 @@ function createCli() {
|
|
|
3717
4886
|
setImmediate(() => {
|
|
3718
4887
|
const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
3719
4888
|
const stockApp = renderApp(
|
|
3720
|
-
/* @__PURE__ */
|
|
4889
|
+
/* @__PURE__ */ jsx13(
|
|
3721
4890
|
StockList,
|
|
3722
4891
|
{
|
|
3723
4892
|
codes: defaultStockCodes,
|
|
@@ -3781,10 +4950,10 @@ compdef _dskcode_completion dskcode`);
|
|
|
3781
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) {
|
|
3782
4951
|
const ctx = this.dskcodeCtx;
|
|
3783
4952
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
3784
|
-
const globalConfigPath =
|
|
4953
|
+
const globalConfigPath = join8(home, ".dskcode", "settings.json");
|
|
3785
4954
|
let globalConfigHasStock = false;
|
|
3786
4955
|
try {
|
|
3787
|
-
const raw = await
|
|
4956
|
+
const raw = await readFile8(globalConfigPath, "utf-8");
|
|
3788
4957
|
const parsed = JSON.parse(raw);
|
|
3789
4958
|
const stock = parsed.stock;
|
|
3790
4959
|
globalConfigHasStock = Array.isArray(stock?.symbols) && stock.symbols.length > 0;
|
|
@@ -3804,7 +4973,7 @@ compdef _dskcode_completion dskcode`);
|
|
|
3804
4973
|
const freshResult = await loadAndValidate();
|
|
3805
4974
|
const codeList = codes && codes.length > 0 ? codes : freshResult.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
3806
4975
|
const app = renderApp(
|
|
3807
|
-
/* @__PURE__ */
|
|
4976
|
+
/* @__PURE__ */ jsx13(
|
|
3808
4977
|
StockList,
|
|
3809
4978
|
{
|
|
3810
4979
|
codes: codeList,
|
|
@@ -3831,19 +5000,19 @@ compdef _dskcode_completion dskcode`);
|
|
|
3831
5000
|
console.log("\u6682\u65E0\u53EF\u7528\u6E38\u620F\u3002");
|
|
3832
5001
|
return;
|
|
3833
5002
|
}
|
|
3834
|
-
const selectedGame = await new Promise((
|
|
5003
|
+
const selectedGame = await new Promise((resolve2) => {
|
|
3835
5004
|
const { unmount } = render4(
|
|
3836
|
-
/* @__PURE__ */
|
|
5005
|
+
/* @__PURE__ */ jsx13(
|
|
3837
5006
|
GamePicker,
|
|
3838
5007
|
{
|
|
3839
5008
|
games,
|
|
3840
5009
|
onSelect: (game) => {
|
|
3841
5010
|
unmount();
|
|
3842
|
-
|
|
5011
|
+
resolve2(game);
|
|
3843
5012
|
},
|
|
3844
5013
|
onExit: () => {
|
|
3845
5014
|
unmount();
|
|
3846
|
-
|
|
5015
|
+
resolve2(null);
|
|
3847
5016
|
}
|
|
3848
5017
|
}
|
|
3849
5018
|
),
|