dskcode 0.1.20 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-BA7UJPKJ.js → chunk-I6HEIOSU.js} +242 -164
- package/dist/chunk-I6HEIOSU.js.map +1 -0
- package/dist/deepseek-YTT76XDE.js +7 -0
- package/dist/index.js +1000 -386
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-7QS2XBBX.js +0 -83
- package/dist/chunk-7QS2XBBX.js.map +0 -1
- package/dist/chunk-BA7UJPKJ.js.map +0 -1
- package/dist/deepseek-UJXV2D6K.js +0 -8
- package/dist/models-OL7DHYGK.js +0 -19
- package/dist/models-OL7DHYGK.js.map +0 -1
- /package/dist/{deepseek-UJXV2D6K.js.map → deepseek-YTT76XDE.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DeepSeekProvider,
|
|
4
|
-
ModelNotSupportedError
|
|
5
|
-
} from "./chunk-BA7UJPKJ.js";
|
|
6
|
-
import {
|
|
4
|
+
ModelNotSupportedError,
|
|
7
5
|
SUPPORTED_MODELS,
|
|
8
6
|
calculateCost,
|
|
9
7
|
estimateTokens,
|
|
10
8
|
getModelMeta,
|
|
11
9
|
isSupportedModel
|
|
12
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-I6HEIOSU.js";
|
|
13
11
|
|
|
14
12
|
// src/cli/index.tsx
|
|
15
13
|
import { Command } from "commander";
|
|
@@ -818,7 +816,7 @@ var LOGO_LINES = [
|
|
|
818
816
|
// src/ui/ChatSession.tsx
|
|
819
817
|
import { Box as Box8, Text as Text9, useInput, Static } from "ink";
|
|
820
818
|
import TextInput from "ink-text-input";
|
|
821
|
-
import { useEffect as useEffect3, useState as useState3, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
819
|
+
import { useEffect as useEffect3, useState as useState3, useCallback as useCallback2, useMemo, useRef as useRef2 } from "react";
|
|
822
820
|
|
|
823
821
|
// src/ui/useDoubleCtrlC.ts
|
|
824
822
|
import { useState as useState2, useCallback, useEffect as useEffect2, useRef } from "react";
|
|
@@ -848,6 +846,9 @@ function useDoubleCtrlC(onExit) {
|
|
|
848
846
|
return { doubleCtrlC, handleCtrlC };
|
|
849
847
|
}
|
|
850
848
|
|
|
849
|
+
// src/ui/ChatSession.tsx
|
|
850
|
+
import InkSpinner2 from "ink-spinner";
|
|
851
|
+
|
|
851
852
|
// src/ui/AssistantMessage.tsx
|
|
852
853
|
import { Box as Box4, Text as Text5 } from "ink";
|
|
853
854
|
|
|
@@ -1182,38 +1183,58 @@ function estimateMessageTokens(msg) {
|
|
|
1182
1183
|
}
|
|
1183
1184
|
return estimateTokens(text) + 10;
|
|
1184
1185
|
}
|
|
1186
|
+
function groupIntoTurns(messages) {
|
|
1187
|
+
const turns = [];
|
|
1188
|
+
let current = null;
|
|
1189
|
+
for (const msg of messages) {
|
|
1190
|
+
if (msg.role === "user") {
|
|
1191
|
+
if (current) turns.push(current);
|
|
1192
|
+
current = [msg];
|
|
1193
|
+
} else {
|
|
1194
|
+
if (!current) current = [];
|
|
1195
|
+
current.push(msg);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
if (current && current.length > 0) turns.push(current);
|
|
1199
|
+
return turns;
|
|
1200
|
+
}
|
|
1201
|
+
function estimateTurnTokens(turn) {
|
|
1202
|
+
let sum = 0;
|
|
1203
|
+
for (const msg of turn) sum += estimateMessageTokens(msg);
|
|
1204
|
+
return sum;
|
|
1205
|
+
}
|
|
1185
1206
|
function trimMessages(messages, opts) {
|
|
1186
1207
|
const meta = getModelMeta(opts.model);
|
|
1187
1208
|
const maxInputTokens = meta.contextWindow - opts.reservedForOutput;
|
|
1188
1209
|
const systemTokens = estimateTokens(opts.systemPrompt);
|
|
1189
1210
|
let remaining = maxInputTokens - systemTokens;
|
|
1190
|
-
const
|
|
1211
|
+
const turns = groupIntoTurns(messages);
|
|
1212
|
+
const preservedTurns = [];
|
|
1191
1213
|
let roundsPreserved = 0;
|
|
1192
|
-
for (let i =
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
roundsPreserved++;
|
|
1196
|
-
}
|
|
1214
|
+
for (let i = turns.length - 1; i >= 0 && roundsPreserved < opts.preserveRecentRounds; i--) {
|
|
1215
|
+
preservedTurns.unshift(turns[i]);
|
|
1216
|
+
roundsPreserved++;
|
|
1197
1217
|
}
|
|
1218
|
+
const preserved = preservedTurns.flat();
|
|
1198
1219
|
for (const msg of preserved) {
|
|
1199
1220
|
remaining -= estimateMessageTokens(msg);
|
|
1200
1221
|
}
|
|
1201
1222
|
if (remaining < 0) {
|
|
1202
|
-
while (
|
|
1203
|
-
const
|
|
1204
|
-
remaining +=
|
|
1223
|
+
while (preservedTurns.length > 1 && remaining < 0) {
|
|
1224
|
+
const dropped = preservedTurns.shift();
|
|
1225
|
+
remaining += estimateTurnTokens(dropped);
|
|
1205
1226
|
}
|
|
1206
|
-
return [
|
|
1227
|
+
return [preservedTurns.flat(), true];
|
|
1207
1228
|
}
|
|
1208
|
-
const
|
|
1209
|
-
const
|
|
1210
|
-
for (let i =
|
|
1211
|
-
const cost =
|
|
1229
|
+
const olderTurns = turns.slice(0, turns.length - preservedTurns.length);
|
|
1230
|
+
const keptTurns = [];
|
|
1231
|
+
for (let i = olderTurns.length - 1; i >= 0; i--) {
|
|
1232
|
+
const cost = estimateTurnTokens(olderTurns[i]);
|
|
1212
1233
|
if (remaining - cost < 0) break;
|
|
1213
1234
|
remaining -= cost;
|
|
1214
|
-
|
|
1235
|
+
keptTurns.unshift(olderTurns[i]);
|
|
1215
1236
|
}
|
|
1216
|
-
const result = [...
|
|
1237
|
+
const result = [...keptTurns, ...preservedTurns].flat();
|
|
1217
1238
|
const trimmed = result.length < messages.length;
|
|
1218
1239
|
return [result, trimmed];
|
|
1219
1240
|
}
|
|
@@ -1420,6 +1441,44 @@ function createProvider(config) {
|
|
|
1420
1441
|
return defaultRegistry.get(config.name, config);
|
|
1421
1442
|
}
|
|
1422
1443
|
|
|
1444
|
+
// src/tool/types.ts
|
|
1445
|
+
function eraseTool(tool) {
|
|
1446
|
+
return {
|
|
1447
|
+
get name() {
|
|
1448
|
+
return tool.name;
|
|
1449
|
+
},
|
|
1450
|
+
get description() {
|
|
1451
|
+
return tool.description;
|
|
1452
|
+
},
|
|
1453
|
+
get kind() {
|
|
1454
|
+
return tool.kind;
|
|
1455
|
+
},
|
|
1456
|
+
get parameters() {
|
|
1457
|
+
return tool.parameters;
|
|
1458
|
+
},
|
|
1459
|
+
get supportsInputStreaming() {
|
|
1460
|
+
return tool.supportsInputStreaming ?? false;
|
|
1461
|
+
},
|
|
1462
|
+
get supportedProviders() {
|
|
1463
|
+
return tool.supportedProviders ?? [];
|
|
1464
|
+
},
|
|
1465
|
+
async execute(args, ctx) {
|
|
1466
|
+
return tool.execute(args, ctx);
|
|
1467
|
+
},
|
|
1468
|
+
initialTitle(args) {
|
|
1469
|
+
return tool.initialTitle?.(args) ?? `${tool.name}`;
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
function isReadOnly(kind) {
|
|
1474
|
+
return kind === "read" /* Read */;
|
|
1475
|
+
}
|
|
1476
|
+
var AlwaysAllowGate = class {
|
|
1477
|
+
check(_toolName, _args) {
|
|
1478
|
+
return true;
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1423
1482
|
// src/agent/extra-prompt.ts
|
|
1424
1483
|
var EXTRA_PROMPT = [
|
|
1425
1484
|
"\u3010\u7EC8\u7AEF\u8F93\u51FA\u7EA6\u675F\u3011",
|
|
@@ -1522,14 +1581,28 @@ ${opts.projectContext}`);
|
|
|
1522
1581
|
var ToolRegistry = class {
|
|
1523
1582
|
#tools = /* @__PURE__ */ new Map();
|
|
1524
1583
|
#disabledNames;
|
|
1584
|
+
#featureFlagChecker;
|
|
1585
|
+
#provider;
|
|
1586
|
+
/** 所有已注册工具的名称列表(编译期等效) */
|
|
1587
|
+
static ALL_TOOL_NAMES = [];
|
|
1525
1588
|
constructor(opts) {
|
|
1526
1589
|
this.#disabledNames = new Set(opts?.disabledTools ?? []);
|
|
1590
|
+
this.#featureFlagChecker = opts?.featureFlagChecker ?? (() => true);
|
|
1591
|
+
this.#provider = opts?.provider;
|
|
1527
1592
|
}
|
|
1528
1593
|
/**
|
|
1529
|
-
*
|
|
1594
|
+
* 注册一个类型安全的 AgentTool。
|
|
1595
|
+
* 自动擦除类型参数后存储。
|
|
1530
1596
|
* 如果同名工具已存在则抛出错误。
|
|
1531
1597
|
*/
|
|
1532
1598
|
register(tool) {
|
|
1599
|
+
return this.registerErased(eraseTool(tool));
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* 注册一个已经擦除类型 AnyAgentTool。
|
|
1603
|
+
* 如果同名工具已存在则抛出错误。
|
|
1604
|
+
*/
|
|
1605
|
+
registerErased(tool) {
|
|
1533
1606
|
if (this.#tools.has(tool.name)) {
|
|
1534
1607
|
throw new Error(`\u5DE5\u5177 "${tool.name}" \u5DF2\u6CE8\u518C\uFF0C\u4E0D\u80FD\u91CD\u590D\u6CE8\u518C`);
|
|
1535
1608
|
}
|
|
@@ -1541,7 +1614,7 @@ var ToolRegistry = class {
|
|
|
1541
1614
|
*/
|
|
1542
1615
|
registerAll(tools) {
|
|
1543
1616
|
for (const tool of tools) {
|
|
1544
|
-
this.
|
|
1617
|
+
this.registerErased(tool);
|
|
1545
1618
|
}
|
|
1546
1619
|
return this;
|
|
1547
1620
|
}
|
|
@@ -1552,25 +1625,44 @@ var ToolRegistry = class {
|
|
|
1552
1625
|
return this.#tools.delete(name);
|
|
1553
1626
|
}
|
|
1554
1627
|
/**
|
|
1555
|
-
*
|
|
1556
|
-
*
|
|
1628
|
+
* 按名称获取工具(未擦除类型版本,调用方自行断言类型)。
|
|
1629
|
+
* 如果工具被禁用、被 feature flag 拦截、或不支持当前 provider,返回 undefined。
|
|
1557
1630
|
*/
|
|
1558
1631
|
get(name) {
|
|
1559
|
-
if (this.#
|
|
1632
|
+
if (!this.#isToolEnabled(name)) return void 0;
|
|
1560
1633
|
return this.#tools.get(name);
|
|
1561
1634
|
}
|
|
1562
1635
|
/**
|
|
1563
1636
|
* 获取所有启用的工具列表。
|
|
1637
|
+
* 依次应用:禁用列表 → Feature Flag → Provider 过滤。
|
|
1564
1638
|
*/
|
|
1565
1639
|
list() {
|
|
1566
1640
|
const result = [];
|
|
1567
1641
|
for (const [name, tool] of this.#tools) {
|
|
1568
|
-
if (
|
|
1642
|
+
if (this.#isToolEnabled(name)) {
|
|
1569
1643
|
result.push(tool);
|
|
1570
1644
|
}
|
|
1571
1645
|
}
|
|
1572
1646
|
return result;
|
|
1573
1647
|
}
|
|
1648
|
+
/**
|
|
1649
|
+
* 按 ToolKind 分类获取工具列表。
|
|
1650
|
+
*/
|
|
1651
|
+
listByKind(kind) {
|
|
1652
|
+
return this.list().filter((t) => t.kind === kind);
|
|
1653
|
+
}
|
|
1654
|
+
/**
|
|
1655
|
+
* 获取读工具列表(可并行执行)。
|
|
1656
|
+
*/
|
|
1657
|
+
listReadTools() {
|
|
1658
|
+
return this.listByKind("read" /* Read */);
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* 获取写工具列表(需串行执行)。
|
|
1662
|
+
*/
|
|
1663
|
+
listWriteTools() {
|
|
1664
|
+
return this.list().filter((t) => !isReadOnly(t.kind));
|
|
1665
|
+
}
|
|
1574
1666
|
/**
|
|
1575
1667
|
* 获取所有已注册的工具名称(含禁用的)。
|
|
1576
1668
|
*/
|
|
@@ -1584,10 +1676,10 @@ var ToolRegistry = class {
|
|
|
1584
1676
|
return this.#tools.has(name);
|
|
1585
1677
|
}
|
|
1586
1678
|
/**
|
|
1587
|
-
*
|
|
1679
|
+
* 检查工具是否已启用(注册 + 未禁用 + 通过 feature flag + 支持 provider)。
|
|
1588
1680
|
*/
|
|
1589
1681
|
isEnabled(name) {
|
|
1590
|
-
return this.#tools.has(name) &&
|
|
1682
|
+
return this.#tools.has(name) && this.#isToolEnabled(name);
|
|
1591
1683
|
}
|
|
1592
1684
|
/**
|
|
1593
1685
|
* 禁用一个工具。
|
|
@@ -1601,6 +1693,12 @@ var ToolRegistry = class {
|
|
|
1601
1693
|
enable(name) {
|
|
1602
1694
|
this.#disabledNames.delete(name);
|
|
1603
1695
|
}
|
|
1696
|
+
/**
|
|
1697
|
+
* 获取工具的分类语义。
|
|
1698
|
+
*/
|
|
1699
|
+
kindOf(name) {
|
|
1700
|
+
return this.#tools.get(name)?.kind;
|
|
1701
|
+
}
|
|
1604
1702
|
/**
|
|
1605
1703
|
* 执行指定工具。
|
|
1606
1704
|
*
|
|
@@ -1626,11 +1724,21 @@ var ToolRegistry = class {
|
|
|
1626
1724
|
};
|
|
1627
1725
|
}
|
|
1628
1726
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
//
|
|
1632
|
-
|
|
1633
|
-
|
|
1727
|
+
// ---------------------------------------------------------------------------
|
|
1728
|
+
// 内部方法
|
|
1729
|
+
// ---------------------------------------------------------------------------
|
|
1730
|
+
/**
|
|
1731
|
+
* 判断工具是否应该启用。
|
|
1732
|
+
* 依次检查:是否在禁用列表 → 是否通过 Feature Flag → 是否支持当前 Provider。
|
|
1733
|
+
*/
|
|
1734
|
+
#isToolEnabled(name) {
|
|
1735
|
+
const tool = this.#tools.get(name);
|
|
1736
|
+
if (!tool) return false;
|
|
1737
|
+
if (this.#disabledNames.has(name)) return false;
|
|
1738
|
+
if (!this.#featureFlagChecker(name)) return false;
|
|
1739
|
+
if (this.#provider && tool.supportedProviders.length > 0) {
|
|
1740
|
+
if (!tool.supportedProviders.includes(this.#provider)) return false;
|
|
1741
|
+
}
|
|
1634
1742
|
return true;
|
|
1635
1743
|
}
|
|
1636
1744
|
};
|
|
@@ -1698,7 +1806,7 @@ var Session = class {
|
|
|
1698
1806
|
* d. 如果没有工具调用 → 退出循环
|
|
1699
1807
|
* 3. yield done 事件
|
|
1700
1808
|
*/
|
|
1701
|
-
async *chat(userInput) {
|
|
1809
|
+
async *chat(userInput, opts) {
|
|
1702
1810
|
this.#messages.push({ role: "user", content: userInput });
|
|
1703
1811
|
const startTime = Date.now();
|
|
1704
1812
|
let toolRounds = 0;
|
|
@@ -1718,7 +1826,11 @@ var Session = class {
|
|
|
1718
1826
|
const toolDefs = this.#buildToolDefinitions();
|
|
1719
1827
|
const stream = this.#provider.chat(apiMessages, {
|
|
1720
1828
|
signal: this.#abortController.signal,
|
|
1721
|
-
tools: toolDefs.length > 0 ? toolDefs : void 0
|
|
1829
|
+
tools: toolDefs.length > 0 ? toolDefs : void 0,
|
|
1830
|
+
thinkingAllowed: opts?.thinkingAllowed,
|
|
1831
|
+
thinkingEffort: opts?.thinkingEffort,
|
|
1832
|
+
responseFormat: opts?.responseFormat,
|
|
1833
|
+
toolChoice: opts?.toolChoice
|
|
1722
1834
|
});
|
|
1723
1835
|
let accumulatedText = "";
|
|
1724
1836
|
let lastUsage;
|
|
@@ -1815,11 +1927,12 @@ ${item.result.diff.patch}`;
|
|
|
1815
1927
|
async #executeBatch(calls) {
|
|
1816
1928
|
const toolCtx = {
|
|
1817
1929
|
cwd: this.#options.cwd,
|
|
1818
|
-
signal: this.#abortController.signal
|
|
1930
|
+
signal: this.#abortController.signal,
|
|
1931
|
+
writeRoots: this.#options.writeRoots
|
|
1819
1932
|
};
|
|
1820
1933
|
const allReadOnly = calls.every((tc) => {
|
|
1821
1934
|
const tool = this.#toolRegistry.get(tc.name);
|
|
1822
|
-
return tool
|
|
1935
|
+
return tool ? isReadOnly(tool.kind) : true;
|
|
1823
1936
|
});
|
|
1824
1937
|
if (allReadOnly && calls.length > 1) {
|
|
1825
1938
|
const MAX_PARALLEL = 8;
|
|
@@ -1873,11 +1986,11 @@ ${item.result.diff.patch}`;
|
|
|
1873
1986
|
record: { name: toolName, success: false, error: "GATE_DENIED", timestamp }
|
|
1874
1987
|
};
|
|
1875
1988
|
}
|
|
1876
|
-
if (!tool.
|
|
1877
|
-
const
|
|
1878
|
-
if (typeof
|
|
1989
|
+
if (!isReadOnly(tool.kind)) {
|
|
1990
|
+
const maybePreview = tool.preview;
|
|
1991
|
+
if (typeof maybePreview === "function") {
|
|
1879
1992
|
try {
|
|
1880
|
-
await
|
|
1993
|
+
await maybePreview(toolArgs, ctx);
|
|
1881
1994
|
} catch {
|
|
1882
1995
|
}
|
|
1883
1996
|
}
|
|
@@ -1977,6 +2090,39 @@ function resolvePath(inputPath, cwd) {
|
|
|
1977
2090
|
const resolved = isAbsolute(inputPath) ? inputPath : resolve(cwd, inputPath);
|
|
1978
2091
|
return resolve(resolved);
|
|
1979
2092
|
}
|
|
2093
|
+
async function realPath(target) {
|
|
2094
|
+
try {
|
|
2095
|
+
return await realpath2(target);
|
|
2096
|
+
} catch {
|
|
2097
|
+
const parent = resolve(target, "..");
|
|
2098
|
+
try {
|
|
2099
|
+
const realParent = await realpath2(parent);
|
|
2100
|
+
return resolve(realParent, relative(parent, target));
|
|
2101
|
+
} catch {
|
|
2102
|
+
return resolve(target);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
async function confine(allowedRoots, target) {
|
|
2107
|
+
if (allowedRoots.length === 0) {
|
|
2108
|
+
return { ok: true };
|
|
2109
|
+
}
|
|
2110
|
+
const realTarget = await realPath(target);
|
|
2111
|
+
for (const root of allowedRoots) {
|
|
2112
|
+
const realRoot = await realPath(root);
|
|
2113
|
+
const rel = relative(realRoot, realTarget);
|
|
2114
|
+
if (!rel.startsWith("..") && rel !== "" && !rel.startsWith("/") && !rel.startsWith("\\")) {
|
|
2115
|
+
return { ok: true };
|
|
2116
|
+
}
|
|
2117
|
+
if (realTarget === realRoot) {
|
|
2118
|
+
return { ok: true };
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
return {
|
|
2122
|
+
ok: false,
|
|
2123
|
+
error: `\u8DEF\u5F84 "${target}" \u4E0D\u5728\u5141\u8BB8\u7684\u5199\u5165\u8303\u56F4\u5185 ${allowedRoots.join(", ")}`
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
1980
2126
|
function truncateOutput(content, maxLength = DEFAULT_MAX_OUTPUT_LENGTH) {
|
|
1981
2127
|
if (content.length <= maxLength) return content;
|
|
1982
2128
|
const truncated = content.slice(0, maxLength);
|
|
@@ -2320,29 +2466,45 @@ function computeFileDiff(oldContent, newContent, filePath) {
|
|
|
2320
2466
|
};
|
|
2321
2467
|
}
|
|
2322
2468
|
|
|
2469
|
+
// src/tool/eol.ts
|
|
2470
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
2471
|
+
function detectEol(text) {
|
|
2472
|
+
if (text.length === 0) return "\n";
|
|
2473
|
+
const crlfIdx = text.indexOf("\r\n");
|
|
2474
|
+
const lfIdx = text.indexOf("\n");
|
|
2475
|
+
if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {
|
|
2476
|
+
return "\r\n";
|
|
2477
|
+
}
|
|
2478
|
+
return "\n";
|
|
2479
|
+
}
|
|
2480
|
+
function hasTrailingNewline(text) {
|
|
2481
|
+
return text.endsWith("\n") || text.endsWith("\r\n");
|
|
2482
|
+
}
|
|
2483
|
+
function normalizeEol(originalContent, newContent) {
|
|
2484
|
+
const targetEol = detectEol(originalContent);
|
|
2485
|
+
const lines = newContent.replace(/\r\n/g, "\n").split("\n");
|
|
2486
|
+
const originalHasTrailing = hasTrailingNewline(originalContent);
|
|
2487
|
+
const newHasTrailing = lines.length > 0 && lines[lines.length - 1] === "";
|
|
2488
|
+
let result = lines.join(targetEol);
|
|
2489
|
+
if (originalHasTrailing && !newHasTrailing) {
|
|
2490
|
+
result += targetEol;
|
|
2491
|
+
}
|
|
2492
|
+
if (!originalHasTrailing && newHasTrailing) {
|
|
2493
|
+
if (result.endsWith(targetEol)) {
|
|
2494
|
+
result = result.slice(0, -targetEol.length);
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
return result;
|
|
2498
|
+
}
|
|
2499
|
+
async function writeFileWithEol(filePath, originalContent, newContent) {
|
|
2500
|
+
const content = originalContent.length > 0 ? normalizeEol(originalContent, newContent) : newContent;
|
|
2501
|
+
await writeFile3(filePath, content, "utf-8");
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2323
2504
|
// src/tool/builtins/read-file.ts
|
|
2324
2505
|
import { readFile as readFile4, stat } from "fs/promises";
|
|
2325
2506
|
import { open } from "fs/promises";
|
|
2326
2507
|
import { relative as relative2 } from "path";
|
|
2327
|
-
var readFileSchema = {
|
|
2328
|
-
type: "object",
|
|
2329
|
-
properties: {
|
|
2330
|
-
path: {
|
|
2331
|
-
type: "string",
|
|
2332
|
-
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2333
|
-
},
|
|
2334
|
-
startLine: {
|
|
2335
|
-
type: "number",
|
|
2336
|
-
description: "\u8D77\u59CB\u884C\u53F7\uFF08\u4ECE 1 \u5F00\u59CB\uFF09\uFF0C\u9ED8\u8BA4\u4E3A 1"
|
|
2337
|
-
},
|
|
2338
|
-
endLine: {
|
|
2339
|
-
type: "number",
|
|
2340
|
-
description: "\u7ED3\u675F\u884C\u53F7\uFF08\u5305\u542B\uFF09\uFF0C\u9ED8\u8BA4\u5230\u6587\u4EF6\u672B\u5C3E"
|
|
2341
|
-
}
|
|
2342
|
-
},
|
|
2343
|
-
required: ["path"],
|
|
2344
|
-
additionalProperties: false
|
|
2345
|
-
};
|
|
2346
2508
|
async function checkBinary(filePath) {
|
|
2347
2509
|
const fileHandle = await open(filePath, "r");
|
|
2348
2510
|
try {
|
|
@@ -2355,15 +2517,32 @@ async function checkBinary(filePath) {
|
|
|
2355
2517
|
}
|
|
2356
2518
|
var readFileTool = {
|
|
2357
2519
|
name: "read_file",
|
|
2520
|
+
kind: "read" /* Read */,
|
|
2358
2521
|
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\u81EA\u52A8\u62D2\u7EDD\u4E8C\u8FDB\u5236\u6587\u4EF6\u3002",
|
|
2359
|
-
parameters:
|
|
2360
|
-
|
|
2522
|
+
parameters: {
|
|
2523
|
+
type: "object",
|
|
2524
|
+
properties: {
|
|
2525
|
+
path: {
|
|
2526
|
+
type: "string",
|
|
2527
|
+
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2528
|
+
},
|
|
2529
|
+
startLine: {
|
|
2530
|
+
type: "number",
|
|
2531
|
+
description: "\u8D77\u59CB\u884C\u53F7\uFF08\u4ECE 1 \u5F00\u59CB\uFF09\uFF0C\u9ED8\u8BA4\u4E3A 1"
|
|
2532
|
+
},
|
|
2533
|
+
endLine: {
|
|
2534
|
+
type: "number",
|
|
2535
|
+
description: "\u7ED3\u675F\u884C\u53F7\uFF08\u5305\u542B\uFF09\uFF0C\u9ED8\u8BA4\u5230\u6587\u4EF6\u672B\u5C3E"
|
|
2536
|
+
}
|
|
2537
|
+
},
|
|
2538
|
+
required: ["path"],
|
|
2539
|
+
additionalProperties: false
|
|
2540
|
+
},
|
|
2361
2541
|
async execute(args, ctx) {
|
|
2362
|
-
|
|
2363
|
-
if (!params?.path || typeof params.path !== "string") {
|
|
2542
|
+
if (!args?.path || typeof args.path !== "string") {
|
|
2364
2543
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 path", error: "INVALID_ARGS" };
|
|
2365
2544
|
}
|
|
2366
|
-
const filePath = resolvePath(
|
|
2545
|
+
const filePath = resolvePath(args.path, ctx.cwd);
|
|
2367
2546
|
try {
|
|
2368
2547
|
const fileStat = await stat(filePath);
|
|
2369
2548
|
const maxSize = 10 * 1024 * 1024;
|
|
@@ -2396,8 +2575,8 @@ var readFileTool = {
|
|
|
2396
2575
|
if (lines.length > 0 && lines[lines.length - 1] === "" && content.endsWith("\n")) {
|
|
2397
2576
|
lines.pop();
|
|
2398
2577
|
}
|
|
2399
|
-
const startLine = Math.max(1,
|
|
2400
|
-
const endLine =
|
|
2578
|
+
const startLine = Math.max(1, args.startLine ?? 1) - 1;
|
|
2579
|
+
const endLine = args.endLine ? Math.min(args.endLine, lines.length) : lines.length;
|
|
2401
2580
|
const selectedLines = lines.slice(startLine, endLine);
|
|
2402
2581
|
const lineNumWidth = String(endLine).length;
|
|
2403
2582
|
const result = selectedLines.map((line, i) => {
|
|
@@ -2427,38 +2606,42 @@ var readFileTool = {
|
|
|
2427
2606
|
};
|
|
2428
2607
|
|
|
2429
2608
|
// src/tool/builtins/write-file.ts
|
|
2430
|
-
import {
|
|
2609
|
+
import { mkdir as mkdir4, readFile as readFile5 } from "fs/promises";
|
|
2431
2610
|
import { dirname, relative as relative3, basename } from "path";
|
|
2432
|
-
var writeFileSchema = {
|
|
2433
|
-
type: "object",
|
|
2434
|
-
properties: {
|
|
2435
|
-
path: {
|
|
2436
|
-
type: "string",
|
|
2437
|
-
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2438
|
-
},
|
|
2439
|
-
content: {
|
|
2440
|
-
type: "string",
|
|
2441
|
-
description: "\u8981\u5199\u5165\u7684\u6587\u4EF6\u5185\u5BB9"
|
|
2442
|
-
}
|
|
2443
|
-
},
|
|
2444
|
-
required: ["path", "content"],
|
|
2445
|
-
additionalProperties: false
|
|
2446
|
-
};
|
|
2447
2611
|
var writeFileTool = {
|
|
2448
2612
|
name: "write_file",
|
|
2613
|
+
kind: "edit" /* Edit */,
|
|
2449
2614
|
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\u8BF7\u52FF\u7528\u6B64\u5DE5\u5177\u521B\u5EFA _temp_\u3001_debug_ \u7B49\u7528\u4E8E\u8BCA\u65AD/\u8C03\u8BD5\u7684\u4E34\u65F6\u6587\u4EF6\u2014\u2014\u5982\u9700\u8BCA\u65AD\u8BF7\u6539\u7528 bash \u5DE5\u5177\u5185\u8054\u811A\u672C\uFF08node -e\uFF09\u3002",
|
|
2450
|
-
parameters:
|
|
2451
|
-
|
|
2615
|
+
parameters: {
|
|
2616
|
+
type: "object",
|
|
2617
|
+
properties: {
|
|
2618
|
+
path: {
|
|
2619
|
+
type: "string",
|
|
2620
|
+
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2621
|
+
},
|
|
2622
|
+
content: {
|
|
2623
|
+
type: "string",
|
|
2624
|
+
description: "\u8981\u5199\u5165\u7684\u6587\u4EF6\u5185\u5BB9"
|
|
2625
|
+
}
|
|
2626
|
+
},
|
|
2627
|
+
required: ["path", "content"],
|
|
2628
|
+
additionalProperties: false
|
|
2629
|
+
},
|
|
2452
2630
|
async execute(args, ctx) {
|
|
2453
|
-
|
|
2454
|
-
if (!params?.path || typeof params.path !== "string") {
|
|
2631
|
+
if (!args?.path || typeof args.path !== "string") {
|
|
2455
2632
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 path", error: "INVALID_ARGS" };
|
|
2456
2633
|
}
|
|
2457
|
-
if (
|
|
2634
|
+
if (args.content === void 0 || args.content === null) {
|
|
2458
2635
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 content", error: "INVALID_ARGS" };
|
|
2459
2636
|
}
|
|
2460
|
-
const filePath = resolvePath(
|
|
2637
|
+
const filePath = resolvePath(args.path, ctx.cwd);
|
|
2461
2638
|
try {
|
|
2639
|
+
if (ctx.writeRoots && ctx.writeRoots.length > 0) {
|
|
2640
|
+
const conf = await confine(ctx.writeRoots, filePath);
|
|
2641
|
+
if (!conf.ok) {
|
|
2642
|
+
return { success: false, data: conf.error, error: "OUTSIDE_WRITE_ROOTS" };
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2462
2645
|
let oldContent = "";
|
|
2463
2646
|
let existedBefore = false;
|
|
2464
2647
|
try {
|
|
@@ -2467,8 +2650,8 @@ var writeFileTool = {
|
|
|
2467
2650
|
} catch {
|
|
2468
2651
|
}
|
|
2469
2652
|
await mkdir4(dirname(filePath), { recursive: true });
|
|
2470
|
-
const content = String(
|
|
2471
|
-
await
|
|
2653
|
+
const content = String(args.content);
|
|
2654
|
+
await writeFileWithEol(filePath, oldContent, content);
|
|
2472
2655
|
const diff = computeFileDiff(oldContent, content, filePath);
|
|
2473
2656
|
diff.existedBefore = existedBefore;
|
|
2474
2657
|
const lineCount = content.split("\n").length;
|
|
@@ -2495,47 +2678,51 @@ var writeFileTool = {
|
|
|
2495
2678
|
};
|
|
2496
2679
|
|
|
2497
2680
|
// src/tool/builtins/edit-file.ts
|
|
2498
|
-
import { readFile as readFile6
|
|
2681
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
2499
2682
|
import { basename as basename2 } from "path";
|
|
2500
|
-
var editFileSchema = {
|
|
2501
|
-
type: "object",
|
|
2502
|
-
properties: {
|
|
2503
|
-
path: {
|
|
2504
|
-
type: "string",
|
|
2505
|
-
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2506
|
-
},
|
|
2507
|
-
old_text: {
|
|
2508
|
-
type: "string",
|
|
2509
|
-
description: "\u8981\u67E5\u627E\u7684\u539F\u59CB\u6587\u672C\uFF08\u7CBE\u786E\u5339\u914D\uFF09"
|
|
2510
|
-
},
|
|
2511
|
-
new_text: {
|
|
2512
|
-
type: "string",
|
|
2513
|
-
description: "\u66FF\u6362\u540E\u7684\u65B0\u6587\u672C"
|
|
2514
|
-
}
|
|
2515
|
-
},
|
|
2516
|
-
required: ["path", "old_text", "new_text"],
|
|
2517
|
-
additionalProperties: false
|
|
2518
|
-
};
|
|
2519
2683
|
var editFileTool = {
|
|
2520
2684
|
name: "edit_file",
|
|
2685
|
+
kind: "edit" /* Edit */,
|
|
2521
2686
|
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",
|
|
2522
|
-
parameters:
|
|
2523
|
-
|
|
2687
|
+
parameters: {
|
|
2688
|
+
type: "object",
|
|
2689
|
+
properties: {
|
|
2690
|
+
path: {
|
|
2691
|
+
type: "string",
|
|
2692
|
+
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2693
|
+
},
|
|
2694
|
+
old_text: {
|
|
2695
|
+
type: "string",
|
|
2696
|
+
description: "\u8981\u67E5\u627E\u7684\u539F\u59CB\u6587\u672C\uFF08\u7CBE\u786E\u5339\u914D\uFF09"
|
|
2697
|
+
},
|
|
2698
|
+
new_text: {
|
|
2699
|
+
type: "string",
|
|
2700
|
+
description: "\u66FF\u6362\u540E\u7684\u65B0\u6587\u672C"
|
|
2701
|
+
}
|
|
2702
|
+
},
|
|
2703
|
+
required: ["path", "old_text", "new_text"],
|
|
2704
|
+
additionalProperties: false
|
|
2705
|
+
},
|
|
2524
2706
|
async execute(args, ctx) {
|
|
2525
|
-
|
|
2526
|
-
if (!params?.path || typeof params.path !== "string") {
|
|
2707
|
+
if (!args?.path || typeof args.path !== "string") {
|
|
2527
2708
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 path", error: "INVALID_ARGS" };
|
|
2528
2709
|
}
|
|
2529
|
-
if (typeof
|
|
2710
|
+
if (typeof args.old_text !== "string") {
|
|
2530
2711
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 old_text", error: "INVALID_ARGS" };
|
|
2531
2712
|
}
|
|
2532
|
-
if (typeof
|
|
2713
|
+
if (typeof args.new_text !== "string") {
|
|
2533
2714
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 new_text", error: "INVALID_ARGS" };
|
|
2534
2715
|
}
|
|
2535
|
-
const filePath = resolvePath(
|
|
2716
|
+
const filePath = resolvePath(args.path, ctx.cwd);
|
|
2717
|
+
if (ctx.writeRoots && ctx.writeRoots.length > 0) {
|
|
2718
|
+
const conf = await confine(ctx.writeRoots, filePath);
|
|
2719
|
+
if (!conf.ok) {
|
|
2720
|
+
return { success: false, data: conf.error, error: "OUTSIDE_WRITE_ROOTS" };
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2536
2723
|
try {
|
|
2537
2724
|
const content = await readFile6(filePath, "utf-8");
|
|
2538
|
-
const firstIndex = content.indexOf(
|
|
2725
|
+
const firstIndex = content.indexOf(args.old_text);
|
|
2539
2726
|
if (firstIndex === -1) {
|
|
2540
2727
|
return {
|
|
2541
2728
|
success: false,
|
|
@@ -2543,7 +2730,7 @@ var editFileTool = {
|
|
|
2543
2730
|
error: "TEXT_NOT_FOUND"
|
|
2544
2731
|
};
|
|
2545
2732
|
}
|
|
2546
|
-
const secondIndex = content.indexOf(
|
|
2733
|
+
const secondIndex = content.indexOf(args.old_text, firstIndex + 1);
|
|
2547
2734
|
if (secondIndex !== -1) {
|
|
2548
2735
|
return {
|
|
2549
2736
|
success: false,
|
|
@@ -2551,14 +2738,14 @@ var editFileTool = {
|
|
|
2551
2738
|
error: "TEXT_MULTIPLE_MATCHES"
|
|
2552
2739
|
};
|
|
2553
2740
|
}
|
|
2554
|
-
const newContent = content.replace(
|
|
2555
|
-
await
|
|
2741
|
+
const newContent = content.replace(args.old_text, args.new_text);
|
|
2742
|
+
await writeFileWithEol(filePath, content, newContent);
|
|
2556
2743
|
const diff = computeFileDiff(content, newContent, filePath);
|
|
2557
2744
|
diff.existedBefore = true;
|
|
2558
2745
|
const beforeText = content.slice(0, firstIndex);
|
|
2559
2746
|
const startLine = beforeText.split("\n").length;
|
|
2560
|
-
const oldLines =
|
|
2561
|
-
const newLines =
|
|
2747
|
+
const oldLines = args.old_text.split("\n").length;
|
|
2748
|
+
const newLines = args.new_text.split("\n").length;
|
|
2562
2749
|
const diffSummary = `+${diff.additions} -${diff.deletions}`;
|
|
2563
2750
|
const summary = `\u{1F4DD} \u4FEE\u6539: ${basename2(filePath)} (+${diff.additions} -${diff.deletions})`;
|
|
2564
2751
|
return {
|
|
@@ -2582,52 +2769,56 @@ ${oldLines} \u884C \u2192 ${newLines} \u884C
|
|
|
2582
2769
|
};
|
|
2583
2770
|
|
|
2584
2771
|
// src/tool/builtins/multi-edit.ts
|
|
2585
|
-
import { readFile as readFile7
|
|
2772
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
2586
2773
|
import { basename as basename3 } from "path";
|
|
2587
|
-
var multiEditSchema = {
|
|
2588
|
-
type: "object",
|
|
2589
|
-
properties: {
|
|
2590
|
-
path: {
|
|
2591
|
-
type: "string",
|
|
2592
|
-
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2593
|
-
},
|
|
2594
|
-
edits: {
|
|
2595
|
-
type: "array",
|
|
2596
|
-
description: "\u6709\u5E8F\u7684\u7F16\u8F91\u6B65\u9AA4\u5217\u8868\u3002\u6BCF\u4E2A\u6B65\u9AA4\u5305\u542B oldText\uFF08\u7CBE\u786E\u5339\u914D\uFF09\u3001newText\uFF08\u66FF\u6362\u5185\u5BB9\uFF09\u548C\u53EF\u9009\u7684 replaceAll\uFF08\u662F\u5426\u66FF\u6362\u6240\u6709\u5339\u914D\u9879\uFF09",
|
|
2597
|
-
items: {
|
|
2598
|
-
type: "object",
|
|
2599
|
-
properties: {
|
|
2600
|
-
oldText: { type: "string", description: "\u8981\u67E5\u627E\u7684\u539F\u59CB\u6587\u672C\uFF08\u7CBE\u786E\u5339\u914D\uFF09" },
|
|
2601
|
-
newText: { type: "string", description: "\u66FF\u6362\u540E\u7684\u65B0\u6587\u672C" },
|
|
2602
|
-
replaceAll: { type: "boolean", description: "\u662F\u5426\u66FF\u6362\u6240\u6709\u5339\u914D\u9879\uFF0C\u9ED8\u8BA4 false" }
|
|
2603
|
-
},
|
|
2604
|
-
required: ["oldText", "newText"],
|
|
2605
|
-
additionalProperties: false
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
},
|
|
2609
|
-
required: ["path", "edits"],
|
|
2610
|
-
additionalProperties: false
|
|
2611
|
-
};
|
|
2612
2774
|
var multiEditTool = {
|
|
2613
2775
|
name: "multi_edit",
|
|
2776
|
+
kind: "edit" /* Edit */,
|
|
2614
2777
|
description: "\u5BF9\u6587\u4EF6\u8FDB\u884C\u539F\u5B50\u6279\u91CF\u66FF\u6362\u7F16\u8F91\u3002\u5728\u4E00\u4E2A\u8BF7\u6C42\u4E2D\u6267\u884C\u591A\u4E2A\u7CBE\u786E\u66FF\u6362\uFF0C\u4EFB\u4E00\u5931\u8D25\u5219\u5168\u90E8\u56DE\u6EDA\u3002\u652F\u6301 replaceAll \u53C2\u6570\u505A\u5168\u5C40\u66FF\u6362\u3002\u9002\u7528\u4E8E\u9700\u8981\u540C\u65F6\u4FEE\u6539\u6587\u4EF6\u591A\u5904\u7684\u573A\u666F\u3002",
|
|
2615
|
-
parameters:
|
|
2616
|
-
|
|
2778
|
+
parameters: {
|
|
2779
|
+
type: "object",
|
|
2780
|
+
properties: {
|
|
2781
|
+
path: {
|
|
2782
|
+
type: "string",
|
|
2783
|
+
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2784
|
+
},
|
|
2785
|
+
edits: {
|
|
2786
|
+
type: "array",
|
|
2787
|
+
description: "\u6709\u5E8F\u7684\u7F16\u8F91\u6B65\u9AA4\u5217\u8868\u3002\u6BCF\u4E2A\u6B65\u9AA4\u5305\u542B oldText\uFF08\u7CBE\u786E\u5339\u914D\uFF09\u3001newText\uFF08\u66FF\u6362\u5185\u5BB9\uFF09\u548C\u53EF\u9009\u7684 replaceAll\uFF08\u662F\u5426\u66FF\u6362\u6240\u6709\u5339\u914D\u9879\uFF09",
|
|
2788
|
+
items: {
|
|
2789
|
+
type: "object",
|
|
2790
|
+
properties: {
|
|
2791
|
+
oldText: { type: "string", description: "\u8981\u67E5\u627E\u7684\u539F\u59CB\u6587\u672C\uFF08\u7CBE\u786E\u5339\u914D\uFF09" },
|
|
2792
|
+
newText: { type: "string", description: "\u66FF\u6362\u540E\u7684\u65B0\u6587\u672C" },
|
|
2793
|
+
replaceAll: { type: "boolean", description: "\u662F\u5426\u66FF\u6362\u6240\u6709\u5339\u914D\u9879\uFF0C\u9ED8\u8BA4 false" }
|
|
2794
|
+
},
|
|
2795
|
+
required: ["oldText", "newText"],
|
|
2796
|
+
additionalProperties: false
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
},
|
|
2800
|
+
required: ["path", "edits"],
|
|
2801
|
+
additionalProperties: false
|
|
2802
|
+
},
|
|
2617
2803
|
async execute(args, ctx) {
|
|
2618
|
-
|
|
2619
|
-
if (!params?.path || typeof params.path !== "string") {
|
|
2804
|
+
if (!args?.path || typeof args.path !== "string") {
|
|
2620
2805
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 path", error: "INVALID_ARGS" };
|
|
2621
2806
|
}
|
|
2622
|
-
if (!Array.isArray(
|
|
2807
|
+
if (!Array.isArray(args.edits) || args.edits.length === 0) {
|
|
2623
2808
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 edits\uFF08\u975E\u7A7A\u6570\u7EC4\uFF09", error: "INVALID_ARGS" };
|
|
2624
2809
|
}
|
|
2625
|
-
const filePath = resolvePath(
|
|
2810
|
+
const filePath = resolvePath(args.path, ctx.cwd);
|
|
2811
|
+
if (ctx.writeRoots && ctx.writeRoots.length > 0) {
|
|
2812
|
+
const conf = await confine(ctx.writeRoots, filePath);
|
|
2813
|
+
if (!conf.ok) {
|
|
2814
|
+
return { success: false, data: conf.error, error: "OUTSIDE_WRITE_ROOTS" };
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2626
2817
|
try {
|
|
2627
2818
|
const originalContent = await readFile7(filePath, "utf-8");
|
|
2628
2819
|
let currentContent = originalContent;
|
|
2629
|
-
for (let idx = 0; idx <
|
|
2630
|
-
const step =
|
|
2820
|
+
for (let idx = 0; idx < args.edits.length; idx++) {
|
|
2821
|
+
const step = args.edits[idx];
|
|
2631
2822
|
if (typeof step.oldText !== "string") {
|
|
2632
2823
|
return {
|
|
2633
2824
|
success: false,
|
|
@@ -2669,15 +2860,15 @@ var multiEditTool = {
|
|
|
2669
2860
|
currentContent = currentContent.replace(step.oldText, step.newText);
|
|
2670
2861
|
}
|
|
2671
2862
|
}
|
|
2672
|
-
await
|
|
2863
|
+
await writeFileWithEol(filePath, originalContent, currentContent);
|
|
2673
2864
|
const diff = computeFileDiff(originalContent, currentContent, filePath);
|
|
2674
2865
|
diff.existedBefore = true;
|
|
2675
2866
|
return {
|
|
2676
2867
|
success: true,
|
|
2677
2868
|
data: `\u6587\u4EF6\u5DF2\u7F16\u8F91\uFF1A${filePath}
|
|
2678
|
-
\u5171\u6267\u884C ${
|
|
2869
|
+
\u5171\u6267\u884C ${args.edits.length} \u6B65\u66FF\u6362
|
|
2679
2870
|
\u53D8\u66F4\uFF1A+${diff.additions} -${diff.deletions}`,
|
|
2680
|
-
summary: `\u{1F4DD} \u4FEE\u6539: ${basename3(filePath)} (${
|
|
2871
|
+
summary: `\u{1F4DD} \u4FEE\u6539: ${basename3(filePath)} (${args.edits.length} \u6B65, +${diff.additions} -${diff.deletions})`,
|
|
2681
2872
|
diff
|
|
2682
2873
|
};
|
|
2683
2874
|
} catch (err) {
|
|
@@ -2692,31 +2883,8 @@ var multiEditTool = {
|
|
|
2692
2883
|
};
|
|
2693
2884
|
|
|
2694
2885
|
// src/tool/builtins/delete-range.ts
|
|
2695
|
-
import { readFile as readFile8
|
|
2886
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
2696
2887
|
import { basename as basename4 } from "path";
|
|
2697
|
-
var deleteRangeSchema = {
|
|
2698
|
-
type: "object",
|
|
2699
|
-
properties: {
|
|
2700
|
-
path: {
|
|
2701
|
-
type: "string",
|
|
2702
|
-
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2703
|
-
},
|
|
2704
|
-
startAnchor: {
|
|
2705
|
-
type: "string",
|
|
2706
|
-
description: "\u5220\u9664\u8303\u56F4\u7684\u8D77\u59CB\u6807\u8BB0\u884C\u5185\u5BB9\uFF08\u5FC5\u987B\u5728\u6587\u4EF6\u4E2D\u552F\u4E00\uFF0C\u7CBE\u786E\u5339\u914D\u6574\u884C\uFF09"
|
|
2707
|
-
},
|
|
2708
|
-
endAnchor: {
|
|
2709
|
-
type: "string",
|
|
2710
|
-
description: "\u5220\u9664\u8303\u56F4\u7684\u7ED3\u675F\u6807\u8BB0\u884C\u5185\u5BB9\uFF08\u5FC5\u987B\u5728\u6587\u4EF6\u4E2D\u552F\u4E00\uFF0C\u7CBE\u786E\u5339\u914D\u6574\u884C\uFF0C\u987B\u5728 start_anchor \u4E4B\u540E\uFF09"
|
|
2711
|
-
},
|
|
2712
|
-
inclusive: {
|
|
2713
|
-
type: "boolean",
|
|
2714
|
-
description: "\u662F\u5426\u5305\u542B\u951A\u70B9\u884C\u672C\u8EAB\uFF0C\u9ED8\u8BA4 false\uFF08\u53EA\u5220\u9664\u4E24\u884C\u4E4B\u95F4\u7684\u5185\u5BB9\uFF09"
|
|
2715
|
-
}
|
|
2716
|
-
},
|
|
2717
|
-
required: ["path", "startAnchor", "endAnchor"],
|
|
2718
|
-
additionalProperties: false
|
|
2719
|
-
};
|
|
2720
2888
|
function findUniqueLine(lines, anchor, label) {
|
|
2721
2889
|
const matches = [];
|
|
2722
2890
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -2734,30 +2902,57 @@ function findUniqueLine(lines, anchor, label) {
|
|
|
2734
2902
|
}
|
|
2735
2903
|
var deleteRangeTool = {
|
|
2736
2904
|
name: "delete_range",
|
|
2905
|
+
kind: "edit" /* Edit */,
|
|
2737
2906
|
description: "\u5220\u9664\u6587\u4EF6\u4E2D\u4E24\u4E2A\u951A\u70B9\u884C\u4E4B\u95F4\u7684\u5185\u5BB9\u3002\u901A\u8FC7\u4E24\u4E2A\u552F\u4E00\u7684\u884C\u5185\u5BB9\u7CBE\u786E\u5B9A\u4F4D\u8303\u56F4\u3002\u9002\u7528\u4E8E\u5220\u9664\u65B9\u6CD5\u3001\u4EE3\u7801\u5757\u3001\u914D\u7F6E\u6BB5\u7B49\u3002",
|
|
2738
|
-
parameters:
|
|
2739
|
-
|
|
2907
|
+
parameters: {
|
|
2908
|
+
type: "object",
|
|
2909
|
+
properties: {
|
|
2910
|
+
path: {
|
|
2911
|
+
type: "string",
|
|
2912
|
+
description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
|
|
2913
|
+
},
|
|
2914
|
+
startAnchor: {
|
|
2915
|
+
type: "string",
|
|
2916
|
+
description: "\u5220\u9664\u8303\u56F4\u7684\u8D77\u59CB\u6807\u8BB0\u884C\u5185\u5BB9\uFF08\u5FC5\u987B\u5728\u6587\u4EF6\u4E2D\u552F\u4E00\uFF0C\u7CBE\u786E\u5339\u914D\u6574\u884C\uFF09"
|
|
2917
|
+
},
|
|
2918
|
+
endAnchor: {
|
|
2919
|
+
type: "string",
|
|
2920
|
+
description: "\u5220\u9664\u8303\u56F4\u7684\u7ED3\u675F\u6807\u8BB0\u884C\u5185\u5BB9\uFF08\u5FC5\u987B\u5728\u6587\u4EF6\u4E2D\u552F\u4E00\uFF0C\u7CBE\u786E\u5339\u914D\u6574\u884C\uFF0C\u987B\u5728 start_anchor \u4E4B\u540E\uFF09"
|
|
2921
|
+
},
|
|
2922
|
+
inclusive: {
|
|
2923
|
+
type: "boolean",
|
|
2924
|
+
description: "\u662F\u5426\u5305\u542B\u951A\u70B9\u884C\u672C\u8EAB\uFF0C\u9ED8\u8BA4 false\uFF08\u53EA\u5220\u9664\u4E24\u884C\u4E4B\u95F4\u7684\u5185\u5BB9\uFF09"
|
|
2925
|
+
}
|
|
2926
|
+
},
|
|
2927
|
+
required: ["path", "startAnchor", "endAnchor"],
|
|
2928
|
+
additionalProperties: false
|
|
2929
|
+
},
|
|
2740
2930
|
async execute(args, ctx) {
|
|
2741
|
-
|
|
2742
|
-
if (!params?.path || typeof params.path !== "string") {
|
|
2931
|
+
if (!args?.path || typeof args.path !== "string") {
|
|
2743
2932
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 path", error: "INVALID_ARGS" };
|
|
2744
2933
|
}
|
|
2745
|
-
if (typeof
|
|
2934
|
+
if (typeof args.startAnchor !== "string") {
|
|
2746
2935
|
return { success: false, data: "\u7F3A\u5C11\u6709\u6548\u53C2\u6570 startAnchor", error: "INVALID_ARGS" };
|
|
2747
2936
|
}
|
|
2748
|
-
if (typeof
|
|
2937
|
+
if (typeof args.endAnchor !== "string") {
|
|
2749
2938
|
return { success: false, data: "\u7F3A\u5C11\u6709\u6548\u53C2\u6570 endAnchor", error: "INVALID_ARGS" };
|
|
2750
2939
|
}
|
|
2751
|
-
const filePath = resolvePath(
|
|
2752
|
-
const inclusive =
|
|
2940
|
+
const filePath = resolvePath(args.path, ctx.cwd);
|
|
2941
|
+
const inclusive = args.inclusive ?? false;
|
|
2942
|
+
if (ctx.writeRoots && ctx.writeRoots.length > 0) {
|
|
2943
|
+
const conf = await confine(ctx.writeRoots, filePath);
|
|
2944
|
+
if (!conf.ok) {
|
|
2945
|
+
return { success: false, data: conf.error, error: "OUTSIDE_WRITE_ROOTS" };
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2753
2948
|
try {
|
|
2754
2949
|
const content = await readFile8(filePath, "utf-8");
|
|
2755
2950
|
const lines = content.split("\n");
|
|
2756
|
-
const startResult = findUniqueLine(lines,
|
|
2951
|
+
const startResult = findUniqueLine(lines, args.startAnchor, "start_anchor");
|
|
2757
2952
|
if ("error" in startResult) {
|
|
2758
2953
|
return { success: false, data: startResult.error, error: "ANCHOR_NOT_FOUND" };
|
|
2759
2954
|
}
|
|
2760
|
-
const endResult = findUniqueLine(lines,
|
|
2955
|
+
const endResult = findUniqueLine(lines, args.endAnchor, "end_anchor");
|
|
2761
2956
|
if ("error" in endResult) {
|
|
2762
2957
|
return { success: false, data: endResult.error, error: "ANCHOR_NOT_FOUND" };
|
|
2763
2958
|
}
|
|
@@ -2781,14 +2976,8 @@ var deleteRangeTool = {
|
|
|
2781
2976
|
}
|
|
2782
2977
|
const newLines = [...lines.slice(0, rangeStart), ...lines.slice(rangeEnd + 1)];
|
|
2783
2978
|
const newContent = newLines.join("\n");
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
writeContent = newContent + "\n";
|
|
2787
|
-
} else if (content.endsWith("\r\n") && !newContent.endsWith("\r\n") && !newContent.endsWith("\n")) {
|
|
2788
|
-
writeContent = newContent + "\r\n";
|
|
2789
|
-
}
|
|
2790
|
-
await writeFile6(filePath, writeContent, "utf-8");
|
|
2791
|
-
const diff = computeFileDiff(content, writeContent, filePath);
|
|
2979
|
+
await writeFileWithEol(filePath, content, newContent);
|
|
2980
|
+
const diff = computeFileDiff(content, newContent, filePath);
|
|
2792
2981
|
diff.existedBefore = true;
|
|
2793
2982
|
const deletedLines = rangeEnd - rangeStart + 1;
|
|
2794
2983
|
const summary = `\u{1F4DD} \u4FEE\u6539: ${basename4(filePath)} (\u5220 ${deletedLines} \u884C, +${diff.additions} -${diff.deletions})`;
|
|
@@ -2814,36 +3003,33 @@ var deleteRangeTool = {
|
|
|
2814
3003
|
// src/tool/builtins/bash.ts
|
|
2815
3004
|
import process3 from "process";
|
|
2816
3005
|
var isWindows2 = process3.platform === "win32";
|
|
2817
|
-
var bashSchema = {
|
|
2818
|
-
type: "object",
|
|
2819
|
-
properties: {
|
|
2820
|
-
command: {
|
|
2821
|
-
type: "string",
|
|
2822
|
-
description: "\u8981\u6267\u884C\u7684 shell \u547D\u4EE4"
|
|
2823
|
-
},
|
|
2824
|
-
timeout: {
|
|
2825
|
-
type: "number",
|
|
2826
|
-
description: "\u6267\u884C\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 30000"
|
|
2827
|
-
}
|
|
2828
|
-
},
|
|
2829
|
-
required: ["command"],
|
|
2830
|
-
additionalProperties: false
|
|
2831
|
-
};
|
|
2832
3006
|
var bashTool = {
|
|
2833
3007
|
name: "bash",
|
|
3008
|
+
kind: "other" /* Other */,
|
|
2834
3009
|
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",
|
|
2835
|
-
parameters:
|
|
2836
|
-
|
|
2837
|
-
|
|
3010
|
+
parameters: {
|
|
3011
|
+
type: "object",
|
|
3012
|
+
properties: {
|
|
3013
|
+
command: {
|
|
3014
|
+
type: "string",
|
|
3015
|
+
description: "\u8981\u6267\u884C\u7684 shell \u547D\u4EE4"
|
|
3016
|
+
},
|
|
3017
|
+
timeout: {
|
|
3018
|
+
type: "number",
|
|
3019
|
+
description: "\u6267\u884C\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 30000"
|
|
3020
|
+
}
|
|
3021
|
+
},
|
|
3022
|
+
required: ["command"],
|
|
3023
|
+
additionalProperties: false
|
|
3024
|
+
},
|
|
2838
3025
|
async execute(args, ctx) {
|
|
2839
|
-
|
|
2840
|
-
if (!params?.command || typeof params.command !== "string") {
|
|
3026
|
+
if (!args?.command || typeof args.command !== "string") {
|
|
2841
3027
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 command", error: "INVALID_ARGS" };
|
|
2842
3028
|
}
|
|
2843
|
-
const timeout =
|
|
3029
|
+
const timeout = args.timeout ?? ctx.timeout ?? getDefaultTimeout();
|
|
2844
3030
|
try {
|
|
2845
3031
|
const shellCommand = isWindows2 ? "cmd" : "sh";
|
|
2846
|
-
const shellArgs = isWindows2 ? ["/c",
|
|
3032
|
+
const shellArgs = isWindows2 ? ["/c", args.command] : ["-c", args.command];
|
|
2847
3033
|
const result = await execCommand(
|
|
2848
3034
|
shellCommand,
|
|
2849
3035
|
shellArgs,
|
|
@@ -2851,7 +3037,6 @@ var bashTool = {
|
|
|
2851
3037
|
timeout,
|
|
2852
3038
|
ctx.signal,
|
|
2853
3039
|
true
|
|
2854
|
-
// isShellCommand — 已指定 shell 程序,不需要二次包装
|
|
2855
3040
|
);
|
|
2856
3041
|
const parts = [];
|
|
2857
3042
|
if (result.stdout) {
|
|
@@ -2863,7 +3048,7 @@ ${truncateOutput(result.stderr)}`);
|
|
|
2863
3048
|
}
|
|
2864
3049
|
const success = result.exitCode === 0;
|
|
2865
3050
|
const output = parts.length > 0 ? parts.join("\n") : "(\u65E0\u8F93\u51FA)";
|
|
2866
|
-
const cmdPreview =
|
|
3051
|
+
const cmdPreview = args.command.length > 60 ? args.command.slice(0, 57) + "..." : args.command;
|
|
2867
3052
|
const summary = `\u{1F527} $ ${cmdPreview}\uFF08exit ${result.exitCode ?? "\u672A\u77E5"}\uFF09`;
|
|
2868
3053
|
return {
|
|
2869
3054
|
success,
|
|
@@ -2886,21 +3071,6 @@ ${truncateOutput(result.stderr)}`);
|
|
|
2886
3071
|
// src/tool/builtins/glob.ts
|
|
2887
3072
|
import { readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
2888
3073
|
import { join as join4, relative as relative4, isAbsolute as isAbsolute2 } from "path";
|
|
2889
|
-
var globSchema = {
|
|
2890
|
-
type: "object",
|
|
2891
|
-
properties: {
|
|
2892
|
-
pattern: {
|
|
2893
|
-
type: "string",
|
|
2894
|
-
description: "\u641C\u7D22\u6A21\u5F0F\uFF08\u652F\u6301 * \u548C ** \u901A\u914D\u7B26\uFF0C\u5982 **/*.ts\u3001src/**/*.test.ts\uFF09"
|
|
2895
|
-
},
|
|
2896
|
-
directory: {
|
|
2897
|
-
type: "string",
|
|
2898
|
-
description: "\u641C\u7D22\u7684\u8D77\u59CB\u76EE\u5F55\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55"
|
|
2899
|
-
}
|
|
2900
|
-
},
|
|
2901
|
-
required: ["pattern"],
|
|
2902
|
-
additionalProperties: false
|
|
2903
|
-
};
|
|
2904
3074
|
function globToRegex(pattern) {
|
|
2905
3075
|
let regexStr = pattern;
|
|
2906
3076
|
regexStr = regexStr.replace(/\*\*\//g, "<<GLOBSTAR_SLASH>>");
|
|
@@ -2937,16 +3107,29 @@ async function walkDir(dir, baseDir) {
|
|
|
2937
3107
|
}
|
|
2938
3108
|
var globTool = {
|
|
2939
3109
|
name: "glob",
|
|
3110
|
+
kind: "read" /* Read */,
|
|
2940
3111
|
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",
|
|
2941
|
-
parameters:
|
|
2942
|
-
|
|
3112
|
+
parameters: {
|
|
3113
|
+
type: "object",
|
|
3114
|
+
properties: {
|
|
3115
|
+
pattern: {
|
|
3116
|
+
type: "string",
|
|
3117
|
+
description: "\u641C\u7D22\u6A21\u5F0F\uFF08\u652F\u6301 * \u548C ** \u901A\u914D\u7B26\uFF0C\u5982 **/*.ts\u3001src/**/*.test.ts\uFF09"
|
|
3118
|
+
},
|
|
3119
|
+
directory: {
|
|
3120
|
+
type: "string",
|
|
3121
|
+
description: "\u641C\u7D22\u7684\u8D77\u59CB\u76EE\u5F55\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55"
|
|
3122
|
+
}
|
|
3123
|
+
},
|
|
3124
|
+
required: ["pattern"],
|
|
3125
|
+
additionalProperties: false
|
|
3126
|
+
},
|
|
2943
3127
|
async execute(args, ctx) {
|
|
2944
|
-
|
|
2945
|
-
if (!params?.pattern || typeof params.pattern !== "string") {
|
|
3128
|
+
if (!args?.pattern || typeof args.pattern !== "string") {
|
|
2946
3129
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
|
|
2947
3130
|
}
|
|
2948
|
-
const searchDir =
|
|
2949
|
-
const regex = globToRegex(
|
|
3131
|
+
const searchDir = args.directory ? isAbsolute2(args.directory) ? args.directory : join4(ctx.cwd, args.directory) : ctx.cwd;
|
|
3132
|
+
const regex = globToRegex(args.pattern);
|
|
2950
3133
|
try {
|
|
2951
3134
|
const dirStat = await stat2(searchDir);
|
|
2952
3135
|
if (!dirStat.isDirectory()) {
|
|
@@ -2957,8 +3140,8 @@ var globTool = {
|
|
|
2957
3140
|
if (matched.length === 0) {
|
|
2958
3141
|
return {
|
|
2959
3142
|
success: true,
|
|
2960
|
-
data: `\u672A\u627E\u5230\u5339\u914D "${
|
|
2961
|
-
summary: `${
|
|
3143
|
+
data: `\u672A\u627E\u5230\u5339\u914D "${args.pattern}" \u7684\u6587\u4EF6`,
|
|
3144
|
+
summary: `${args.pattern} \u2192 0 \u4E2A\u6587\u4EF6`
|
|
2962
3145
|
};
|
|
2963
3146
|
}
|
|
2964
3147
|
const limit = 200;
|
|
@@ -2970,7 +3153,7 @@ var globTool = {
|
|
|
2970
3153
|
return {
|
|
2971
3154
|
success: true,
|
|
2972
3155
|
data: truncateOutput(output + suffix),
|
|
2973
|
-
summary: `${
|
|
3156
|
+
summary: `${args.pattern} \u2192 ${matched.length} \u4E2A\u6587\u4EF6`
|
|
2974
3157
|
};
|
|
2975
3158
|
} catch (err) {
|
|
2976
3159
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -2986,33 +3169,6 @@ var globTool = {
|
|
|
2986
3169
|
// src/tool/builtins/grep.ts
|
|
2987
3170
|
import { readdir as readdir3, readFile as readFile9, stat as stat3 } from "fs/promises";
|
|
2988
3171
|
import { join as join5, relative as relative5, isAbsolute as isAbsolute3 } from "path";
|
|
2989
|
-
var grepSchema = {
|
|
2990
|
-
type: "object",
|
|
2991
|
-
properties: {
|
|
2992
|
-
pattern: {
|
|
2993
|
-
type: "string",
|
|
2994
|
-
description: "\u6B63\u5219\u8868\u8FBE\u5F0F\u641C\u7D22\u6A21\u5F0F"
|
|
2995
|
-
},
|
|
2996
|
-
directory: {
|
|
2997
|
-
type: "string",
|
|
2998
|
-
description: "\u641C\u7D22\u7684\u76EE\u5F55\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55"
|
|
2999
|
-
},
|
|
3000
|
-
include: {
|
|
3001
|
-
type: "string",
|
|
3002
|
-
description: "\u6587\u4EF6\u6269\u5C55\u540D\u8FC7\u6EE4\uFF08\u5982 ts\u3001json\uFF09\uFF0C\u4E0D\u542B\u70B9\u53F7"
|
|
3003
|
-
},
|
|
3004
|
-
case_sensitive: {
|
|
3005
|
-
type: "boolean",
|
|
3006
|
-
description: "\u662F\u5426\u5927\u5C0F\u5199\u654F\u611F\uFF0C\u9ED8\u8BA4 false"
|
|
3007
|
-
},
|
|
3008
|
-
max_files: {
|
|
3009
|
-
type: "number",
|
|
3010
|
-
description: "\u6700\u5927\u641C\u7D22\u6587\u4EF6\u6570\uFF0C\u9ED8\u8BA4 200"
|
|
3011
|
-
}
|
|
3012
|
-
},
|
|
3013
|
-
required: ["pattern"],
|
|
3014
|
-
additionalProperties: false
|
|
3015
|
-
};
|
|
3016
3172
|
async function collectFiles(dir, baseDir, extension, maxFiles = 200) {
|
|
3017
3173
|
const results = [];
|
|
3018
3174
|
let entries;
|
|
@@ -3040,24 +3196,49 @@ async function collectFiles(dir, baseDir, extension, maxFiles = 200) {
|
|
|
3040
3196
|
}
|
|
3041
3197
|
var grepTool = {
|
|
3042
3198
|
name: "grep",
|
|
3199
|
+
kind: "read" /* Read */,
|
|
3043
3200
|
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",
|
|
3044
|
-
parameters:
|
|
3045
|
-
|
|
3201
|
+
parameters: {
|
|
3202
|
+
type: "object",
|
|
3203
|
+
properties: {
|
|
3204
|
+
pattern: {
|
|
3205
|
+
type: "string",
|
|
3206
|
+
description: "\u6B63\u5219\u8868\u8FBE\u5F0F\u641C\u7D22\u6A21\u5F0F"
|
|
3207
|
+
},
|
|
3208
|
+
directory: {
|
|
3209
|
+
type: "string",
|
|
3210
|
+
description: "\u641C\u7D22\u7684\u76EE\u5F55\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55"
|
|
3211
|
+
},
|
|
3212
|
+
include: {
|
|
3213
|
+
type: "string",
|
|
3214
|
+
description: "\u6587\u4EF6\u6269\u5C55\u540D\u8FC7\u6EE4\uFF08\u5982 ts\u3001json\uFF09\uFF0C\u4E0D\u542B\u70B9\u53F7"
|
|
3215
|
+
},
|
|
3216
|
+
case_sensitive: {
|
|
3217
|
+
type: "boolean",
|
|
3218
|
+
description: "\u662F\u5426\u5927\u5C0F\u5199\u654F\u611F\uFF0C\u9ED8\u8BA4 false"
|
|
3219
|
+
},
|
|
3220
|
+
max_files: {
|
|
3221
|
+
type: "number",
|
|
3222
|
+
description: "\u6700\u5927\u641C\u7D22\u6587\u4EF6\u6570\uFF0C\u9ED8\u8BA4 200"
|
|
3223
|
+
}
|
|
3224
|
+
},
|
|
3225
|
+
required: ["pattern"],
|
|
3226
|
+
additionalProperties: false
|
|
3227
|
+
},
|
|
3046
3228
|
async execute(args, ctx) {
|
|
3047
|
-
|
|
3048
|
-
if (!params?.pattern || typeof params.pattern !== "string") {
|
|
3229
|
+
if (!args?.pattern || typeof args.pattern !== "string") {
|
|
3049
3230
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
|
|
3050
3231
|
}
|
|
3051
|
-
const searchDir =
|
|
3052
|
-
const maxFiles =
|
|
3232
|
+
const searchDir = args.directory ? isAbsolute3(args.directory) ? args.directory : join5(ctx.cwd, args.directory) : ctx.cwd;
|
|
3233
|
+
const maxFiles = args.max_files ?? 200;
|
|
3053
3234
|
try {
|
|
3054
|
-
const flags =
|
|
3055
|
-
const regex = new RegExp(
|
|
3235
|
+
const flags = args.case_sensitive ? "g" : "gi";
|
|
3236
|
+
const regex = new RegExp(args.pattern, flags);
|
|
3056
3237
|
const dirStat = await stat3(searchDir);
|
|
3057
3238
|
if (!dirStat.isDirectory()) {
|
|
3058
3239
|
return { success: false, data: `\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${searchDir}`, error: "NOT_DIRECTORY" };
|
|
3059
3240
|
}
|
|
3060
|
-
const files = await collectFiles(searchDir, searchDir,
|
|
3241
|
+
const files = await collectFiles(searchDir, searchDir, args.include, maxFiles);
|
|
3061
3242
|
const matches = [];
|
|
3062
3243
|
for (const filePath of files) {
|
|
3063
3244
|
try {
|
|
@@ -3083,13 +3264,13 @@ var grepTool = {
|
|
|
3083
3264
|
if (matches.length === 0) {
|
|
3084
3265
|
return {
|
|
3085
3266
|
success: true,
|
|
3086
|
-
data: `\u672A\u627E\u5230\u5339\u914D "${
|
|
3087
|
-
summary: `\u{1F50D} "${
|
|
3267
|
+
data: `\u672A\u627E\u5230\u5339\u914D "${args.pattern}" \u7684\u5185\u5BB9`,
|
|
3268
|
+
summary: `\u{1F50D} "${args.pattern}" \u2192 0 \u6761\u547D\u4E2D`
|
|
3088
3269
|
};
|
|
3089
3270
|
}
|
|
3090
3271
|
const output = matches.map((m) => `${m.file}:${m.line}: ${m.content}`).join("\n");
|
|
3091
3272
|
const fileSet = new Set(matches.map((m) => m.file));
|
|
3092
|
-
const summary = `\u{1F50D} "${
|
|
3273
|
+
const summary = `\u{1F50D} "${args.pattern}" \u2192 ${matches.length} \u6761\u547D\u4E2D / ${fileSet.size} \u4E2A\u6587\u4EF6`;
|
|
3093
3274
|
return {
|
|
3094
3275
|
success: true,
|
|
3095
3276
|
data: truncateOutput(output),
|
|
@@ -3109,30 +3290,28 @@ var grepTool = {
|
|
|
3109
3290
|
// src/tool/builtins/ls.ts
|
|
3110
3291
|
import { readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
3111
3292
|
import { join as join6, relative as relative6 } from "path";
|
|
3112
|
-
var lsSchema = {
|
|
3113
|
-
type: "object",
|
|
3114
|
-
properties: {
|
|
3115
|
-
path: {
|
|
3116
|
-
type: "string",
|
|
3117
|
-
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"
|
|
3118
|
-
},
|
|
3119
|
-
all: {
|
|
3120
|
-
type: "boolean",
|
|
3121
|
-
description: "\u662F\u5426\u663E\u793A\u9690\u85CF\u6587\u4EF6\uFF08\u4EE5 . \u5F00\u5934\u7684\u6587\u4EF6\uFF09\uFF0C\u9ED8\u8BA4 false"
|
|
3122
|
-
}
|
|
3123
|
-
},
|
|
3124
|
-
required: [],
|
|
3125
|
-
additionalProperties: false
|
|
3126
|
-
};
|
|
3127
3293
|
var lsTool = {
|
|
3128
3294
|
name: "ls",
|
|
3295
|
+
kind: "read" /* Read */,
|
|
3129
3296
|
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",
|
|
3130
|
-
parameters:
|
|
3131
|
-
|
|
3297
|
+
parameters: {
|
|
3298
|
+
type: "object",
|
|
3299
|
+
properties: {
|
|
3300
|
+
path: {
|
|
3301
|
+
type: "string",
|
|
3302
|
+
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"
|
|
3303
|
+
},
|
|
3304
|
+
all: {
|
|
3305
|
+
type: "boolean",
|
|
3306
|
+
description: "\u662F\u5426\u663E\u793A\u9690\u85CF\u6587\u4EF6\uFF08\u4EE5 . \u5F00\u5934\u7684\u6587\u4EF6\uFF09\uFF0C\u9ED8\u8BA4 false"
|
|
3307
|
+
}
|
|
3308
|
+
},
|
|
3309
|
+
required: [],
|
|
3310
|
+
additionalProperties: false
|
|
3311
|
+
},
|
|
3132
3312
|
async execute(args, ctx) {
|
|
3133
|
-
const
|
|
3134
|
-
const
|
|
3135
|
-
const showAll = params.all ?? false;
|
|
3313
|
+
const dirPath = args.path ? resolvePath(args.path, ctx.cwd) : ctx.cwd;
|
|
3314
|
+
const showAll = args.all ?? false;
|
|
3136
3315
|
try {
|
|
3137
3316
|
const entries = await readdir4(dirPath, { withFileTypes: true });
|
|
3138
3317
|
const lines = [];
|
|
@@ -3185,58 +3364,56 @@ ${lines.join("\n")}`),
|
|
|
3185
3364
|
};
|
|
3186
3365
|
|
|
3187
3366
|
// src/tool/builtins/fetch.ts
|
|
3188
|
-
var fetchSchema = {
|
|
3189
|
-
type: "object",
|
|
3190
|
-
properties: {
|
|
3191
|
-
url: {
|
|
3192
|
-
type: "string",
|
|
3193
|
-
description: "\u8BF7\u6C42\u7684 URL"
|
|
3194
|
-
},
|
|
3195
|
-
method: {
|
|
3196
|
-
type: "string",
|
|
3197
|
-
description: "HTTP \u65B9\u6CD5\uFF08GET\u3001POST\u3001PUT\u3001DELETE \u7B49\uFF09\uFF0C\u9ED8\u8BA4 GET"
|
|
3198
|
-
},
|
|
3199
|
-
headers: {
|
|
3200
|
-
type: "object",
|
|
3201
|
-
description: "\u8BF7\u6C42\u5934\u952E\u503C\u5BF9",
|
|
3202
|
-
additionalProperties: { type: "string" }
|
|
3203
|
-
},
|
|
3204
|
-
body: {
|
|
3205
|
-
type: "string",
|
|
3206
|
-
description: "\u8BF7\u6C42\u4F53\u5185\u5BB9\uFF08POST/PUT \u65F6\u4F7F\u7528\uFF09"
|
|
3207
|
-
},
|
|
3208
|
-
max_length: {
|
|
3209
|
-
type: "number",
|
|
3210
|
-
description: "\u54CD\u5E94\u5185\u5BB9\u6700\u5927\u957F\u5EA6\uFF08\u5B57\u7B26\uFF09\uFF0C\u9ED8\u8BA4 50000"
|
|
3211
|
-
}
|
|
3212
|
-
},
|
|
3213
|
-
required: ["url"],
|
|
3214
|
-
additionalProperties: false
|
|
3215
|
-
};
|
|
3216
3367
|
var fetchTool = {
|
|
3217
3368
|
name: "fetch",
|
|
3369
|
+
kind: "read" /* Read */,
|
|
3218
3370
|
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",
|
|
3219
|
-
parameters:
|
|
3220
|
-
|
|
3371
|
+
parameters: {
|
|
3372
|
+
type: "object",
|
|
3373
|
+
properties: {
|
|
3374
|
+
url: {
|
|
3375
|
+
type: "string",
|
|
3376
|
+
description: "\u8BF7\u6C42\u7684 URL"
|
|
3377
|
+
},
|
|
3378
|
+
method: {
|
|
3379
|
+
type: "string",
|
|
3380
|
+
description: "HTTP \u65B9\u6CD5\uFF08GET\u3001POST\u3001PUT\u3001DELETE \u7B49\uFF09\uFF0C\u9ED8\u8BA4 GET"
|
|
3381
|
+
},
|
|
3382
|
+
headers: {
|
|
3383
|
+
type: "object",
|
|
3384
|
+
description: "\u8BF7\u6C42\u5934\u952E\u503C\u5BF9",
|
|
3385
|
+
additionalProperties: { type: "string" }
|
|
3386
|
+
},
|
|
3387
|
+
body: {
|
|
3388
|
+
type: "string",
|
|
3389
|
+
description: "\u8BF7\u6C42\u4F53\u5185\u5BB9\uFF08POST/PUT \u65F6\u4F7F\u7528\uFF09"
|
|
3390
|
+
},
|
|
3391
|
+
max_length: {
|
|
3392
|
+
type: "number",
|
|
3393
|
+
description: "\u54CD\u5E94\u5185\u5BB9\u6700\u5927\u957F\u5EA6\uFF08\u5B57\u7B26\uFF09\uFF0C\u9ED8\u8BA4 50000"
|
|
3394
|
+
}
|
|
3395
|
+
},
|
|
3396
|
+
required: ["url"],
|
|
3397
|
+
additionalProperties: false
|
|
3398
|
+
},
|
|
3221
3399
|
async execute(args, ctx) {
|
|
3222
|
-
|
|
3223
|
-
if (!params?.url || typeof params.url !== "string") {
|
|
3400
|
+
if (!args?.url || typeof args.url !== "string") {
|
|
3224
3401
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 url", error: "INVALID_ARGS" };
|
|
3225
3402
|
}
|
|
3226
|
-
const method = (
|
|
3403
|
+
const method = (args.method ?? "GET").toUpperCase();
|
|
3227
3404
|
const timeout = 3e4;
|
|
3228
|
-
const maxLength =
|
|
3405
|
+
const maxLength = args.max_length ?? 5e4;
|
|
3229
3406
|
try {
|
|
3230
3407
|
const fetchOptions = {
|
|
3231
3408
|
method,
|
|
3232
|
-
headers:
|
|
3409
|
+
headers: args.headers ?? {},
|
|
3233
3410
|
signal: ctx.signal ?? AbortSignal.timeout(timeout)
|
|
3234
3411
|
};
|
|
3235
|
-
if (
|
|
3412
|
+
if (args.body && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
3236
3413
|
fetchOptions.headers["Content-Type"] ??= "application/json";
|
|
3237
|
-
fetchOptions.body =
|
|
3414
|
+
fetchOptions.body = args.body;
|
|
3238
3415
|
}
|
|
3239
|
-
const response = await fetch(
|
|
3416
|
+
const response = await fetch(args.url, fetchOptions);
|
|
3240
3417
|
const contentType = response.headers.get("content-type") ?? "unknown";
|
|
3241
3418
|
const statusText = `${response.status} ${response.statusText}`;
|
|
3242
3419
|
let body;
|
|
@@ -3249,7 +3426,7 @@ var fetchTool = {
|
|
|
3249
3426
|
const header = `\u72B6\u6001: ${statusText}
|
|
3250
3427
|
\u5185\u5BB9\u7C7B\u578B: ${contentType}`;
|
|
3251
3428
|
const separator = body.length > 0 ? "\n---\n" : "";
|
|
3252
|
-
const urlPreview =
|
|
3429
|
+
const urlPreview = args.url.length > 60 ? args.url.slice(0, 57) + "..." : args.url;
|
|
3253
3430
|
const summary = `\u{1F310} ${method} ${urlPreview} \u2192 ${response.status}`;
|
|
3254
3431
|
return {
|
|
3255
3432
|
success: response.ok,
|
|
@@ -3276,16 +3453,16 @@ var fetchTool = {
|
|
|
3276
3453
|
|
|
3277
3454
|
// src/tool/builtins/index.ts
|
|
3278
3455
|
var builtinTools = [
|
|
3279
|
-
readFileTool,
|
|
3280
|
-
writeFileTool,
|
|
3281
|
-
editFileTool,
|
|
3282
|
-
multiEditTool,
|
|
3283
|
-
deleteRangeTool,
|
|
3284
|
-
bashTool,
|
|
3285
|
-
globTool,
|
|
3286
|
-
grepTool,
|
|
3287
|
-
lsTool,
|
|
3288
|
-
fetchTool
|
|
3456
|
+
eraseTool(readFileTool),
|
|
3457
|
+
eraseTool(writeFileTool),
|
|
3458
|
+
eraseTool(editFileTool),
|
|
3459
|
+
eraseTool(multiEditTool),
|
|
3460
|
+
eraseTool(deleteRangeTool),
|
|
3461
|
+
eraseTool(bashTool),
|
|
3462
|
+
eraseTool(globTool),
|
|
3463
|
+
eraseTool(grepTool),
|
|
3464
|
+
eraseTool(lsTool),
|
|
3465
|
+
eraseTool(fetchTool)
|
|
3289
3466
|
];
|
|
3290
3467
|
|
|
3291
3468
|
// src/utils/gradient.ts
|
|
@@ -3376,6 +3553,8 @@ registerCommand("/help", {
|
|
|
3376
3553
|
registerCommand("/clear", { desc: "\u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2", handler: () => ({ kind: "clear" }) });
|
|
3377
3554
|
registerCommand("/version", { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => ({ kind: "text", content: "dskcode v0.1.10" }) });
|
|
3378
3555
|
registerCommand("/model", { desc: "\u5207\u6362\u6A21\u578B", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /model \u8FDB\u5165\u9009\u62E9\u754C\u9762" }) });
|
|
3556
|
+
registerCommand("/thinking", { desc: "\u5207\u6362\u6DF1\u5EA6\u601D\u8003\u6A21\u5F0F", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /thinking \u5207\u6362" }) });
|
|
3557
|
+
registerCommand("/effort", { desc: "\u5207\u6362\u63A8\u7406\u7B49\u7EA7 High/Max", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /effort \u5207\u6362" }) });
|
|
3379
3558
|
registerCommand("/game", { desc: "\u542F\u52A8\u6E38\u620F", handler: () => ({ kind: "navigate", target: "game" }) });
|
|
3380
3559
|
registerCommand("/stock", { desc: "\u67E5\u770B\u80A1\u7968\u884C\u60C5", handler: () => ({ kind: "navigate", target: "stock" }) });
|
|
3381
3560
|
var STREAMING_PLACEHOLDERS = [
|
|
@@ -3434,6 +3613,19 @@ function ChatSession({
|
|
|
3434
3613
|
const [activeModel, setActiveModel] = useState3(model);
|
|
3435
3614
|
const [streamingModel, setStreamingModel] = useState3(void 0);
|
|
3436
3615
|
const [streamError, setStreamError] = useState3(void 0);
|
|
3616
|
+
const [thinkingEnabled, setThinkingEnabled] = useState3(true);
|
|
3617
|
+
const [thinkingEffort, setThinkingEffort] = useState3("high");
|
|
3618
|
+
const [responseFormat, setResponseFormat] = useState3("text");
|
|
3619
|
+
const [toolChoice, setToolChoice] = useState3(void 0);
|
|
3620
|
+
const sessionCost = useMemo(() => {
|
|
3621
|
+
return displayMessages.reduce((sum, msg) => {
|
|
3622
|
+
if (msg.assistantDetail?.cost) {
|
|
3623
|
+
return sum + msg.assistantDetail.cost;
|
|
3624
|
+
}
|
|
3625
|
+
return sum;
|
|
3626
|
+
}, 0);
|
|
3627
|
+
}, [displayMessages]);
|
|
3628
|
+
const hasConversationStarted = displayMessages.length > 0;
|
|
3437
3629
|
const cmdTips = Array.from(getRegisteredCommands()).filter(([name]) => name !== "/exit" && name !== "/quit").map(([name, cmd]) => ({ name, desc: cmd.desc }));
|
|
3438
3630
|
const [cmdTipIndex, setCmdTipIndex] = useState3(0);
|
|
3439
3631
|
const [cmdTipGradientColors, setCmdTipGradientColors] = useState3([]);
|
|
@@ -3565,6 +3757,11 @@ function ChatSession({
|
|
|
3565
3757
|
return;
|
|
3566
3758
|
}
|
|
3567
3759
|
}
|
|
3760
|
+
if (selectingModel) {
|
|
3761
|
+
if (key.upArrow || key.downArrow) {
|
|
3762
|
+
}
|
|
3763
|
+
return;
|
|
3764
|
+
}
|
|
3568
3765
|
if (key.ctrl && _input === "c") {
|
|
3569
3766
|
if (isStreaming) {
|
|
3570
3767
|
abortRef.current?.abort();
|
|
@@ -3584,7 +3781,7 @@ function ChatSession({
|
|
|
3584
3781
|
if (cmdTips.length <= 1) return;
|
|
3585
3782
|
const timer = setInterval(() => {
|
|
3586
3783
|
setCmdTipIndex((prev) => (prev + 1) % cmdTips.length);
|
|
3587
|
-
},
|
|
3784
|
+
}, 2e3);
|
|
3588
3785
|
return () => clearInterval(timer);
|
|
3589
3786
|
}, [cmdTips.length]);
|
|
3590
3787
|
useEffect3(() => {
|
|
@@ -3631,7 +3828,7 @@ function ChatSession({
|
|
|
3631
3828
|
if (!apiKey || !baseUrl) return;
|
|
3632
3829
|
let cancelled = false;
|
|
3633
3830
|
setBalanceLoading(true);
|
|
3634
|
-
import("./deepseek-
|
|
3831
|
+
import("./deepseek-YTT76XDE.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
|
|
3635
3832
|
const provider = new DeepSeekProvider2({
|
|
3636
3833
|
apiKey,
|
|
3637
3834
|
baseUrl,
|
|
@@ -3700,14 +3897,48 @@ function ChatSession({
|
|
|
3700
3897
|
const trimmed = value.trim();
|
|
3701
3898
|
if (!trimmed) return;
|
|
3702
3899
|
if (trimmed.startsWith("/") && trimmed.length > 1) {
|
|
3703
|
-
|
|
3900
|
+
const cmdLower = trimmed.toLowerCase();
|
|
3901
|
+
if (cmdLower === "/model") {
|
|
3704
3902
|
const curIdx = modelOptions.indexOf(activeModel);
|
|
3705
3903
|
setModelSelectIndex(curIdx >= 0 ? curIdx : 0);
|
|
3706
3904
|
setSelectingModel(true);
|
|
3707
3905
|
setInput("");
|
|
3708
3906
|
return;
|
|
3709
3907
|
}
|
|
3710
|
-
|
|
3908
|
+
if (cmdLower === "/thinking") {
|
|
3909
|
+
setThinkingEnabled((prev) => !prev);
|
|
3910
|
+
setDisplayMessages((prev) => [
|
|
3911
|
+
...prev,
|
|
3912
|
+
{ role: "user", content: trimmed },
|
|
3913
|
+
{ role: "assistant", content: `\u6DF1\u5EA6\u601D\u8003\u5DF2${thinkingEnabled ? "\u5173\u95ED" : "\u5F00\u542F"}` }
|
|
3914
|
+
]);
|
|
3915
|
+
setInput("");
|
|
3916
|
+
return;
|
|
3917
|
+
}
|
|
3918
|
+
if (cmdLower === "/effort") {
|
|
3919
|
+
const next = thinkingEffort === "high" ? "max" : "high";
|
|
3920
|
+
setThinkingEffort(next);
|
|
3921
|
+
setDisplayMessages((prev) => [
|
|
3922
|
+
...prev,
|
|
3923
|
+
{ role: "user", content: trimmed },
|
|
3924
|
+
{ role: "assistant", content: `\u63A8\u7406\u7B49\u7EA7\u5DF2\u5207\u6362\u4E3A ${next === "high" ? "High" : "Max"}` }
|
|
3925
|
+
]);
|
|
3926
|
+
setInput("");
|
|
3927
|
+
return;
|
|
3928
|
+
}
|
|
3929
|
+
if (cmdLower === "/tools") {
|
|
3930
|
+
const next = toolChoice === void 0 ? "none" : toolChoice === "none" ? "required" : toolChoice === "required" ? "auto" : void 0;
|
|
3931
|
+
setToolChoice(next);
|
|
3932
|
+
const label = next === void 0 ? "\u81EA\u52A8\uFF08\u9ED8\u8BA4\uFF09" : next === "none" ? "\u7981\u6B62\u8C03\u7528" : next === "required" ? "\u5F3A\u5236\u8C03\u7528" : "\u81EA\u52A8\uFF08\u9ED8\u8BA4\uFF09";
|
|
3933
|
+
setDisplayMessages((prev) => [
|
|
3934
|
+
...prev,
|
|
3935
|
+
{ role: "user", content: trimmed },
|
|
3936
|
+
{ role: "assistant", content: `\u5DE5\u5177\u8C03\u7528\u7B56\u7565\u5DF2\u5207\u6362\u4E3A ${label}` }
|
|
3937
|
+
]);
|
|
3938
|
+
setInput("");
|
|
3939
|
+
return;
|
|
3940
|
+
}
|
|
3941
|
+
const cmd = commandRegistry.get(cmdLower);
|
|
3711
3942
|
if (cmd) {
|
|
3712
3943
|
const result = cmd.handler();
|
|
3713
3944
|
switch (result.kind) {
|
|
@@ -3782,7 +4013,12 @@ function ChatSession({
|
|
|
3782
4013
|
const abortController = new AbortController();
|
|
3783
4014
|
abortRef.current = abortController;
|
|
3784
4015
|
try {
|
|
3785
|
-
for await (const event of session.chat(trimmed
|
|
4016
|
+
for await (const event of session.chat(trimmed, {
|
|
4017
|
+
thinkingAllowed: thinkingEnabled || void 0,
|
|
4018
|
+
thinkingEffort: thinkingEnabled ? thinkingEffort : void 0,
|
|
4019
|
+
responseFormat: responseFormat !== "text" ? responseFormat : void 0,
|
|
4020
|
+
toolChoice
|
|
4021
|
+
})) {
|
|
3786
4022
|
if (abortController.signal.aborted) break;
|
|
3787
4023
|
switch (event.type) {
|
|
3788
4024
|
case "text_delta":
|
|
@@ -3820,6 +4056,11 @@ function ChatSession({
|
|
|
3820
4056
|
setStreamingModel(event.model);
|
|
3821
4057
|
currentUsageRef.current = event.usage;
|
|
3822
4058
|
currentModelRef.current = event.model;
|
|
4059
|
+
{
|
|
4060
|
+
const cost = calculateCost(event.usage, event.model);
|
|
4061
|
+
setCurrentCost(cost.totalCost);
|
|
4062
|
+
currentCostRef.current = cost.totalCost;
|
|
4063
|
+
}
|
|
3823
4064
|
break;
|
|
3824
4065
|
case "done":
|
|
3825
4066
|
setCurrentElapsed(event.elapsed);
|
|
@@ -3861,23 +4102,14 @@ function ChatSession({
|
|
|
3861
4102
|
]);
|
|
3862
4103
|
}
|
|
3863
4104
|
}
|
|
3864
|
-
}, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills]);
|
|
4105
|
+
}, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel]);
|
|
3865
4106
|
useEffect3(() => {
|
|
3866
4107
|
if (!isStreaming && externalCostTracker) {
|
|
3867
4108
|
setTodayCost(externalCostTracker.todayTotalCost);
|
|
3868
4109
|
}
|
|
3869
4110
|
}, [isStreaming, externalCostTracker]);
|
|
3870
|
-
useEffect3(() => {
|
|
3871
|
-
if (currentUsage && streamingModel && !currentCost) {
|
|
3872
|
-
import("./models-OL7DHYGK.js").then(({ calculateCost: calculateCost2 }) => {
|
|
3873
|
-
const cost = calculateCost2(currentUsage, streamingModel);
|
|
3874
|
-
setCurrentCost(cost.totalCost);
|
|
3875
|
-
currentCostRef.current = cost.totalCost;
|
|
3876
|
-
});
|
|
3877
|
-
}
|
|
3878
|
-
}, [currentUsage, streamingModel, currentCost]);
|
|
3879
4111
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
|
|
3880
|
-
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", marginBottom: 1, children: [
|
|
4112
|
+
!hasConversationStarted && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", marginBottom: 1, children: [
|
|
3881
4113
|
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
|
|
3882
4114
|
const colorIndex = (i + offset) % CYBER_PALETTE.length;
|
|
3883
4115
|
return /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text9, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }) }, i);
|
|
@@ -3899,6 +4131,15 @@ function ChatSession({
|
|
|
3899
4131
|
" \u{1F527} \u6A21\u578B ",
|
|
3900
4132
|
SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel
|
|
3901
4133
|
] }),
|
|
4134
|
+
thinkingEnabled && /* @__PURE__ */ jsxs8(Text9, { color: "#ff9800", children: [
|
|
4135
|
+
" \u{1F9E0} \u6DF1\u5EA6\u601D\u8003 ",
|
|
4136
|
+
thinkingEffort === "max" ? "Max" : "High"
|
|
4137
|
+
] }),
|
|
4138
|
+
responseFormat === "json_object" && /* @__PURE__ */ jsx8(Text9, { color: "#4caf50", children: " \u{1F4C4} JSON" }),
|
|
4139
|
+
toolChoice !== void 0 && /* @__PURE__ */ jsxs8(Text9, { color: "#e91e63", children: [
|
|
4140
|
+
" \u{1F6E0} ",
|
|
4141
|
+
toolChoice === "none" ? "\u7981\u6B62\u5DE5\u5177" : toolChoice === "required" ? "\u5F3A\u5236\u5DE5\u5177" : ""
|
|
4142
|
+
] }),
|
|
3902
4143
|
cmdTips.length > 0 && (() => {
|
|
3903
4144
|
const tip = cmdTips[cmdTipIndex % cmdTips.length];
|
|
3904
4145
|
if (!tip) return null;
|
|
@@ -4006,7 +4247,21 @@ function ChatSession({
|
|
|
4006
4247
|
] }),
|
|
4007
4248
|
/* @__PURE__ */ jsx8(Text9, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
|
|
4008
4249
|
] }) : /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
4009
|
-
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */
|
|
4250
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: hasConversationStarted && (balance !== null || sessionCost > 0 || isStreaming) ? /* @__PURE__ */ jsxs8(Text9, { color: "#00ffff", dimColor: true, children: [
|
|
4251
|
+
"\u2500".repeat(Math.max(dividerWidth - 35, 10)),
|
|
4252
|
+
balance !== null && /* @__PURE__ */ jsxs8(Text9, { color: "yellow", children: [
|
|
4253
|
+
" \u{1F4B0} \u4F59\u989D \xA5",
|
|
4254
|
+
balance.toFixed(2)
|
|
4255
|
+
] }),
|
|
4256
|
+
isStreaming ? /* @__PURE__ */ jsxs8(Text9, { color: "cyan", children: [
|
|
4257
|
+
" \u{1F4CA} \u672C\u6B21 \xA5",
|
|
4258
|
+
sessionCost > 0 ? sessionCost.toFixed(4) + " " : "",
|
|
4259
|
+
/* @__PURE__ */ jsx8(InkSpinner2, { type: "dots" })
|
|
4260
|
+
] }) : sessionCost > 0 ? /* @__PURE__ */ jsxs8(Text9, { color: "cyan", children: [
|
|
4261
|
+
" \u{1F4CA} \u672C\u6B21 \xA5",
|
|
4262
|
+
sessionCost.toFixed(4)
|
|
4263
|
+
] }) : null
|
|
4264
|
+
] }) : /* @__PURE__ */ jsx8(Text9, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
|
|
4010
4265
|
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
4011
4266
|
/* @__PURE__ */ jsx8(Box8, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text9, { bold: true, color: "#00ff41", children: "\u26A1" }) }),
|
|
4012
4267
|
/* @__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(
|
|
@@ -4224,7 +4479,7 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
4224
4479
|
const [initialLevel, setInitialLevel] = useState5(1);
|
|
4225
4480
|
const [selectingLevel, setSelectingLevel] = useState5(true);
|
|
4226
4481
|
const stateRef = useRef3(createInitialState(initialLevel));
|
|
4227
|
-
const [
|
|
4482
|
+
const [tick2, setTick] = useState5(0);
|
|
4228
4483
|
const onExitRef = useRef3(_onExit);
|
|
4229
4484
|
onExitRef.current = _onExit;
|
|
4230
4485
|
useEffect4(() => {
|
|
@@ -4279,7 +4534,7 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
4279
4534
|
const aliveCount = s.bricks.filter((b) => b.alive).length;
|
|
4280
4535
|
const board = buildBoard(s);
|
|
4281
4536
|
const def = getLevel(s.level);
|
|
4282
|
-
void
|
|
4537
|
+
void tick2;
|
|
4283
4538
|
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
4284
4539
|
/* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
|
|
4285
4540
|
/* @__PURE__ */ jsx10(Box10, { width: 20, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
@@ -4754,7 +5009,7 @@ function buildGameView(s, scoreLines, scoreColor, message) {
|
|
|
4754
5009
|
}
|
|
4755
5010
|
function CoderCheck({ onExit: _onExit }) {
|
|
4756
5011
|
const stateRef = useRef4(createInitialState2());
|
|
4757
|
-
const [
|
|
5012
|
+
const [tick2, setTick] = useState6(0);
|
|
4758
5013
|
const [colorOffset, setColorOffset] = useState6(0);
|
|
4759
5014
|
const onExitRef = useRef4(_onExit);
|
|
4760
5015
|
onExitRef.current = _onExit;
|
|
@@ -4832,7 +5087,7 @@ function CoderCheck({ onExit: _onExit }) {
|
|
|
4832
5087
|
const scoreStr = String(s.score).padStart(5, "0");
|
|
4833
5088
|
const scoreLines = buildScoreLines(scoreStr);
|
|
4834
5089
|
const view = buildGameView(s, scoreLines, scoreColor, s.message);
|
|
4835
|
-
void
|
|
5090
|
+
void tick2;
|
|
4836
5091
|
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
|
|
4837
5092
|
/* @__PURE__ */ jsx11(Box11, { flexDirection: "row", children: /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
4838
5093
|
"\u751F\u547D ",
|
|
@@ -4889,16 +5144,375 @@ var coder_check_default = {
|
|
|
4889
5144
|
}
|
|
4890
5145
|
};
|
|
4891
5146
|
|
|
5147
|
+
// src/game/snake/engine.ts
|
|
5148
|
+
function isOpposite(a, b) {
|
|
5149
|
+
return a === "UP" && b === "DOWN" || a === "DOWN" && b === "UP" || a === "LEFT" && b === "RIGHT" || a === "RIGHT" && b === "LEFT";
|
|
5150
|
+
}
|
|
5151
|
+
function samePoint(a, b) {
|
|
5152
|
+
return a.x === b.x && a.y === b.y;
|
|
5153
|
+
}
|
|
5154
|
+
function movePoint(p, dir) {
|
|
5155
|
+
switch (dir) {
|
|
5156
|
+
case "UP":
|
|
5157
|
+
return { x: p.x, y: p.y - 1 };
|
|
5158
|
+
case "DOWN":
|
|
5159
|
+
return { x: p.x, y: p.y + 1 };
|
|
5160
|
+
case "LEFT":
|
|
5161
|
+
return { x: p.x - 1, y: p.y };
|
|
5162
|
+
case "RIGHT":
|
|
5163
|
+
return { x: p.x + 1, y: p.y };
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
function wrapPoint(p, config) {
|
|
5167
|
+
return {
|
|
5168
|
+
x: (p.x % config.width + config.width) % config.width,
|
|
5169
|
+
y: (p.y % config.height + config.height) % config.height
|
|
5170
|
+
};
|
|
5171
|
+
}
|
|
5172
|
+
function generateFood(snake, config, type, excludePositions = []) {
|
|
5173
|
+
const occupied = new Set(snake.map((p) => `${p.x},${p.y}`));
|
|
5174
|
+
for (const p of excludePositions) {
|
|
5175
|
+
occupied.add(`${p.x},${p.y}`);
|
|
5176
|
+
}
|
|
5177
|
+
const totalCells = config.width * config.height;
|
|
5178
|
+
if (occupied.size >= totalCells) return null;
|
|
5179
|
+
let pos;
|
|
5180
|
+
do {
|
|
5181
|
+
pos = {
|
|
5182
|
+
x: Math.floor(Math.random() * config.width),
|
|
5183
|
+
y: Math.floor(Math.random() * config.height)
|
|
5184
|
+
};
|
|
5185
|
+
} while (occupied.has(`${pos.x},${pos.y}`));
|
|
5186
|
+
return {
|
|
5187
|
+
position: pos,
|
|
5188
|
+
type,
|
|
5189
|
+
...type === "special" ? { spawnedAt: Date.now() } : {}
|
|
5190
|
+
};
|
|
5191
|
+
}
|
|
5192
|
+
function createInitialState3(config) {
|
|
5193
|
+
const startX = Math.floor(config.width / 2);
|
|
5194
|
+
const startY = Math.floor(config.height / 2);
|
|
5195
|
+
const snake = [
|
|
5196
|
+
{ x: startX, y: startY },
|
|
5197
|
+
{ x: startX - 1, y: startY },
|
|
5198
|
+
{ x: startX - 2, y: startY }
|
|
5199
|
+
];
|
|
5200
|
+
const food = generateFood(snake, config, "normal");
|
|
5201
|
+
return {
|
|
5202
|
+
snake,
|
|
5203
|
+
direction: "RIGHT",
|
|
5204
|
+
nextDirection: "RIGHT",
|
|
5205
|
+
food,
|
|
5206
|
+
specialFood: null,
|
|
5207
|
+
score: 0,
|
|
5208
|
+
speedLevel: 1,
|
|
5209
|
+
isGameOver: false,
|
|
5210
|
+
isPaused: false,
|
|
5211
|
+
wallWrap: Math.random() < 0.5,
|
|
5212
|
+
tickInterval: config.initialSpeed
|
|
5213
|
+
};
|
|
5214
|
+
}
|
|
5215
|
+
function tick(state, config) {
|
|
5216
|
+
state.direction = state.nextDirection;
|
|
5217
|
+
const head = state.snake[0];
|
|
5218
|
+
let newHead = movePoint(head, state.direction);
|
|
5219
|
+
const outOfBounds = newHead.x < 0 || newHead.x >= config.width || newHead.y < 0 || newHead.y >= config.height;
|
|
5220
|
+
if (outOfBounds) {
|
|
5221
|
+
if (state.wallWrap) {
|
|
5222
|
+
newHead = wrapPoint(newHead, config);
|
|
5223
|
+
} else {
|
|
5224
|
+
state.isGameOver = true;
|
|
5225
|
+
return;
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
5228
|
+
const ateNormal = samePoint(newHead, state.food.position);
|
|
5229
|
+
const ateSpecial = state.specialFood !== null && samePoint(newHead, state.specialFood.position);
|
|
5230
|
+
const willEat = ateNormal || ateSpecial;
|
|
5231
|
+
const bodyToCheck = willEat ? state.snake : state.snake.slice(0, -1);
|
|
5232
|
+
for (const segment of bodyToCheck) {
|
|
5233
|
+
if (samePoint(newHead, segment)) {
|
|
5234
|
+
state.isGameOver = true;
|
|
5235
|
+
return;
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5238
|
+
state.snake.unshift(newHead);
|
|
5239
|
+
if (ateNormal) {
|
|
5240
|
+
state.score += 1;
|
|
5241
|
+
updateSpeed(state, config);
|
|
5242
|
+
const exclude = state.specialFood ? [state.specialFood.position] : [];
|
|
5243
|
+
const newFood = generateFood(state.snake, config, "normal", exclude);
|
|
5244
|
+
if (newFood) {
|
|
5245
|
+
state.food = newFood;
|
|
5246
|
+
} else {
|
|
5247
|
+
state.isGameOver = true;
|
|
5248
|
+
return;
|
|
5249
|
+
}
|
|
5250
|
+
if (state.specialFood === null && Math.random() < 0.25) {
|
|
5251
|
+
state.specialFood = generateFood(state.snake, config, "special", [state.food.position]);
|
|
5252
|
+
}
|
|
5253
|
+
} else if (ateSpecial) {
|
|
5254
|
+
state.score += 3;
|
|
5255
|
+
updateSpeed(state, config);
|
|
5256
|
+
state.specialFood = null;
|
|
5257
|
+
} else {
|
|
5258
|
+
state.snake.pop();
|
|
5259
|
+
}
|
|
5260
|
+
if (state.specialFood?.spawnedAt !== void 0) {
|
|
5261
|
+
if (Date.now() - state.specialFood.spawnedAt > 5e3) {
|
|
5262
|
+
state.specialFood = null;
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5266
|
+
function updateSpeed(state, config) {
|
|
5267
|
+
const newLevel = Math.floor(state.score / config.scorePerLevel) + 1;
|
|
5268
|
+
state.speedLevel = newLevel;
|
|
5269
|
+
state.tickInterval = Math.max(
|
|
5270
|
+
config.minSpeed,
|
|
5271
|
+
config.initialSpeed - (newLevel - 1) * config.speedDecrement
|
|
5272
|
+
);
|
|
5273
|
+
}
|
|
5274
|
+
function changeDirection(state, newDir) {
|
|
5275
|
+
if (!isOpposite(newDir, state.direction)) {
|
|
5276
|
+
state.nextDirection = newDir;
|
|
5277
|
+
}
|
|
5278
|
+
}
|
|
5279
|
+
|
|
5280
|
+
// src/game/snake/renderer.ts
|
|
5281
|
+
import chalk4 from "chalk";
|
|
5282
|
+
var CELL_W = 2;
|
|
5283
|
+
var EMPTY = " ";
|
|
5284
|
+
var BODY_CHAR = "\u2593";
|
|
5285
|
+
var HEAD_CHAR = "\u2593";
|
|
5286
|
+
var NORMAL_FOOD_CHAR = "\u25C6";
|
|
5287
|
+
var SPECIAL_FOOD_CHAR = "\u2605";
|
|
5288
|
+
var CSI = "\x1B[";
|
|
5289
|
+
var CURSOR_HIDE = `${CSI}?25l`;
|
|
5290
|
+
var CURSOR_SHOW = `${CSI}?25h`;
|
|
5291
|
+
var CURSOR_HOME = `${CSI}H`;
|
|
5292
|
+
function hideCursor() {
|
|
5293
|
+
process.stdout.write(CURSOR_HIDE);
|
|
5294
|
+
}
|
|
5295
|
+
function showCursor() {
|
|
5296
|
+
process.stdout.write(CURSOR_SHOW);
|
|
5297
|
+
}
|
|
5298
|
+
function clearScreen() {
|
|
5299
|
+
process.stdout.write(`${CSI}2J${CURSOR_HOME}`);
|
|
5300
|
+
}
|
|
5301
|
+
function cellColor(char, type) {
|
|
5302
|
+
switch (type) {
|
|
5303
|
+
case "snake-head":
|
|
5304
|
+
return chalk4.greenBright(char);
|
|
5305
|
+
case "snake-body":
|
|
5306
|
+
return chalk4.green(char);
|
|
5307
|
+
case "normal-food":
|
|
5308
|
+
return chalk4.red(char);
|
|
5309
|
+
case "special-food":
|
|
5310
|
+
return chalk4.yellow(char);
|
|
5311
|
+
case "empty":
|
|
5312
|
+
return char;
|
|
5313
|
+
}
|
|
5314
|
+
}
|
|
5315
|
+
function renderToTerminal(state, config) {
|
|
5316
|
+
const lines = buildRenderLines(state, config);
|
|
5317
|
+
process.stdout.write(`${CSI}2J${CURSOR_HOME}` + lines.join("\n"));
|
|
5318
|
+
}
|
|
5319
|
+
function buildRenderLines(state, config) {
|
|
5320
|
+
const { width: W, height: H } = config;
|
|
5321
|
+
const lines = [];
|
|
5322
|
+
const grid = Array.from({ length: H }, () => Array(W).fill("empty"));
|
|
5323
|
+
for (let i = 1; i < state.snake.length; i++) {
|
|
5324
|
+
const seg = state.snake[i];
|
|
5325
|
+
if (seg.y >= 0 && seg.y < H && seg.x >= 0 && seg.x < W) {
|
|
5326
|
+
grid[seg.y][seg.x] = "snake-body";
|
|
5327
|
+
}
|
|
5328
|
+
}
|
|
5329
|
+
const head = state.snake[0];
|
|
5330
|
+
if (head.y >= 0 && head.y < H && head.x >= 0 && head.x < W) {
|
|
5331
|
+
grid[head.y][head.x] = "snake-head";
|
|
5332
|
+
}
|
|
5333
|
+
const fp = state.food.position;
|
|
5334
|
+
if (grid[fp.y][fp.x] === "empty") {
|
|
5335
|
+
grid[fp.y][fp.x] = "normal-food";
|
|
5336
|
+
}
|
|
5337
|
+
if (state.specialFood) {
|
|
5338
|
+
const sp = state.specialFood.position;
|
|
5339
|
+
if (grid[sp.y][sp.x] === "empty") {
|
|
5340
|
+
grid[sp.y][sp.x] = "special-food";
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
lines.push(" " + chalk4.gray("\u2554" + "\u2550\u2550".repeat(W) + "\u2557"));
|
|
5344
|
+
for (let y = 0; y < H; y++) {
|
|
5345
|
+
let row = " " + chalk4.gray("\u2551");
|
|
5346
|
+
for (let x = 0; x < W; x++) {
|
|
5347
|
+
const cellType = grid[y][x];
|
|
5348
|
+
let char;
|
|
5349
|
+
switch (cellType) {
|
|
5350
|
+
case "snake-head":
|
|
5351
|
+
char = HEAD_CHAR.repeat(CELL_W);
|
|
5352
|
+
break;
|
|
5353
|
+
case "snake-body":
|
|
5354
|
+
char = BODY_CHAR.repeat(CELL_W);
|
|
5355
|
+
break;
|
|
5356
|
+
case "normal-food":
|
|
5357
|
+
char = NORMAL_FOOD_CHAR + " ";
|
|
5358
|
+
break;
|
|
5359
|
+
case "special-food":
|
|
5360
|
+
char = SPECIAL_FOOD_CHAR + " ";
|
|
5361
|
+
break;
|
|
5362
|
+
default:
|
|
5363
|
+
char = EMPTY;
|
|
5364
|
+
}
|
|
5365
|
+
row += cellColor(char, cellType);
|
|
5366
|
+
}
|
|
5367
|
+
row += chalk4.gray("\u2551");
|
|
5368
|
+
lines.push(row);
|
|
5369
|
+
}
|
|
5370
|
+
lines.push(" " + chalk4.gray("\u255A" + "\u2550\u2550".repeat(W) + "\u255D"));
|
|
5371
|
+
lines.push("");
|
|
5372
|
+
lines.push(
|
|
5373
|
+
` ${chalk4.cyan("\u5206\u6570")}: ${state.score} ${chalk4.cyan("\u957F\u5EA6")}: ${state.snake.length} ${chalk4.cyan("\u901F\u5EA6")}: Lv.${state.speedLevel} ${chalk4.cyan("\u8FB9\u754C")}: ${state.wallWrap ? "\u7A7F\u5899" : "\u649E\u5899"}`
|
|
5374
|
+
);
|
|
5375
|
+
if (state.specialFood?.spawnedAt !== void 0) {
|
|
5376
|
+
const remaining = Math.ceil((5e3 - (Date.now() - state.specialFood.spawnedAt)) / 1e3);
|
|
5377
|
+
if (remaining > 0) {
|
|
5378
|
+
lines.push(` ${chalk4.yellow(`\u2B50 \u7279\u6B8A\u98DF\u7269 ${remaining} \u79D2\u540E\u6D88\u5931`)}`);
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
lines.push(` ${chalk4.gray("W/A/S/D \u6216 \u65B9\u5411\u952E\u79FB\u52A8 | P \u6682\u505C | M \u5207\u6362\u6A21\u5F0F | Q \u9000\u51FA")}`);
|
|
5382
|
+
if (state.isPaused) {
|
|
5383
|
+
lines.push("");
|
|
5384
|
+
lines.push(` ${chalk4.yellow("\u23F8 \u5DF2\u6682\u505C \u2014 \u6309 P \u7EE7\u7EED")}`);
|
|
5385
|
+
}
|
|
5386
|
+
if (state.isGameOver) {
|
|
5387
|
+
lines.push("");
|
|
5388
|
+
lines.push(` ${chalk4.red.bold("\u{1F480} \u6E38\u620F\u7ED3\u675F!")} ${chalk4.cyan("\u6700\u7EC8\u5206\u6570: " + state.score)}`);
|
|
5389
|
+
lines.push(` ${chalk4.gray("\u6309 R \u91CD\u65B0\u5F00\u59CB | \u6309 Q \u9000\u51FA")}`);
|
|
5390
|
+
}
|
|
5391
|
+
return lines;
|
|
5392
|
+
}
|
|
5393
|
+
|
|
5394
|
+
// src/game/snake/input.ts
|
|
5395
|
+
import * as readline from "readline";
|
|
5396
|
+
var KEY_TO_DIR = {
|
|
5397
|
+
up: "UP",
|
|
5398
|
+
w: "UP",
|
|
5399
|
+
W: "UP",
|
|
5400
|
+
down: "DOWN",
|
|
5401
|
+
s: "DOWN",
|
|
5402
|
+
S: "DOWN",
|
|
5403
|
+
left: "LEFT",
|
|
5404
|
+
a: "LEFT",
|
|
5405
|
+
A: "LEFT",
|
|
5406
|
+
right: "RIGHT",
|
|
5407
|
+
d: "RIGHT",
|
|
5408
|
+
D: "RIGHT"
|
|
5409
|
+
};
|
|
5410
|
+
function setupInput(ctx, onQuit, onRestart) {
|
|
5411
|
+
readline.emitKeypressEvents(process.stdin);
|
|
5412
|
+
if (process.stdin.isTTY) {
|
|
5413
|
+
process.stdin.setRawMode(true);
|
|
5414
|
+
}
|
|
5415
|
+
const handler = (_, key) => {
|
|
5416
|
+
if (!key || !key.name) return;
|
|
5417
|
+
const { name, ctrl } = key;
|
|
5418
|
+
if (ctrl && (name === "c" || name === "d")) {
|
|
5419
|
+
onQuit();
|
|
5420
|
+
return;
|
|
5421
|
+
}
|
|
5422
|
+
if (name === "q") {
|
|
5423
|
+
onQuit();
|
|
5424
|
+
return;
|
|
5425
|
+
}
|
|
5426
|
+
const state = ctx.state;
|
|
5427
|
+
if (state.isGameOver) {
|
|
5428
|
+
if (name === "r") {
|
|
5429
|
+
ctx.state = onRestart();
|
|
5430
|
+
}
|
|
5431
|
+
return;
|
|
5432
|
+
}
|
|
5433
|
+
if (name === "p") {
|
|
5434
|
+
state.isPaused = !state.isPaused;
|
|
5435
|
+
return;
|
|
5436
|
+
}
|
|
5437
|
+
if (name === "m") {
|
|
5438
|
+
state.wallWrap = !state.wallWrap;
|
|
5439
|
+
return;
|
|
5440
|
+
}
|
|
5441
|
+
const dir = KEY_TO_DIR[name];
|
|
5442
|
+
if (dir) {
|
|
5443
|
+
changeDirection(state, dir);
|
|
5444
|
+
}
|
|
5445
|
+
};
|
|
5446
|
+
process.stdin.on("keypress", handler);
|
|
5447
|
+
return () => {
|
|
5448
|
+
process.stdin.removeListener("keypress", handler);
|
|
5449
|
+
if (process.stdin.isTTY) {
|
|
5450
|
+
process.stdin.setRawMode(false);
|
|
5451
|
+
}
|
|
5452
|
+
};
|
|
5453
|
+
}
|
|
5454
|
+
|
|
5455
|
+
// src/game/snake/index.ts
|
|
5456
|
+
var CONFIG = {
|
|
5457
|
+
width: 20,
|
|
5458
|
+
height: 16,
|
|
5459
|
+
initialSpeed: 180,
|
|
5460
|
+
minSpeed: 60,
|
|
5461
|
+
speedDecrement: 10,
|
|
5462
|
+
scorePerLevel: 5
|
|
5463
|
+
};
|
|
5464
|
+
var IDLE_INTERVAL = 200;
|
|
5465
|
+
var snakeGame = {
|
|
5466
|
+
id: "snake",
|
|
5467
|
+
name: "Big Snake",
|
|
5468
|
+
description: "\u7ECF\u5178\u8D2A\u5403\u86C7\uFF0C\u53CC\u98DF\u7269\u7CFB\u7EDF\u3001\u96BE\u5EA6\u9012\u589E\u3001\u7A7F\u5899/\u649E\u5899\u6A21\u5F0F",
|
|
5469
|
+
play: async () => {
|
|
5470
|
+
const ctx = { state: createInitialState3(CONFIG) };
|
|
5471
|
+
return new Promise((resolve2) => {
|
|
5472
|
+
const cleanupInput = setupInput(
|
|
5473
|
+
ctx,
|
|
5474
|
+
() => {
|
|
5475
|
+
stop = true;
|
|
5476
|
+
clearTimeout(timerId);
|
|
5477
|
+
showCursor();
|
|
5478
|
+
cleanupInput();
|
|
5479
|
+
resolve2();
|
|
5480
|
+
},
|
|
5481
|
+
() => {
|
|
5482
|
+
return createInitialState3(CONFIG);
|
|
5483
|
+
}
|
|
5484
|
+
);
|
|
5485
|
+
clearScreen();
|
|
5486
|
+
hideCursor();
|
|
5487
|
+
let stop = false;
|
|
5488
|
+
let timerId;
|
|
5489
|
+
function loop() {
|
|
5490
|
+
if (stop) return;
|
|
5491
|
+
const s = ctx.state;
|
|
5492
|
+
if (!s.isGameOver && !s.isPaused) {
|
|
5493
|
+
tick(s, CONFIG);
|
|
5494
|
+
}
|
|
5495
|
+
renderToTerminal(s, CONFIG);
|
|
5496
|
+
const interval = s.isGameOver || s.isPaused ? IDLE_INTERVAL : s.tickInterval;
|
|
5497
|
+
timerId = setTimeout(loop, interval);
|
|
5498
|
+
}
|
|
5499
|
+
timerId = setTimeout(loop, 0);
|
|
5500
|
+
});
|
|
5501
|
+
}
|
|
5502
|
+
};
|
|
5503
|
+
var snake_default = snakeGame;
|
|
5504
|
+
|
|
4892
5505
|
// src/game/registry.ts
|
|
4893
5506
|
function initGames() {
|
|
4894
5507
|
registerGame(brick_breaker_default);
|
|
4895
5508
|
registerGame(coder_check_default);
|
|
5509
|
+
registerGame(snake_default);
|
|
4896
5510
|
return listGames();
|
|
4897
5511
|
}
|
|
4898
5512
|
|
|
4899
5513
|
// src/cli/index.tsx
|
|
4900
5514
|
import { render as render4 } from "ink";
|
|
4901
|
-
import
|
|
5515
|
+
import chalk5 from "chalk";
|
|
4902
5516
|
|
|
4903
5517
|
// src/stock/StockList.tsx
|
|
4904
5518
|
import { Box as Box12, Text as Text13, useInput as useInput5 } from "ink";
|
|
@@ -5331,7 +5945,7 @@ function createCli() {
|
|
|
5331
5945
|
const key = await promptForApiKey();
|
|
5332
5946
|
if (!key) process.exit(1);
|
|
5333
5947
|
const savedPath = await saveApiKey(key);
|
|
5334
|
-
console.log(` ${
|
|
5948
|
+
console.log(` ${chalk5.green("\u2714")} API Key \u5DF2\u4FDD\u5B58\u5230 ${chalk5.dim(savedPath)}
|
|
5335
5949
|
`);
|
|
5336
5950
|
const result = await loadAndValidate();
|
|
5337
5951
|
ctx = { ...ctx, config: result.config };
|
|
@@ -5478,8 +6092,8 @@ compdef _dskcode_completion dskcode`);
|
|
|
5478
6092
|
{ code: "sh601899" }
|
|
5479
6093
|
];
|
|
5480
6094
|
const savedPath = await saveStockConfig(defaultSymbols);
|
|
5481
|
-
console.log(`${
|
|
5482
|
-
console.log(`${
|
|
6095
|
+
console.log(`${chalk5.green("\u2714")} \u5DF2\u751F\u6210\u81EA\u9009\u80A1\u914D\u7F6E: ${chalk5.dim(savedPath)}`);
|
|
6096
|
+
console.log(`${chalk5.dim(" \u63D0\u793A: \u53EF\u7F16\u8F91\u4E0A\u8FF0\u6587\u4EF6\u81EA\u5B9A\u4E49\u81EA\u9009\u80A1\u5217\u8868")}
|
|
5483
6097
|
`);
|
|
5484
6098
|
}
|
|
5485
6099
|
const freshResult = await loadAndValidate();
|
|
@@ -5533,7 +6147,7 @@ compdef _dskcode_completion dskcode`);
|
|
|
5533
6147
|
});
|
|
5534
6148
|
if (selectedGame) {
|
|
5535
6149
|
console.log(`
|
|
5536
|
-
\u542F\u52A8\u6E38\u620F: ${
|
|
6150
|
+
\u542F\u52A8\u6E38\u620F: ${chalk5.green(selectedGame.name)}
|
|
5537
6151
|
`);
|
|
5538
6152
|
await selectedGame.play();
|
|
5539
6153
|
}
|