dskcode 0.1.10 → 0.1.12
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-DIZMSV5H.js +85 -0
- package/dist/chunk-DIZMSV5H.js.map +1 -0
- package/dist/chunk-FODNQIT4.js +626 -0
- package/dist/chunk-FODNQIT4.js.map +1 -0
- package/dist/deepseek-OIJOG3N5.js +8 -0
- package/dist/deepseek-OIJOG3N5.js.map +1 -0
- package/dist/index.js +1484 -181
- package/dist/index.js.map +1 -1
- package/dist/models-NQGNKB4T.js +19 -0
- package/dist/models-NQGNKB4T.js.map +1 -0
- package/package.json +1 -1
- package/dist/deepseek-7Y2G6EKM.js +0 -385
- package/dist/deepseek-7Y2G6EKM.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DeepSeekProvider,
|
|
4
|
+
ModelNotSupportedError
|
|
5
|
+
} from "./chunk-FODNQIT4.js";
|
|
6
|
+
import {
|
|
7
|
+
SUPPORTED_MODELS,
|
|
8
|
+
calculateCost,
|
|
9
|
+
estimateTokens,
|
|
10
|
+
getModelMeta,
|
|
11
|
+
isSupportedModel
|
|
12
|
+
} from "./chunk-DIZMSV5H.js";
|
|
2
13
|
|
|
3
14
|
// src/cli/index.tsx
|
|
4
15
|
import { Command } from "commander";
|
|
@@ -65,6 +76,12 @@ function mergeConfig(base, overlay) {
|
|
|
65
76
|
if (overlay.maxToolRounds !== void 0) {
|
|
66
77
|
result.maxToolRounds = overlay.maxToolRounds;
|
|
67
78
|
}
|
|
79
|
+
if (overlay.budgetLimit !== void 0) {
|
|
80
|
+
result.budgetLimit = overlay.budgetLimit;
|
|
81
|
+
}
|
|
82
|
+
if (overlay.tokenBudgetLimit !== void 0) {
|
|
83
|
+
result.tokenBudgetLimit = overlay.tokenBudgetLimit;
|
|
84
|
+
}
|
|
68
85
|
if (overlay.providers !== void 0) {
|
|
69
86
|
result.providers = overlay.providers;
|
|
70
87
|
}
|
|
@@ -85,7 +102,9 @@ var ENV_MAP = {
|
|
|
85
102
|
[`${ENV_PREFIX}VERBOSE`]: "verbose",
|
|
86
103
|
[`${ENV_PREFIX}MAX_TOKENS`]: "maxTokens",
|
|
87
104
|
[`${ENV_PREFIX}TEMPERATURE`]: "temperature",
|
|
88
|
-
[`${ENV_PREFIX}MAX_TOOL_ROUNDS`]: "maxToolRounds"
|
|
105
|
+
[`${ENV_PREFIX}MAX_TOOL_ROUNDS`]: "maxToolRounds",
|
|
106
|
+
[`${ENV_PREFIX}BUDGET_LIMIT`]: "budgetLimit",
|
|
107
|
+
[`${ENV_PREFIX}TOKEN_BUDGET_LIMIT`]: "tokenBudgetLimit"
|
|
89
108
|
};
|
|
90
109
|
function applyEnvVars(config) {
|
|
91
110
|
const result = { ...config, providers: [...config.providers] };
|
|
@@ -99,9 +118,11 @@ function applyEnvVars(config) {
|
|
|
99
118
|
break;
|
|
100
119
|
}
|
|
101
120
|
case "maxTokens":
|
|
102
|
-
case "maxToolRounds":
|
|
121
|
+
case "maxToolRounds":
|
|
122
|
+
case "budgetLimit":
|
|
123
|
+
case "tokenBudgetLimit": {
|
|
103
124
|
const n = Number(raw);
|
|
104
|
-
if (Number.isFinite(n) && n
|
|
125
|
+
if (Number.isFinite(n) && n >= 0) {
|
|
105
126
|
result[configKey] = n;
|
|
106
127
|
}
|
|
107
128
|
break;
|
|
@@ -156,6 +177,12 @@ function applyCliOverrides(config, flags) {
|
|
|
156
177
|
if (flags.temperature !== void 0 && flags.temperature >= 0 && flags.temperature <= 2) {
|
|
157
178
|
result.temperature = flags.temperature;
|
|
158
179
|
}
|
|
180
|
+
if (flags.budgetLimit !== void 0 && flags.budgetLimit >= 0) {
|
|
181
|
+
result.budgetLimit = flags.budgetLimit;
|
|
182
|
+
}
|
|
183
|
+
if (flags.tokenBudgetLimit !== void 0 && flags.tokenBudgetLimit >= 0) {
|
|
184
|
+
result.tokenBudgetLimit = flags.tokenBudgetLimit;
|
|
185
|
+
}
|
|
159
186
|
return result;
|
|
160
187
|
}
|
|
161
188
|
function validateConfig(config) {
|
|
@@ -166,7 +193,7 @@ function validateConfig(config) {
|
|
|
166
193
|
message: "\u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A Provider\u3002\u8BF7\u901A\u8FC7\u914D\u7F6E\u6587\u4EF6\u6216 DEEPSEEK_API_KEY \u73AF\u5883\u53D8\u91CF\u8BBE\u7F6E\u3002"
|
|
167
194
|
});
|
|
168
195
|
}
|
|
169
|
-
const
|
|
196
|
+
const SUPPORTED_MODELS2 = ["deepseek-v4-flash", "deepseek-v4-pro"];
|
|
170
197
|
for (let i = 0; i < config.providers.length; i++) {
|
|
171
198
|
const p = config.providers[i];
|
|
172
199
|
if (!p.name) {
|
|
@@ -180,10 +207,10 @@ function validateConfig(config) {
|
|
|
180
207
|
field: `providers[${i}].model`,
|
|
181
208
|
message: `Provider "${p.name || i}" \u7F3A\u5C11 model \u5B57\u6BB5\u3002`
|
|
182
209
|
});
|
|
183
|
-
} else if (!
|
|
210
|
+
} else if (!SUPPORTED_MODELS2.includes(p.model)) {
|
|
184
211
|
errors.push({
|
|
185
212
|
field: `providers[${i}].model`,
|
|
186
|
-
message: `Provider "${p.name || i}" \u7684 model "${p.model}" \u4E0D\u53D7\u652F\u6301\u3002dskcode \u4EC5\u652F\u6301: ${
|
|
213
|
+
message: `Provider "${p.name || i}" \u7684 model "${p.model}" \u4E0D\u53D7\u652F\u6301\u3002dskcode \u4EC5\u652F\u6301: ${SUPPORTED_MODELS2.join(", ")}`
|
|
187
214
|
});
|
|
188
215
|
}
|
|
189
216
|
}
|
|
@@ -216,6 +243,18 @@ function validateConfig(config) {
|
|
|
216
243
|
message: "maxToolRounds \u5FC5\u987B\u5927\u4E8E\u7B49\u4E8E 1\u3002"
|
|
217
244
|
});
|
|
218
245
|
}
|
|
246
|
+
if (config.budgetLimit !== void 0 && config.budgetLimit < 0) {
|
|
247
|
+
errors.push({
|
|
248
|
+
field: "budgetLimit",
|
|
249
|
+
message: "budgetLimit \u5FC5\u987B\u5927\u4E8E\u7B49\u4E8E 0\uFF080 \u8868\u793A\u4E0D\u9650\u5236\uFF09\u3002"
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (config.tokenBudgetLimit !== void 0 && config.tokenBudgetLimit < 0) {
|
|
253
|
+
errors.push({
|
|
254
|
+
field: "tokenBudgetLimit",
|
|
255
|
+
message: "tokenBudgetLimit \u5FC5\u987B\u5927\u4E8E\u7B49\u4E8E 0\uFF080 \u8868\u793A\u4E0D\u9650\u5236\uFF09\u3002"
|
|
256
|
+
});
|
|
257
|
+
}
|
|
219
258
|
return errors;
|
|
220
259
|
}
|
|
221
260
|
async function loadConfig(configPath) {
|
|
@@ -265,6 +304,34 @@ async function saveApiKey(apiKey) {
|
|
|
265
304
|
await writeFile(configFile, JSON.stringify(configData, null, 2), "utf-8");
|
|
266
305
|
return configFile;
|
|
267
306
|
}
|
|
307
|
+
async function saveModelConfig(model) {
|
|
308
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
309
|
+
const configDir = join(home, ".dskcode");
|
|
310
|
+
const configFile = join(configDir, "settings.json");
|
|
311
|
+
await mkdir(configDir, { recursive: true });
|
|
312
|
+
let configData;
|
|
313
|
+
try {
|
|
314
|
+
const raw = await readFile(configFile, "utf-8");
|
|
315
|
+
configData = JSON.parse(raw);
|
|
316
|
+
} catch {
|
|
317
|
+
configData = structuredClone(defaultConfig);
|
|
318
|
+
}
|
|
319
|
+
const providers = configData.providers ?? [];
|
|
320
|
+
const defaultProviderName = configData.defaultProvider ?? "deepseek";
|
|
321
|
+
const existing = providers.find((p) => p.name === defaultProviderName);
|
|
322
|
+
if (existing) {
|
|
323
|
+
existing.model = model;
|
|
324
|
+
} else {
|
|
325
|
+
providers.push({
|
|
326
|
+
name: defaultProviderName,
|
|
327
|
+
baseUrl: "https://api.deepseek.com",
|
|
328
|
+
model
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
configData.providers = providers;
|
|
332
|
+
await writeFile(configFile, JSON.stringify(configData, null, 2), "utf-8");
|
|
333
|
+
return configFile;
|
|
334
|
+
}
|
|
268
335
|
async function saveStockConfig(symbols) {
|
|
269
336
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
270
337
|
const configDir = join(home, ".dskcode");
|
|
@@ -421,8 +488,8 @@ async function promptForApiKey() {
|
|
|
421
488
|
}
|
|
422
489
|
|
|
423
490
|
// src/cli/index.tsx
|
|
424
|
-
import { readFile as
|
|
425
|
-
import { join as
|
|
491
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
492
|
+
import { join as join3 } from "path";
|
|
426
493
|
|
|
427
494
|
// src/ui/RenderScope.tsx
|
|
428
495
|
import { render } from "ink";
|
|
@@ -435,6 +502,15 @@ function renderApp(node) {
|
|
|
435
502
|
import { Text } from "ink";
|
|
436
503
|
import InkSpinner from "ink-spinner";
|
|
437
504
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
505
|
+
function Spinner({ type = "dots", label }) {
|
|
506
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
507
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: /* @__PURE__ */ jsx(InkSpinner, { type }) }),
|
|
508
|
+
label ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
509
|
+
" ",
|
|
510
|
+
label
|
|
511
|
+
] }) : null
|
|
512
|
+
] });
|
|
513
|
+
}
|
|
438
514
|
|
|
439
515
|
// src/ui/StatusMessage.tsx
|
|
440
516
|
import { Box, Text as Text2 } from "ink";
|
|
@@ -455,9 +531,9 @@ var LOGO_LINES = [
|
|
|
455
531
|
];
|
|
456
532
|
|
|
457
533
|
// src/ui/ChatSession.tsx
|
|
458
|
-
import { Box as
|
|
534
|
+
import { Box as Box5, Text as Text6, useInput, Static } from "ink";
|
|
459
535
|
import TextInput from "ink-text-input";
|
|
460
|
-
import { useEffect as useEffect3, useState as useState3, useCallback as useCallback2 } from "react";
|
|
536
|
+
import { useEffect as useEffect3, useState as useState3, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
461
537
|
|
|
462
538
|
// src/ui/useDoubleCtrlC.ts
|
|
463
539
|
import { useState as useState2, useCallback, useEffect as useEffect2, useRef } from "react";
|
|
@@ -487,8 +563,836 @@ function useDoubleCtrlC(onExit) {
|
|
|
487
563
|
return { doubleCtrlC, handleCtrlC };
|
|
488
564
|
}
|
|
489
565
|
|
|
490
|
-
// src/ui/
|
|
566
|
+
// src/ui/AssistantMessage.tsx
|
|
567
|
+
import { Box as Box4, Text as Text5 } from "ink";
|
|
568
|
+
|
|
569
|
+
// src/provider/cost-tracker.ts
|
|
570
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
571
|
+
import { join as join2 } from "path";
|
|
572
|
+
var CostTracker = class {
|
|
573
|
+
#costDir;
|
|
574
|
+
#budgetLimit;
|
|
575
|
+
#tokenBudgetLimit;
|
|
576
|
+
#onBudgetExceeded;
|
|
577
|
+
// 会话级统计(内存)
|
|
578
|
+
#sessionId;
|
|
579
|
+
#sessionStartedAt;
|
|
580
|
+
#sessionRecords = [];
|
|
581
|
+
// 日级统计(内存缓存,启动时从磁盘恢复)
|
|
582
|
+
#todayDate;
|
|
583
|
+
#todaySummary;
|
|
584
|
+
// 持久化标记
|
|
585
|
+
#dirty = false;
|
|
586
|
+
#flushInProgress = false;
|
|
587
|
+
constructor(options = {}) {
|
|
588
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
589
|
+
this.#costDir = options.costDir ?? join2(home, ".dskcode", "costs");
|
|
590
|
+
this.#budgetLimit = options.budgetLimit ?? 0;
|
|
591
|
+
this.#tokenBudgetLimit = options.tokenBudgetLimit ?? 0;
|
|
592
|
+
this.#onBudgetExceeded = options.onBudgetExceeded;
|
|
593
|
+
this.#sessionId = generateSessionId();
|
|
594
|
+
this.#sessionStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
595
|
+
const today = getTodayStr();
|
|
596
|
+
this.#todayDate = today;
|
|
597
|
+
this.#todaySummary = createEmptyDailySummary(today);
|
|
598
|
+
}
|
|
599
|
+
// -----------------------------------------------------------------------
|
|
600
|
+
// 核心方法
|
|
601
|
+
// -----------------------------------------------------------------------
|
|
602
|
+
/**
|
|
603
|
+
* 记录一次 API 调用的 Token 使用量和成本。
|
|
604
|
+
* 自动计算费用,累加到会话级和日级统计。
|
|
605
|
+
*/
|
|
606
|
+
record(usage, model) {
|
|
607
|
+
const cost = calculateCost(usage, model);
|
|
608
|
+
const record = {
|
|
609
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
610
|
+
model,
|
|
611
|
+
usage: { ...usage },
|
|
612
|
+
cost: { ...cost }
|
|
613
|
+
};
|
|
614
|
+
this.#sessionRecords.push(record);
|
|
615
|
+
this.#ensureTodayBucket();
|
|
616
|
+
this.#addToDaily(record);
|
|
617
|
+
this.#dirty = true;
|
|
618
|
+
this.#checkBudget();
|
|
619
|
+
return cost;
|
|
620
|
+
}
|
|
621
|
+
// -----------------------------------------------------------------------
|
|
622
|
+
// 会话级查询
|
|
623
|
+
// -----------------------------------------------------------------------
|
|
624
|
+
/** 当前会话 ID */
|
|
625
|
+
get sessionId() {
|
|
626
|
+
return this.#sessionId;
|
|
627
|
+
}
|
|
628
|
+
/** 当前会话的所有成本记录 */
|
|
629
|
+
get records() {
|
|
630
|
+
return this.#sessionRecords;
|
|
631
|
+
}
|
|
632
|
+
/** 当前会话累计成本汇总 */
|
|
633
|
+
get sessionSummary() {
|
|
634
|
+
let totalPromptTokens = 0;
|
|
635
|
+
let totalCompletionTokens = 0;
|
|
636
|
+
let totalCachedTokens = 0;
|
|
637
|
+
let totalCost = 0;
|
|
638
|
+
for (const r of this.#sessionRecords) {
|
|
639
|
+
totalPromptTokens += r.usage.promptTokens;
|
|
640
|
+
totalCompletionTokens += r.usage.completionTokens;
|
|
641
|
+
totalCachedTokens += r.usage.cachedPromptTokens ?? 0;
|
|
642
|
+
totalCost += r.cost.totalCost;
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
sessionId: this.#sessionId,
|
|
646
|
+
startedAt: this.#sessionStartedAt,
|
|
647
|
+
totalPromptTokens,
|
|
648
|
+
totalCompletionTokens,
|
|
649
|
+
totalCachedTokens,
|
|
650
|
+
totalCost,
|
|
651
|
+
records: [...this.#sessionRecords]
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
/** 当前会话总费用(元) */
|
|
655
|
+
get sessionTotalCost() {
|
|
656
|
+
return this.#sessionRecords.reduce((sum, r) => sum + r.cost.totalCost, 0);
|
|
657
|
+
}
|
|
658
|
+
/** 当前会话总 prompt token 数 */
|
|
659
|
+
get sessionPromptTokens() {
|
|
660
|
+
return this.#sessionRecords.reduce((sum, r) => sum + r.usage.promptTokens, 0);
|
|
661
|
+
}
|
|
662
|
+
/** 当前会话总 completion token 数 */
|
|
663
|
+
get sessionCompletionTokens() {
|
|
664
|
+
return this.#sessionRecords.reduce((sum, r) => sum + r.usage.completionTokens, 0);
|
|
665
|
+
}
|
|
666
|
+
/** 当前会话 API 调用次数 */
|
|
667
|
+
get sessionCallCount() {
|
|
668
|
+
return this.#sessionRecords.length;
|
|
669
|
+
}
|
|
670
|
+
// -----------------------------------------------------------------------
|
|
671
|
+
// 日级查询
|
|
672
|
+
// -----------------------------------------------------------------------
|
|
673
|
+
/** 今日成本汇总 */
|
|
674
|
+
get todaySummary() {
|
|
675
|
+
this.#ensureTodayBucket();
|
|
676
|
+
return { ...this.#todaySummary };
|
|
677
|
+
}
|
|
678
|
+
/** 今日总费用(元) */
|
|
679
|
+
get todayTotalCost() {
|
|
680
|
+
this.#ensureTodayBucket();
|
|
681
|
+
return this.#todaySummary.totalCost;
|
|
682
|
+
}
|
|
683
|
+
/** 今日 API 调用次数 */
|
|
684
|
+
get todayCallCount() {
|
|
685
|
+
this.#ensureTodayBucket();
|
|
686
|
+
return this.#todaySummary.totalCalls;
|
|
687
|
+
}
|
|
688
|
+
// -----------------------------------------------------------------------
|
|
689
|
+
// 预算检查
|
|
690
|
+
// -----------------------------------------------------------------------
|
|
691
|
+
/** 是否已超出预算 */
|
|
692
|
+
get isBudgetExceeded() {
|
|
693
|
+
if (this.#budgetLimit > 0 && this.todayTotalCost >= this.#budgetLimit) {
|
|
694
|
+
return true;
|
|
695
|
+
}
|
|
696
|
+
if (this.#tokenBudgetLimit > 0) {
|
|
697
|
+
const totalTokens = this.#todaySummary.totalPromptTokens + this.#todaySummary.totalCompletionTokens;
|
|
698
|
+
if (totalTokens >= this.#tokenBudgetLimit) return true;
|
|
699
|
+
}
|
|
700
|
+
return false;
|
|
701
|
+
}
|
|
702
|
+
/** 剩余预算(元),无限制时返回 Infinity */
|
|
703
|
+
get remainingBudget() {
|
|
704
|
+
if (this.#budgetLimit <= 0) return Infinity;
|
|
705
|
+
return Math.max(0, this.#budgetLimit - this.todayTotalCost);
|
|
706
|
+
}
|
|
707
|
+
// -----------------------------------------------------------------------
|
|
708
|
+
// 持久化
|
|
709
|
+
// -----------------------------------------------------------------------
|
|
710
|
+
/**
|
|
711
|
+
* 从磁盘加载历史成本数据。
|
|
712
|
+
* 启动时调用,用于恢复今日和历史数据。
|
|
713
|
+
*/
|
|
714
|
+
async load() {
|
|
715
|
+
const store = await this.#loadStore();
|
|
716
|
+
const today = getTodayStr();
|
|
717
|
+
if (store.daily[today]) {
|
|
718
|
+
this.#todaySummary = store.daily[today];
|
|
719
|
+
this.#todayDate = today;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* 将脏数据持久化到磁盘。
|
|
724
|
+
* 通常在每次 record() 后异步调用,或在会话结束时主动调用。
|
|
725
|
+
*/
|
|
726
|
+
async flush() {
|
|
727
|
+
if (!this.#dirty || this.#flushInProgress) return;
|
|
728
|
+
this.#flushInProgress = true;
|
|
729
|
+
this.#dirty = false;
|
|
730
|
+
try {
|
|
731
|
+
await this.#saveStore();
|
|
732
|
+
} finally {
|
|
733
|
+
this.#flushInProgress = false;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* 查询指定日期范围的成本汇总。
|
|
738
|
+
* @param startDate 起始日期(YYYY-MM-DD),包含
|
|
739
|
+
* @param endDate 截止日期(YYYY-MM-DD),包含,默认同 startDate
|
|
740
|
+
*/
|
|
741
|
+
async queryRange(startDate, endDate) {
|
|
742
|
+
const store = await this.#loadStore();
|
|
743
|
+
const end = endDate ?? startDate;
|
|
744
|
+
const result = [];
|
|
745
|
+
for (const [date, summary] of Object.entries(store.daily)) {
|
|
746
|
+
if (date >= startDate && date <= end) {
|
|
747
|
+
result.push(summary);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
result.sort((a, b) => a.date.localeCompare(b.date));
|
|
751
|
+
return result;
|
|
752
|
+
}
|
|
753
|
+
/** 重置会话(开始新会话,不影响日级累计) */
|
|
754
|
+
resetSession() {
|
|
755
|
+
this.#sessionId = generateSessionId();
|
|
756
|
+
this.#sessionStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
757
|
+
this.#sessionRecords.length = 0;
|
|
758
|
+
}
|
|
759
|
+
// -----------------------------------------------------------------------
|
|
760
|
+
// 内部方法
|
|
761
|
+
// -----------------------------------------------------------------------
|
|
762
|
+
/** 确保今日的统计桶存在(处理跨日情况) */
|
|
763
|
+
#ensureTodayBucket() {
|
|
764
|
+
const today = getTodayStr();
|
|
765
|
+
if (today !== this.#todayDate) {
|
|
766
|
+
this.#todayDate = today;
|
|
767
|
+
this.#todaySummary = createEmptyDailySummary(today);
|
|
768
|
+
this.#dirty = true;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
/** 将一条记录累加到日级汇总 */
|
|
772
|
+
#addToDaily(record) {
|
|
773
|
+
this.#todaySummary.totalPromptTokens += record.usage.promptTokens;
|
|
774
|
+
this.#todaySummary.totalCompletionTokens += record.usage.completionTokens;
|
|
775
|
+
this.#todaySummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;
|
|
776
|
+
this.#todaySummary.totalCost += record.cost.totalCost;
|
|
777
|
+
this.#todaySummary.totalCalls += 1;
|
|
778
|
+
const modelKey = record.model;
|
|
779
|
+
if (!this.#todaySummary.byModel[modelKey]) {
|
|
780
|
+
this.#todaySummary.byModel[modelKey] = {
|
|
781
|
+
model: record.model,
|
|
782
|
+
totalPromptTokens: 0,
|
|
783
|
+
totalCompletionTokens: 0,
|
|
784
|
+
totalCachedTokens: 0,
|
|
785
|
+
totalCost: 0,
|
|
786
|
+
totalCalls: 0
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
const modelSummary = this.#todaySummary.byModel[modelKey];
|
|
790
|
+
modelSummary.totalPromptTokens += record.usage.promptTokens;
|
|
791
|
+
modelSummary.totalCompletionTokens += record.usage.completionTokens;
|
|
792
|
+
modelSummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;
|
|
793
|
+
modelSummary.totalCost += record.cost.totalCost;
|
|
794
|
+
modelSummary.totalCalls += 1;
|
|
795
|
+
}
|
|
796
|
+
/** 预算超限检查 */
|
|
797
|
+
#checkBudget() {
|
|
798
|
+
if (!this.isBudgetExceeded) return;
|
|
799
|
+
this.#onBudgetExceeded?.(this);
|
|
800
|
+
}
|
|
801
|
+
/** 从磁盘加载成本存储 */
|
|
802
|
+
async #loadStore() {
|
|
803
|
+
const filePath = join2(this.#costDir, "history.json");
|
|
804
|
+
try {
|
|
805
|
+
const raw = await readFile2(filePath, "utf-8");
|
|
806
|
+
return JSON.parse(raw);
|
|
807
|
+
} catch {
|
|
808
|
+
return { version: 1, daily: {} };
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
/** 保存成本存储到磁盘 */
|
|
812
|
+
async #saveStore() {
|
|
813
|
+
const store = await this.#loadStore();
|
|
814
|
+
store.daily[this.#todayDate] = this.#todaySummary;
|
|
815
|
+
const cutoffDate = getDateNDaysAgo(90);
|
|
816
|
+
const datesToRemove = Object.keys(store.daily).filter(
|
|
817
|
+
(date) => date < cutoffDate
|
|
818
|
+
);
|
|
819
|
+
for (const date of datesToRemove) {
|
|
820
|
+
delete store.daily[date];
|
|
821
|
+
}
|
|
822
|
+
await mkdir2(this.#costDir, { recursive: true });
|
|
823
|
+
const filePath = join2(this.#costDir, "history.json");
|
|
824
|
+
await writeFile2(filePath, JSON.stringify(store, null, 2), "utf-8");
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
function generateSessionId() {
|
|
828
|
+
const ts = Date.now().toString(36);
|
|
829
|
+
const rand = Math.random().toString(36).slice(2, 8);
|
|
830
|
+
return `${ts}-${rand}`;
|
|
831
|
+
}
|
|
832
|
+
function getTodayStr() {
|
|
833
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
834
|
+
}
|
|
835
|
+
function getDateNDaysAgo(n) {
|
|
836
|
+
const d = /* @__PURE__ */ new Date();
|
|
837
|
+
d.setDate(d.getDate() - n);
|
|
838
|
+
return d.toISOString().slice(0, 10);
|
|
839
|
+
}
|
|
840
|
+
function createEmptyDailySummary(date) {
|
|
841
|
+
return {
|
|
842
|
+
date,
|
|
843
|
+
totalPromptTokens: 0,
|
|
844
|
+
totalCompletionTokens: 0,
|
|
845
|
+
totalCachedTokens: 0,
|
|
846
|
+
totalCost: 0,
|
|
847
|
+
totalCalls: 0,
|
|
848
|
+
byModel: {}
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function formatMoney(yuan) {
|
|
852
|
+
if (yuan === 0) return "\xA50.00";
|
|
853
|
+
if (yuan < 0.01) return `\xA5${yuan.toFixed(6)}`;
|
|
854
|
+
if (yuan < 1) return `\xA5${yuan.toFixed(4)}`;
|
|
855
|
+
return `\xA5${yuan.toFixed(2)}`;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/ui/ToolCallBlock.tsx
|
|
859
|
+
import { Box as Box3, Text as Text4 } from "ink";
|
|
491
860
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
861
|
+
function formatArgsSummary(args) {
|
|
862
|
+
try {
|
|
863
|
+
const parsed = JSON.parse(args);
|
|
864
|
+
const lines = Object.entries(parsed).map(([key, value]) => {
|
|
865
|
+
const val = String(value);
|
|
866
|
+
const truncated = val.length > 80 ? val.slice(0, 77) + "..." : val;
|
|
867
|
+
return ` ${key}: ${truncated}`;
|
|
868
|
+
});
|
|
869
|
+
return lines.join("\n");
|
|
870
|
+
} catch {
|
|
871
|
+
const truncated = args.length > 120 ? args.slice(0, 117) + "..." : args;
|
|
872
|
+
return ` ${truncated}`;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function ToolCallBlock({ call, showPendingHint = true }) {
|
|
876
|
+
const argsDisplay = formatArgsSummary(call.arguments);
|
|
877
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 3, marginTop: 1, children: [
|
|
878
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
879
|
+
/* @__PURE__ */ jsxs3(Text4, { color: "#00ffff", bold: true, children: [
|
|
880
|
+
"\u{1F4E6} ",
|
|
881
|
+
call.name
|
|
882
|
+
] }),
|
|
883
|
+
/* @__PURE__ */ jsxs3(Text4, { color: "#555555", children: [
|
|
884
|
+
" ",
|
|
885
|
+
"\u2500".repeat(Math.max(1, 30 - call.name.length))
|
|
886
|
+
] })
|
|
887
|
+
] }),
|
|
888
|
+
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: /* @__PURE__ */ jsx3(Text4, { color: "#888888", children: argsDisplay }) }),
|
|
889
|
+
showPendingHint && /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text4, { color: "yellow", children: "\u23F3 \u7B49\u5F85\u6267\u884C\uFF08\u5DE5\u5177\u7CFB\u7EDF\u5C06\u5728\u7B2C08\u7AE0\u5B9E\u73B0\uFF09" }) })
|
|
890
|
+
] });
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// src/agent/message-builder.ts
|
|
894
|
+
function estimateMessageTokens(msg) {
|
|
895
|
+
let text = msg.content;
|
|
896
|
+
if (msg.toolCalls) {
|
|
897
|
+
for (const tc of msg.toolCalls) {
|
|
898
|
+
text += tc.name + tc.arguments;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
return estimateTokens(text) + 10;
|
|
902
|
+
}
|
|
903
|
+
function trimMessages(messages, opts) {
|
|
904
|
+
const meta = getModelMeta(opts.model);
|
|
905
|
+
const maxInputTokens = meta.contextWindow - opts.reservedForOutput;
|
|
906
|
+
const systemTokens = estimateTokens(opts.systemPrompt);
|
|
907
|
+
let remaining = maxInputTokens - systemTokens;
|
|
908
|
+
const preserved = [];
|
|
909
|
+
let roundsPreserved = 0;
|
|
910
|
+
for (let i = messages.length - 1; i >= 0 && roundsPreserved < opts.preserveRecentRounds; i--) {
|
|
911
|
+
preserved.unshift(messages[i]);
|
|
912
|
+
if (messages[i].role === "user") {
|
|
913
|
+
roundsPreserved++;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
for (const msg of preserved) {
|
|
917
|
+
remaining -= estimateMessageTokens(msg);
|
|
918
|
+
}
|
|
919
|
+
if (remaining < 0) {
|
|
920
|
+
while (preserved.length > 1 && remaining < 0) {
|
|
921
|
+
const removed = preserved.shift();
|
|
922
|
+
remaining += estimateMessageTokens(removed);
|
|
923
|
+
}
|
|
924
|
+
return [preserved, true];
|
|
925
|
+
}
|
|
926
|
+
const olderMessages = messages.slice(0, messages.length - preserved.length);
|
|
927
|
+
const kept = [];
|
|
928
|
+
for (let i = olderMessages.length - 1; i >= 0; i--) {
|
|
929
|
+
const cost = estimateMessageTokens(olderMessages[i]);
|
|
930
|
+
if (remaining - cost < 0) break;
|
|
931
|
+
remaining -= cost;
|
|
932
|
+
kept.unshift(olderMessages[i]);
|
|
933
|
+
}
|
|
934
|
+
const result = [...kept, ...preserved];
|
|
935
|
+
const trimmed = result.length < messages.length;
|
|
936
|
+
return [result, trimmed];
|
|
937
|
+
}
|
|
938
|
+
function buildApiMessages(systemPrompt, history) {
|
|
939
|
+
return [
|
|
940
|
+
{ role: "system", content: systemPrompt },
|
|
941
|
+
...history
|
|
942
|
+
];
|
|
943
|
+
}
|
|
944
|
+
function formatUsageSummary(usage) {
|
|
945
|
+
const prompt = usage.promptTokens.toLocaleString();
|
|
946
|
+
const completion = usage.completionTokens.toLocaleString();
|
|
947
|
+
const total = (usage.promptTokens + usage.completionTokens).toLocaleString();
|
|
948
|
+
let summary = `\u{1F4E5} ${prompt} + \u{1F4E4} ${completion} = \u{1F4E6} ${total} tokens`;
|
|
949
|
+
if (usage.cachedPromptTokens && usage.cachedPromptTokens > 0) {
|
|
950
|
+
const cacheRate = (usage.cachedPromptTokens / usage.promptTokens * 100).toFixed(1);
|
|
951
|
+
summary += ` \u2502 \u{1F5C4}\uFE0F \u7F13\u5B58\u547D\u4E2D ${cacheRate}%`;
|
|
952
|
+
}
|
|
953
|
+
return summary;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// src/ui/AssistantMessage.tsx
|
|
957
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
958
|
+
function formatElapsed(ms) {
|
|
959
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
960
|
+
const seconds = (ms / 1e3).toFixed(1);
|
|
961
|
+
return `${seconds}s`;
|
|
962
|
+
}
|
|
963
|
+
function AssistantMessage({
|
|
964
|
+
content,
|
|
965
|
+
toolCalls,
|
|
966
|
+
isStreaming = false,
|
|
967
|
+
usage,
|
|
968
|
+
elapsed,
|
|
969
|
+
cost,
|
|
970
|
+
model
|
|
971
|
+
}) {
|
|
972
|
+
if (!content && (!toolCalls || toolCalls.length === 0) && !isStreaming) {
|
|
973
|
+
return null;
|
|
974
|
+
}
|
|
975
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, children: [
|
|
976
|
+
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", children: [
|
|
977
|
+
/* @__PURE__ */ jsx4(Box4, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx4(Text5, { bold: true, color: "#ff00ff", children: "\u{1F916}" }) }),
|
|
978
|
+
/* @__PURE__ */ jsxs4(Box4, { flexGrow: 1, flexDirection: "column", children: [
|
|
979
|
+
content && /* @__PURE__ */ jsx4(Text5, { wrap: "wrap", children: content }),
|
|
980
|
+
isStreaming && !content && /* @__PURE__ */ jsx4(Text5, { color: "#888888", children: "..." })
|
|
981
|
+
] })
|
|
982
|
+
] }),
|
|
983
|
+
toolCalls && toolCalls.length > 0 && /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: toolCalls.map((tc, i) => /* @__PURE__ */ jsx4(ToolCallBlock, { call: tc }, tc.id ?? i)) }),
|
|
984
|
+
!isStreaming && (usage || elapsed !== void 0) && /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, marginLeft: 3, children: [
|
|
985
|
+
/* @__PURE__ */ jsx4(Text5, { color: "#555555", children: "\u2500".repeat(36) }),
|
|
986
|
+
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", gap: 2, children: [
|
|
987
|
+
cost !== void 0 && cost > 0 && /* @__PURE__ */ jsxs4(Text5, { color: "yellow", children: [
|
|
988
|
+
"\u{1F4B0} \u672C\u6B21 ",
|
|
989
|
+
formatMoney(cost)
|
|
990
|
+
] }),
|
|
991
|
+
elapsed !== void 0 && /* @__PURE__ */ jsxs4(Text5, { color: "cyan", children: [
|
|
992
|
+
"\u{1F550} ",
|
|
993
|
+
formatElapsed(elapsed)
|
|
994
|
+
] }),
|
|
995
|
+
usage && /* @__PURE__ */ jsx4(Text5, { color: "#888888", children: formatUsageSummary(usage) })
|
|
996
|
+
] })
|
|
997
|
+
] })
|
|
998
|
+
] });
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// src/provider/registry.ts
|
|
1002
|
+
var ProviderRegistry = class {
|
|
1003
|
+
#factories = /* @__PURE__ */ new Map();
|
|
1004
|
+
#instances = /* @__PURE__ */ new Map();
|
|
1005
|
+
/** 注册一个 Provider 工厂 */
|
|
1006
|
+
register(name, factory) {
|
|
1007
|
+
this.#factories.set(name, factory);
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* 获取或创建一个 Provider 实例(单例)。
|
|
1011
|
+
*
|
|
1012
|
+
* 相同的 name + baseUrl + model 组合会复用同一实例。
|
|
1013
|
+
* 如果 model 不在支持列表中,抛出 ModelNotSupportedError。
|
|
1014
|
+
*/
|
|
1015
|
+
get(name, config) {
|
|
1016
|
+
if (!isSupportedModel(config.model)) {
|
|
1017
|
+
throw new ModelNotSupportedError(config.model);
|
|
1018
|
+
}
|
|
1019
|
+
const cacheKey = `${name}:${config.baseUrl}:${config.model}`;
|
|
1020
|
+
const cached = this.#instances.get(cacheKey);
|
|
1021
|
+
if (cached) return cached;
|
|
1022
|
+
const factory = this.#factories.get(name);
|
|
1023
|
+
if (!factory) {
|
|
1024
|
+
const available = [...this.#factories.keys()].join(", ");
|
|
1025
|
+
throw new Error(`\u672A\u6CE8\u518C\u7684 Provider: "${name}"\u3002\u53EF\u7528: ${available}`);
|
|
1026
|
+
}
|
|
1027
|
+
const instance = factory(config);
|
|
1028
|
+
this.#instances.set(cacheKey, instance);
|
|
1029
|
+
return instance;
|
|
1030
|
+
}
|
|
1031
|
+
/** 列出已注册的 Provider 名称 */
|
|
1032
|
+
list() {
|
|
1033
|
+
return [...this.#factories.keys()];
|
|
1034
|
+
}
|
|
1035
|
+
/** 清除缓存的实例(用于配置热重载) */
|
|
1036
|
+
clear() {
|
|
1037
|
+
this.#instances.clear();
|
|
1038
|
+
}
|
|
1039
|
+
};
|
|
1040
|
+
var defaultRegistry = new ProviderRegistry();
|
|
1041
|
+
defaultRegistry.register("deepseek", (config) => {
|
|
1042
|
+
return new DeepSeekProvider({
|
|
1043
|
+
apiKey: config.apiKey,
|
|
1044
|
+
baseUrl: config.baseUrl,
|
|
1045
|
+
model: config.model
|
|
1046
|
+
});
|
|
1047
|
+
});
|
|
1048
|
+
function createProvider(config) {
|
|
1049
|
+
return defaultRegistry.get(config.name, config);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// src/agent/extra-prompt.ts
|
|
1053
|
+
var EXTRA_PROMPT = [
|
|
1054
|
+
"\u3010\u7EC8\u7AEF\u8F93\u51FA\u7EA6\u675F\u3011",
|
|
1055
|
+
"",
|
|
1056
|
+
"\u4F60\u7684\u56DE\u590D\u5C06\u76F4\u63A5\u6E32\u67D3\u5728\u7528\u6237\u7EC8\u7AEF\u7684\u547D\u4EE4\u884C\u754C\u9762\u4E2D\uFF0C\u4E0D\u652F\u6301 Markdown \u6E32\u67D3\u3002",
|
|
1057
|
+
"\u8BF7\u4E25\u683C\u9075\u5B88\u4EE5\u4E0B\u683C\u5F0F\u89C4\u5219\u3002",
|
|
1058
|
+
"",
|
|
1059
|
+
"\u4E00\u3001\u7981\u6B62\u4F7F\u7528\u7684\u7B26\u53F7",
|
|
1060
|
+
" - \u4E0D\u8981\u4F7F\u7528 Markdown \u6807\u9898\u7B26\u53F7\uFF08\u5355\u72EC\u6216\u8FDE\u7EED\u7684\u4E95\u53F7\uFF09",
|
|
1061
|
+
" - \u4E0D\u8981\u4F7F\u7528\u7C97\u4F53\u6216\u659C\u4F53\u7B26\u53F7\uFF08\u661F\u53F7\u3001\u4E0B\u5212\u7EBF\uFF09",
|
|
1062
|
+
" - \u4E0D\u8981\u4F7F\u7528\u4EE3\u7801\u5757\u6807\u8BB0\uFF08\u8FDE\u7EED\u4E09\u4E2A\u53CD\u5F15\u53F7\uFF09",
|
|
1063
|
+
" - \u4E0D\u8981\u4F7F\u7528\u5757\u5F15\u7528\u7B26\u53F7\uFF08\u5927\u4E8E\u53F7\uFF09",
|
|
1064
|
+
" - \u4E0D\u8981\u4F7F\u7528\u5206\u9694\u7EBF\uFF08\u8FDE\u7EED\u4E09\u4E2A\u51CF\u53F7\u6216\u661F\u53F7\uFF09",
|
|
1065
|
+
" - \u4E0D\u8981\u4F7F\u7528\u884C\u5185\u4EE3\u7801\u6807\u8BB0\uFF08\u5355\u4E2A\u53CD\u5F15\u53F7\uFF09",
|
|
1066
|
+
"",
|
|
1067
|
+
"\u4E8C\u3001\u63A8\u8350\u7684\u7EC4\u7EC7\u65B9\u5F0F",
|
|
1068
|
+
' - \u7528\u7EAF\u6587\u5B57\u63CF\u8FF0\u5C42\u7EA7\uFF0C\u4F8B\u5982"\u7B2C\u4E00\u70B9"\u3001"\u7B2C\u4E8C\u70B9"\u66FF\u4EE3\u6807\u9898\u7B26\u53F7',
|
|
1069
|
+
" - \u7528 emoji \u7B26\u53F7\u505A\u89C6\u89C9\u6807\u8BB0\uFF08\u5982 \u{1F4CC} \u{1F4E6} \u{1F4DD} \u26A0\uFE0F \u7B49\uFF09",
|
|
1070
|
+
" - \u7528\u6570\u5B57\u5E8F\u53F7\uFF081. 2. 3.\uFF09\u7EC4\u7EC7\u5217\u8868",
|
|
1071
|
+
" - \u4EE3\u7801\u793A\u4F8B\u7528\u7F29\u8FDB\uFF08\u6BCF\u884C\u524D\u52A0 4 \u4E2A\u7A7A\u683C\uFF09\u66FF\u4EE3\u4EE3\u7801\u5757\u6807\u8BB0",
|
|
1072
|
+
" - \u6587\u4EF6\u8DEF\u5F84\u3001\u547D\u4EE4\u3001\u53D8\u91CF\u540D\u76F4\u63A5\u4E66\u5199\uFF0C\u4E0D\u8981\u7528\u4EFB\u4F55\u7B26\u53F7\u5305\u88F9",
|
|
1073
|
+
"",
|
|
1074
|
+
"\u4E09\u3001\u6B63\u786E\u683C\u5F0F\u793A\u4F8B",
|
|
1075
|
+
"",
|
|
1076
|
+
" \u7B2C\u4E00\u70B9\uFF1A\u4F18\u5316\u6570\u636E\u5E93\u67E5\u8BE2",
|
|
1077
|
+
" 1. \u6DFB\u52A0\u7D22\u5F15\u5230 user_id \u5B57\u6BB5",
|
|
1078
|
+
" 2. \u7528\u8FDE\u63A5\u6C60\u66FF\u4EE3\u77ED\u8FDE\u63A5",
|
|
1079
|
+
"",
|
|
1080
|
+
" \u4EE3\u7801\u793A\u4F8B\uFF1A",
|
|
1081
|
+
" const pool = new Pool({ max: 20 });",
|
|
1082
|
+
' await pool.query("SELECT * FROM users");',
|
|
1083
|
+
"",
|
|
1084
|
+
" \u8F93\u51FA\u63D0\u793A\uFF1A",
|
|
1085
|
+
" \u{1F4CC} \u4F18\u5316\u524D\uFF1A\u67E5\u8BE2\u8017\u65F6 3.2 \u79D2",
|
|
1086
|
+
"",
|
|
1087
|
+
"\u56DB\u3001\u9519\u8BEF\u683C\u5F0F\u793A\u4F8B\uFF08\u7981\u6B62\u8FD9\u6837\u5199\uFF09",
|
|
1088
|
+
"",
|
|
1089
|
+
" \u4E95\u53F7\u4E32 \u4F18\u5316\u6570\u636E\u5E93\u67E5\u8BE2\uFF08\u8FD9\u662F\u9519\u8BEF\u7684\uFF09",
|
|
1090
|
+
" \u661F\u53F7 \u6DFB\u52A0\u7D22\u5F15\u5230 user_id \u5B57\u6BB5\uFF08\u8FD9\u662F\u9519\u8BEF\u7684\uFF09",
|
|
1091
|
+
" \u4E09\u4E2A\u53CD\u5F15\u53F7\u5F00\u59CB\u6216\u7ED3\u675F\uFF08\u8FD9\u662F\u9519\u8BEF\u7684\uFF09",
|
|
1092
|
+
"",
|
|
1093
|
+
"\u6CE8\u610F\uFF1A\u4E0A\u8FF0\u793A\u4F8B\u4E2D\u7684\u9519\u8BEF\u5199\u6CD5\u4EC5\u4F5C\u4E3A\u8BF4\u660E\uFF0C\u4F60\u7684\u56DE\u590D\u4E2D\u4E0D\u8981\u51FA\u73B0\u8FD9\u4E9B\u683C\u5F0F\u3002"
|
|
1094
|
+
].join("\n");
|
|
1095
|
+
|
|
1096
|
+
// src/agent/system-prompt.ts
|
|
1097
|
+
function buildSystemPrompt(opts) {
|
|
1098
|
+
const sections = [];
|
|
1099
|
+
sections.push(`\u4F60\u662F dskcode\uFF0C\u4E00\u4E2A\u57FA\u4E8E DeepSeek \u7684 AI \u7F16\u7A0B\u52A9\u624B\uFF0C\u8FD0\u884C\u5728\u7528\u6237\u7EC8\u7AEF\u4E2D\u3002\u4F60\u7684\u804C\u8D23\u662F\u5E2E\u52A9\u5F00\u53D1\u8005\u7F16\u5199\u3001\u7406\u89E3\u3001\u8C03\u8BD5\u548C\u91CD\u6784\u4EE3\u7801\u3002
|
|
1100
|
+
|
|
1101
|
+
## \u6838\u5FC3\u539F\u5219
|
|
1102
|
+
- \u56DE\u7B54\u4F7F\u7528\u4E2D\u6587\uFF0C\u4EE3\u7801\u6807\u8BC6\u7B26\u4FDD\u6301\u82F1\u6587
|
|
1103
|
+
- \u63D0\u4F9B\u51C6\u786E\u3001\u5B9E\u7528\u3001\u53EF\u64CD\u4F5C\u7684\u5EFA\u8BAE
|
|
1104
|
+
- \u5BF9\u4E0D\u786E\u5B9A\u7684\u5185\u5BB9\u660E\u786E\u6807\u6CE8\uFF0C\u4E0D\u7F16\u9020\u4E8B\u5B9E
|
|
1105
|
+
- \u4EE3\u7801\u793A\u4F8B\u4FDD\u6301\u7B80\u6D01\uFF0C\u9644\u5E26\u5FC5\u8981\u7684\u6CE8\u91CA`);
|
|
1106
|
+
sections.push(`## \u5F53\u524D\u6A21\u578B
|
|
1107
|
+
- \u6A21\u578B\uFF1A${opts.model}`);
|
|
1108
|
+
const now = /* @__PURE__ */ new Date();
|
|
1109
|
+
const dateStr = now.toLocaleDateString("zh-CN", {
|
|
1110
|
+
year: "numeric",
|
|
1111
|
+
month: "long",
|
|
1112
|
+
day: "numeric",
|
|
1113
|
+
weekday: "long"
|
|
1114
|
+
});
|
|
1115
|
+
const timeStr = now.toLocaleTimeString("zh-CN", {
|
|
1116
|
+
hour: "2-digit",
|
|
1117
|
+
minute: "2-digit"
|
|
1118
|
+
});
|
|
1119
|
+
sections.push(`## \u65F6\u95F4\u4E0A\u4E0B\u6587
|
|
1120
|
+
- \u5F53\u524D\u65E5\u671F\uFF1A${dateStr}
|
|
1121
|
+
- \u5F53\u524D\u65F6\u95F4\uFF1A${timeStr}
|
|
1122
|
+
- \u5DE5\u4F5C\u76EE\u5F55\uFF1A${opts.cwd}`);
|
|
1123
|
+
if (opts.tools && opts.tools.length > 0) {
|
|
1124
|
+
const toolLines = opts.tools.map((t) => {
|
|
1125
|
+
const paramInfo = t.parameters ? Object.keys(t.parameters.properties ?? {}).join(", ") : "";
|
|
1126
|
+
return `- **${t.name}**\uFF1A${t.description}${paramInfo ? `\uFF08\u53C2\u6570\uFF1A${paramInfo}\uFF09` : ""}`;
|
|
1127
|
+
}).join("\n");
|
|
1128
|
+
sections.push(`## \u53EF\u7528\u5DE5\u5177
|
|
1129
|
+
|
|
1130
|
+
\u4F60\u53EF\u4EE5\u901A\u8FC7\u5DE5\u5177\u8C03\u7528\u6267\u884C\u64CD\u4F5C\u3002\u5F53\u524D\u53EF\u7528\u7684\u5DE5\u5177\uFF1A
|
|
1131
|
+
|
|
1132
|
+
${toolLines}
|
|
1133
|
+
|
|
1134
|
+
\u8C03\u7528\u5DE5\u5177\u65F6\uFF0C\u8BF7\u4F7F\u7528\u6807\u51C6 function_call \u683C\u5F0F\u3002\u5DE5\u5177\u5C06\u7531\u7CFB\u7EDF\u6267\u884C\u5E76\u8FD4\u56DE\u7ED3\u679C\u3002`);
|
|
1135
|
+
}
|
|
1136
|
+
if (opts.projectContext) {
|
|
1137
|
+
sections.push(`## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
1138
|
+
|
|
1139
|
+
${opts.projectContext}`);
|
|
1140
|
+
}
|
|
1141
|
+
sections.push(`## \u884C\u4E3A\u7EA6\u675F
|
|
1142
|
+
- \u5982\u679C\u7528\u6237\u8BF7\u6C42\u4E0D\u660E\u786E\uFF0C\u8BF7\u4E3B\u52A8\u8BE2\u95EE\u6F84\u6E05
|
|
1143
|
+
- \u6D89\u53CA\u6587\u4EF6\u64CD\u4F5C\u65F6\uFF0C\u5148\u786E\u8BA4\u6587\u4EF6\u8DEF\u5F84
|
|
1144
|
+
- \u4E0D\u6267\u884C\u53EF\u80FD\u9020\u6210\u4E0D\u53EF\u9006\u635F\u5BB3\u7684\u64CD\u4F5C\uFF08\u5982 rm -rf\uFF09\u9664\u975E\u7528\u6237\u660E\u786E\u786E\u8BA4
|
|
1145
|
+
- \u5F53\u5DE5\u5177\u8C03\u7528\u8FD4\u56DE\u9519\u8BEF\u65F6\uFF0C\u5206\u6790\u539F\u56E0\u5E76\u5C1D\u8BD5\u4FEE\u590D`);
|
|
1146
|
+
sections.push(EXTRA_PROMPT);
|
|
1147
|
+
return sections.join("\n\n");
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// src/agent/index.ts
|
|
1151
|
+
var Session = class {
|
|
1152
|
+
#messages = [];
|
|
1153
|
+
#provider;
|
|
1154
|
+
#tools;
|
|
1155
|
+
#costTracker;
|
|
1156
|
+
#options;
|
|
1157
|
+
#abortController = new AbortController();
|
|
1158
|
+
constructor(provider, tools = [], costTracker, options) {
|
|
1159
|
+
this.#provider = provider;
|
|
1160
|
+
this.#tools = tools;
|
|
1161
|
+
this.#costTracker = costTracker ?? new CostTracker();
|
|
1162
|
+
this.#options = {
|
|
1163
|
+
cwd: options?.cwd ?? process.cwd(),
|
|
1164
|
+
maxToolRounds: options?.maxToolRounds ?? 20,
|
|
1165
|
+
reservedForOutput: options?.reservedForOutput ?? 4096,
|
|
1166
|
+
preserveRecentRounds: options?.preserveRecentRounds ?? 10,
|
|
1167
|
+
projectContext: options?.projectContext
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
// -------------------------------------------------------------------------
|
|
1171
|
+
// 公共只读属性
|
|
1172
|
+
// -------------------------------------------------------------------------
|
|
1173
|
+
get messages() {
|
|
1174
|
+
return this.#messages;
|
|
1175
|
+
}
|
|
1176
|
+
get accumulatedCost() {
|
|
1177
|
+
return this.#costTracker.sessionTotalCost;
|
|
1178
|
+
}
|
|
1179
|
+
get costTracker() {
|
|
1180
|
+
return this.#costTracker;
|
|
1181
|
+
}
|
|
1182
|
+
get model() {
|
|
1183
|
+
return this.#provider.model();
|
|
1184
|
+
}
|
|
1185
|
+
// -------------------------------------------------------------------------
|
|
1186
|
+
// 流式对话 — Agent 主循环
|
|
1187
|
+
// -------------------------------------------------------------------------
|
|
1188
|
+
/**
|
|
1189
|
+
* 执行一轮对话,以 AsyncGenerator 形式逐步 yield 事件。
|
|
1190
|
+
*
|
|
1191
|
+
* 调用方式:
|
|
1192
|
+
* ```ts
|
|
1193
|
+
* for await (const event of session.chat("你好")) {
|
|
1194
|
+
* switch (event.type) {
|
|
1195
|
+
* case "text_delta": // 追加文本
|
|
1196
|
+
* case "tool_calls": // 展示工具调用
|
|
1197
|
+
* case "usage": // 记录使用量
|
|
1198
|
+
* case "done": // 本轮完成
|
|
1199
|
+
* case "error": // 处理错误
|
|
1200
|
+
* }
|
|
1201
|
+
* }
|
|
1202
|
+
* ```
|
|
1203
|
+
*/
|
|
1204
|
+
async *chat(userInput) {
|
|
1205
|
+
this.#messages.push({ role: "user", content: userInput });
|
|
1206
|
+
const systemPrompt = this.#buildSystemPrompt();
|
|
1207
|
+
const [trimmed, wasTrimmed] = trimMessages(
|
|
1208
|
+
[...this.#messages],
|
|
1209
|
+
{
|
|
1210
|
+
model: this.#provider.model(),
|
|
1211
|
+
reservedForOutput: this.#options.reservedForOutput,
|
|
1212
|
+
systemPrompt,
|
|
1213
|
+
preserveRecentRounds: this.#options.preserveRecentRounds
|
|
1214
|
+
}
|
|
1215
|
+
);
|
|
1216
|
+
if (wasTrimmed) {
|
|
1217
|
+
}
|
|
1218
|
+
const apiMessages = buildApiMessages(systemPrompt, trimmed);
|
|
1219
|
+
const toolDefs = this.#buildToolDefinitions();
|
|
1220
|
+
const startTime = Date.now();
|
|
1221
|
+
try {
|
|
1222
|
+
const stream = this.#provider.chat(apiMessages, {
|
|
1223
|
+
signal: this.#abortController.signal
|
|
1224
|
+
// 将工具定义传给 provider(如支持 function calling)
|
|
1225
|
+
// 注意:当前 chat() 签名不含 tools 参数,
|
|
1226
|
+
// 工具定义在 system prompt 中描述,由模型通过文本方式请求
|
|
1227
|
+
});
|
|
1228
|
+
let accumulatedText = "";
|
|
1229
|
+
let lastUsage;
|
|
1230
|
+
let lastToolCalls;
|
|
1231
|
+
let lastFinishReason = null;
|
|
1232
|
+
for await (const chunk of stream) {
|
|
1233
|
+
if (chunk.content) {
|
|
1234
|
+
accumulatedText += chunk.content;
|
|
1235
|
+
yield { type: "text_delta", content: chunk.content };
|
|
1236
|
+
}
|
|
1237
|
+
if (chunk.toolCalls && chunk.toolCalls.length > 0) {
|
|
1238
|
+
lastToolCalls = chunk.toolCalls;
|
|
1239
|
+
}
|
|
1240
|
+
if (chunk.usage) {
|
|
1241
|
+
lastUsage = chunk.usage;
|
|
1242
|
+
}
|
|
1243
|
+
if (chunk.finishReason) {
|
|
1244
|
+
lastFinishReason = chunk.finishReason;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
if (lastToolCalls && lastToolCalls.length > 0) {
|
|
1248
|
+
yield { type: "tool_calls", calls: lastToolCalls };
|
|
1249
|
+
}
|
|
1250
|
+
if (lastUsage) {
|
|
1251
|
+
const modelId = this.#provider.model();
|
|
1252
|
+
const costInfo = this.#costTracker.record(lastUsage, modelId);
|
|
1253
|
+
yield { type: "usage", usage: lastUsage, model: modelId };
|
|
1254
|
+
}
|
|
1255
|
+
const assistantMsg = {
|
|
1256
|
+
role: "assistant",
|
|
1257
|
+
content: accumulatedText
|
|
1258
|
+
};
|
|
1259
|
+
if (lastToolCalls && lastToolCalls.length > 0) {
|
|
1260
|
+
assistantMsg.toolCalls = lastToolCalls;
|
|
1261
|
+
}
|
|
1262
|
+
this.#messages.push(assistantMsg);
|
|
1263
|
+
if (lastToolCalls && lastToolCalls.length > 0) {
|
|
1264
|
+
for (const tc of lastToolCalls) {
|
|
1265
|
+
this.#messages.push({
|
|
1266
|
+
role: "tool",
|
|
1267
|
+
content: `\u26A0 \u5DE5\u5177 "${tc.name}" \u7B49\u5F85\u6267\u884C\uFF08\u5DE5\u5177\u7CFB\u7EDF\u5C06\u5728\u7B2C08\u7AE0\u5B9E\u73B0\uFF09`,
|
|
1268
|
+
toolCallId: tc.id,
|
|
1269
|
+
name: tc.name
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
const elapsed = Date.now() - startTime;
|
|
1274
|
+
yield { type: "done", elapsed };
|
|
1275
|
+
} catch (err) {
|
|
1276
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
yield {
|
|
1283
|
+
type: "error",
|
|
1284
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
// -------------------------------------------------------------------------
|
|
1289
|
+
// 会话管理
|
|
1290
|
+
// -------------------------------------------------------------------------
|
|
1291
|
+
/** 取消正在进行的流式请求 */
|
|
1292
|
+
abort() {
|
|
1293
|
+
this.#abortController.abort();
|
|
1294
|
+
}
|
|
1295
|
+
/** 重置会话历史(保留 provider/tools 配置,重置成本追踪) */
|
|
1296
|
+
reset() {
|
|
1297
|
+
this.#messages.length = 0;
|
|
1298
|
+
this.#costTracker.resetSession();
|
|
1299
|
+
}
|
|
1300
|
+
// -------------------------------------------------------------------------
|
|
1301
|
+
// 内部方法
|
|
1302
|
+
// -------------------------------------------------------------------------
|
|
1303
|
+
/** 构建系统提示词 */
|
|
1304
|
+
#buildSystemPrompt() {
|
|
1305
|
+
const toolDescs = this.#tools.map((t) => ({
|
|
1306
|
+
name: t.name,
|
|
1307
|
+
description: t.description,
|
|
1308
|
+
parameters: t.parameters
|
|
1309
|
+
}));
|
|
1310
|
+
const opts = {
|
|
1311
|
+
model: this.#provider.model(),
|
|
1312
|
+
tools: toolDescs.length > 0 ? toolDescs : void 0,
|
|
1313
|
+
projectContext: this.#options.projectContext ?? void 0,
|
|
1314
|
+
cwd: this.#options.cwd
|
|
1315
|
+
};
|
|
1316
|
+
return buildSystemPrompt(opts);
|
|
1317
|
+
}
|
|
1318
|
+
/** 将注册的工具转为 ToolDefinition 格式(预留给 function calling) */
|
|
1319
|
+
#buildToolDefinitions() {
|
|
1320
|
+
return this.#tools.map((t) => ({
|
|
1321
|
+
type: "function",
|
|
1322
|
+
function: {
|
|
1323
|
+
name: t.name,
|
|
1324
|
+
description: t.description,
|
|
1325
|
+
parameters: t.parameters
|
|
1326
|
+
}
|
|
1327
|
+
}));
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1330
|
+
|
|
1331
|
+
// src/utils/gradient.ts
|
|
1332
|
+
var IDLE_GRADIENT_STOPS = [
|
|
1333
|
+
[0, 255, 65],
|
|
1334
|
+
// #00ff41
|
|
1335
|
+
[0, 255, 255],
|
|
1336
|
+
// #00ffff
|
|
1337
|
+
[189, 147, 249]
|
|
1338
|
+
// #bd93f9
|
|
1339
|
+
];
|
|
1340
|
+
var CMD_TIP_GRADIENT_STOPS = [
|
|
1341
|
+
[255, 245, 180],
|
|
1342
|
+
// #fff5b4 浅柠黄
|
|
1343
|
+
[255, 210, 80],
|
|
1344
|
+
// #ffd250 暖黄
|
|
1345
|
+
[255, 150, 50]
|
|
1346
|
+
// #ff9632 橙色
|
|
1347
|
+
];
|
|
1348
|
+
var STREAMING_GRADIENT_STOPS = [
|
|
1349
|
+
[255, 191, 0],
|
|
1350
|
+
// #ffbf00 琥珀金
|
|
1351
|
+
[210, 140, 60],
|
|
1352
|
+
// #d28c3c 焦糖
|
|
1353
|
+
[175, 70, 40]
|
|
1354
|
+
// #af4628 棕红
|
|
1355
|
+
];
|
|
1356
|
+
var GRADIENT_ANIMATION = {
|
|
1357
|
+
/** 空闲占位符动画:每帧相位步进(值越大流速越快) */
|
|
1358
|
+
idlePhaseStep: 0.06,
|
|
1359
|
+
/** 空闲占位符动画:帧间隔(ms) */
|
|
1360
|
+
idleInterval: 40,
|
|
1361
|
+
/** 流式占位符动画:每帧相位步进 */
|
|
1362
|
+
streamingPhaseStep: 0.05,
|
|
1363
|
+
/** 流式占位符动画:帧间隔(ms) */
|
|
1364
|
+
streamingInterval: 40,
|
|
1365
|
+
/** 命令提示条动画:每帧相位步进 */
|
|
1366
|
+
cmdTipPhaseStep: 0.05,
|
|
1367
|
+
/** 命令提示条动画:帧间隔(ms) */
|
|
1368
|
+
cmdTipInterval: 40
|
|
1369
|
+
};
|
|
1370
|
+
var SKIP_CHARS = /* @__PURE__ */ new Set([" ", "~", ".", "\u3002", "\uFF01", "\u{1F447}"]);
|
|
1371
|
+
function lerpStopsToHex(t, stops) {
|
|
1372
|
+
const len = stops.length;
|
|
1373
|
+
const segment = t * (len - 1);
|
|
1374
|
+
const idx = Math.min(Math.floor(segment), len - 2);
|
|
1375
|
+
const frac = segment - idx;
|
|
1376
|
+
const stop = stops[idx];
|
|
1377
|
+
const nextStop = stops[idx + 1];
|
|
1378
|
+
if (!stop || !nextStop) return "#808080";
|
|
1379
|
+
const r = Math.round(stop[0] + (nextStop[0] - stop[0]) * frac);
|
|
1380
|
+
const g = Math.round(stop[1] + (nextStop[1] - stop[1]) * frac);
|
|
1381
|
+
const b = Math.round(stop[2] + (nextStop[2] - stop[2]) * frac);
|
|
1382
|
+
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
1383
|
+
}
|
|
1384
|
+
function getGradientColors(text, phase, stops) {
|
|
1385
|
+
const len = text.length;
|
|
1386
|
+
if (len === 0) return [];
|
|
1387
|
+
return text.split("").map((ch, i) => {
|
|
1388
|
+
if (SKIP_CHARS.has(ch)) return "";
|
|
1389
|
+
const t = (i / (len - 1) + phase) % 1;
|
|
1390
|
+
return lerpStopsToHex(t, stops);
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// src/ui/ChatSession.tsx
|
|
1395
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
492
1396
|
var commandRegistry = /* @__PURE__ */ new Map();
|
|
493
1397
|
function registerCommand(name, cmd) {
|
|
494
1398
|
commandRegistry.set(name, cmd);
|
|
@@ -510,38 +1414,179 @@ registerCommand("/help", {
|
|
|
510
1414
|
}
|
|
511
1415
|
});
|
|
512
1416
|
registerCommand("/clear", { desc: "\u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2", handler: () => ({ kind: "clear" }) });
|
|
513
|
-
registerCommand("/version", { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => ({ kind: "text", content: "dskcode v0.
|
|
1417
|
+
registerCommand("/version", { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => ({ kind: "text", content: "dskcode v0.1.10" }) });
|
|
1418
|
+
registerCommand("/model", { desc: "\u5207\u6362\u6A21\u578B", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /model \u8FDB\u5165\u9009\u62E9\u754C\u9762" }) });
|
|
514
1419
|
registerCommand("/game", { desc: "\u542F\u52A8\u6E38\u620F", handler: () => ({ kind: "navigate", target: "game" }) });
|
|
515
1420
|
registerCommand("/stock", { desc: "\u67E5\u770B\u80A1\u7968\u884C\u60C5", handler: () => ({ kind: "navigate", target: "stock" }) });
|
|
516
|
-
|
|
1421
|
+
var STREAMING_PLACEHOLDERS = [
|
|
1422
|
+
"\u8BA9\u5B50\u5F39\u98DE\u4E00\u4F1A\u513F...",
|
|
1423
|
+
"\u9A6C\u4E0A\u5C31\u597D...",
|
|
1424
|
+
"\u6B63\u5728\u618B\u5927\u62DB...",
|
|
1425
|
+
"\u7A0D\u7B49\u4E00\u4E0B\u4E0B~",
|
|
1426
|
+
"\u7801\u5B57\u4E2D...",
|
|
1427
|
+
"\u8111\u5B50\u8F6C\u5F97\u98DE\u5FEB..."
|
|
1428
|
+
];
|
|
1429
|
+
var IDLE_PLACEHOLDERS = [
|
|
1430
|
+
"\u60F3\u5E72\u5565\uFF1F\u76F4\u63A5\u8BF4~",
|
|
1431
|
+
"\u6765\u5427\uFF0C\u5429\u5490\u70B9\u5565",
|
|
1432
|
+
"\u968F\u65F6\u5F85\u547D...",
|
|
1433
|
+
"\u6233\u8FD9\u91CC\u5F00\u804A...",
|
|
1434
|
+
"\u7B49\u4F60\u5F00\u53E3...",
|
|
1435
|
+
"\u5C3D\u7BA1\u4F7F\u5524~"
|
|
1436
|
+
];
|
|
1437
|
+
function pickRandom(arr) {
|
|
1438
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
1439
|
+
}
|
|
1440
|
+
function ChatSession({
|
|
1441
|
+
providerCount,
|
|
1442
|
+
toolCount,
|
|
1443
|
+
verbose,
|
|
1444
|
+
apiKey,
|
|
1445
|
+
baseUrl,
|
|
1446
|
+
costTracker: externalCostTracker,
|
|
1447
|
+
model = "deepseek-v4-flash",
|
|
1448
|
+
onLaunchGame,
|
|
1449
|
+
onLaunchStock
|
|
1450
|
+
}) {
|
|
1451
|
+
const termWidth = typeof process.stdout.columns === "number" ? process.stdout.columns : 80;
|
|
1452
|
+
const dividerWidth = Math.max(termWidth - 2, 1);
|
|
517
1453
|
const [offset, setOffset] = useState3(0);
|
|
518
|
-
const [
|
|
1454
|
+
const [displayMessages, setDisplayMessages] = useState3([]);
|
|
519
1455
|
const [input, setInput] = useState3("");
|
|
520
1456
|
const [balance, setBalance] = useState3(null);
|
|
521
1457
|
const [balanceLoading, setBalanceLoading] = useState3(false);
|
|
522
|
-
const
|
|
1458
|
+
const [todayCost, setTodayCost] = useState3(null);
|
|
1459
|
+
const [isStreaming, setIsStreaming] = useState3(false);
|
|
1460
|
+
const [streamingPlaceholder, setStreamingPlaceholder] = useState3("");
|
|
1461
|
+
const [idlePlaceholder, setIdlePlaceholder] = useState3(() => pickRandom(IDLE_PLACEHOLDERS));
|
|
1462
|
+
const [gradientColors, setGradientColors] = useState3([]);
|
|
1463
|
+
const gradientPhaseRef = useRef2(0);
|
|
1464
|
+
const [streamingGradientColors, setStreamingGradientColors] = useState3([]);
|
|
1465
|
+
const streamingPhaseRef = useRef2(0);
|
|
1466
|
+
const [currentContent, setCurrentContent] = useState3("");
|
|
1467
|
+
const [currentToolCalls, setCurrentToolCalls] = useState3([]);
|
|
1468
|
+
const [currentUsage, setCurrentUsage] = useState3(void 0);
|
|
1469
|
+
const [currentElapsed, setCurrentElapsed] = useState3(void 0);
|
|
1470
|
+
const [currentCost, setCurrentCost] = useState3(void 0);
|
|
1471
|
+
const [activeModel, setActiveModel] = useState3(model);
|
|
1472
|
+
const [streamingModel, setStreamingModel] = useState3(void 0);
|
|
1473
|
+
const [streamError, setStreamError] = useState3(void 0);
|
|
1474
|
+
const cmdTips = Array.from(getRegisteredCommands()).filter(([name]) => name !== "/exit" && name !== "/quit").map(([name, cmd]) => ({ name, desc: cmd.desc }));
|
|
1475
|
+
const [cmdTipIndex, setCmdTipIndex] = useState3(0);
|
|
1476
|
+
const [cmdTipGradientColors, setCmdTipGradientColors] = useState3([]);
|
|
1477
|
+
const cmdTipPhaseRef = useRef2(0);
|
|
1478
|
+
const [selectingModel, setSelectingModel] = useState3(false);
|
|
1479
|
+
const [modelSelectIndex, setModelSelectIndex] = useState3(0);
|
|
1480
|
+
const modelOptions = ["deepseek-v4-flash", "deepseek-v4-pro"];
|
|
1481
|
+
const sessionRef = useRef2(null);
|
|
1482
|
+
const abortRef = useRef2(null);
|
|
1483
|
+
const currentContentRef = useRef2("");
|
|
1484
|
+
const currentToolCallsRef = useRef2([]);
|
|
1485
|
+
const currentUsageRef = useRef2(void 0);
|
|
1486
|
+
const currentElapsedRef = useRef2(void 0);
|
|
1487
|
+
const currentCostRef = useRef2(void 0);
|
|
1488
|
+
const currentModelRef = useRef2(void 0);
|
|
1489
|
+
const streamErrorRef = useRef2(void 0);
|
|
1490
|
+
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {
|
|
1491
|
+
process.exit(0);
|
|
1492
|
+
});
|
|
523
1493
|
useInput(
|
|
524
1494
|
useCallback2(
|
|
525
|
-
(
|
|
526
|
-
if (
|
|
527
|
-
|
|
1495
|
+
(_input, key) => {
|
|
1496
|
+
if (selectingModel) {
|
|
1497
|
+
if (key.upArrow) {
|
|
1498
|
+
setModelSelectIndex((prev) => (prev - 1 + modelOptions.length) % modelOptions.length);
|
|
1499
|
+
} else if (key.downArrow) {
|
|
1500
|
+
setModelSelectIndex((prev) => (prev + 1) % modelOptions.length);
|
|
1501
|
+
} else if (key.return) {
|
|
1502
|
+
const selected = modelOptions[modelSelectIndex];
|
|
1503
|
+
if (selected === activeModel) {
|
|
1504
|
+
setDisplayMessages((prev) => [
|
|
1505
|
+
...prev,
|
|
1506
|
+
{ role: "assistant", content: `\u5DF2\u7ECF\u5728\u4F7F\u7528 ${SUPPORTED_MODELS[selected].displayName}` }
|
|
1507
|
+
]);
|
|
1508
|
+
} else {
|
|
1509
|
+
setActiveModel(selected);
|
|
1510
|
+
sessionRef.current?.reset();
|
|
1511
|
+
saveModelConfig(selected).catch(() => {
|
|
1512
|
+
});
|
|
1513
|
+
setDisplayMessages((prev) => [
|
|
1514
|
+
...prev,
|
|
1515
|
+
{ role: "assistant", content: `\u6A21\u578B\u5DF2\u5207\u6362\u4E3A ${SUPPORTED_MODELS[selected].displayName}\uFF08${selected}\uFF09` }
|
|
1516
|
+
]);
|
|
1517
|
+
}
|
|
1518
|
+
setSelectingModel(false);
|
|
1519
|
+
} else if (key.escape) {
|
|
1520
|
+
setSelectingModel(false);
|
|
1521
|
+
}
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
if (key.ctrl && _input === "c") {
|
|
1525
|
+
if (isStreaming) {
|
|
1526
|
+
abortRef.current?.abort();
|
|
1527
|
+
} else {
|
|
1528
|
+
handleCtrlC();
|
|
1529
|
+
}
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
if (!input && !isStreaming && _input) {
|
|
1533
|
+
setInput(_input);
|
|
528
1534
|
}
|
|
529
1535
|
},
|
|
530
|
-
[handleCtrlC]
|
|
1536
|
+
[selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input]
|
|
531
1537
|
)
|
|
532
1538
|
);
|
|
1539
|
+
useEffect3(() => {
|
|
1540
|
+
if (cmdTips.length <= 1) return;
|
|
1541
|
+
const timer = setInterval(() => {
|
|
1542
|
+
setCmdTipIndex((prev) => (prev + 1) % cmdTips.length);
|
|
1543
|
+
}, 5e3);
|
|
1544
|
+
return () => clearInterval(timer);
|
|
1545
|
+
}, [cmdTips.length]);
|
|
1546
|
+
useEffect3(() => {
|
|
1547
|
+
const tip = cmdTips[cmdTipIndex % cmdTips.length];
|
|
1548
|
+
if (!tip) {
|
|
1549
|
+
setCmdTipGradientColors([]);
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
const text = `${tip.name} ${tip.desc}`;
|
|
1553
|
+
cmdTipPhaseRef.current = 0;
|
|
1554
|
+
setCmdTipGradientColors(getGradientColors(text, 1, CMD_TIP_GRADIENT_STOPS));
|
|
1555
|
+
const interval = setInterval(() => {
|
|
1556
|
+
cmdTipPhaseRef.current = (cmdTipPhaseRef.current + GRADIENT_ANIMATION.cmdTipPhaseStep) % 1;
|
|
1557
|
+
setCmdTipGradientColors(getGradientColors(text, 1 - cmdTipPhaseRef.current, CMD_TIP_GRADIENT_STOPS));
|
|
1558
|
+
}, GRADIENT_ANIMATION.cmdTipInterval);
|
|
1559
|
+
return () => clearInterval(interval);
|
|
1560
|
+
}, [cmdTipIndex, cmdTips.length]);
|
|
533
1561
|
useEffect3(() => {
|
|
534
1562
|
const timer = setInterval(() => {
|
|
535
1563
|
setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);
|
|
536
1564
|
}, 500);
|
|
537
1565
|
return () => clearInterval(timer);
|
|
538
1566
|
}, []);
|
|
1567
|
+
useEffect3(() => {
|
|
1568
|
+
if (!apiKey || !baseUrl) return;
|
|
1569
|
+
const provider = createProvider({
|
|
1570
|
+
name: "deepseek",
|
|
1571
|
+
apiKey,
|
|
1572
|
+
baseUrl,
|
|
1573
|
+
model: activeModel
|
|
1574
|
+
});
|
|
1575
|
+
const tracker = externalCostTracker ?? new CostTracker();
|
|
1576
|
+
const session = new Session(provider, [], tracker, {
|
|
1577
|
+
cwd: process.cwd()
|
|
1578
|
+
});
|
|
1579
|
+
sessionRef.current = session;
|
|
1580
|
+
return () => {
|
|
1581
|
+
sessionRef.current = null;
|
|
1582
|
+
};
|
|
1583
|
+
}, [apiKey, baseUrl, activeModel, externalCostTracker]);
|
|
539
1584
|
useEffect3(() => {
|
|
540
1585
|
if (!apiKey || !baseUrl) return;
|
|
541
1586
|
let cancelled = false;
|
|
542
1587
|
setBalanceLoading(true);
|
|
543
|
-
import("./deepseek-
|
|
544
|
-
const provider = new
|
|
1588
|
+
import("./deepseek-OIJOG3N5.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
|
|
1589
|
+
const provider = new DeepSeekProvider2({
|
|
545
1590
|
apiKey,
|
|
546
1591
|
baseUrl,
|
|
547
1592
|
model: "deepseek-v4-flash"
|
|
@@ -561,10 +1606,61 @@ function ChatSession({ providerCount, toolCount, verbose, apiKey, baseUrl, onLau
|
|
|
561
1606
|
cancelled = true;
|
|
562
1607
|
};
|
|
563
1608
|
}, [apiKey, baseUrl]);
|
|
564
|
-
|
|
1609
|
+
useEffect3(() => {
|
|
1610
|
+
if (!externalCostTracker) return;
|
|
1611
|
+
let cancelled = false;
|
|
1612
|
+
let timer;
|
|
1613
|
+
const refresh = () => {
|
|
1614
|
+
setTodayCost(externalCostTracker.todayTotalCost);
|
|
1615
|
+
};
|
|
1616
|
+
externalCostTracker.load().then(() => {
|
|
1617
|
+
if (cancelled) return;
|
|
1618
|
+
refresh();
|
|
1619
|
+
timer = setInterval(refresh, 5e3);
|
|
1620
|
+
}).catch(() => {
|
|
1621
|
+
});
|
|
1622
|
+
return () => {
|
|
1623
|
+
cancelled = true;
|
|
1624
|
+
if (timer) clearInterval(timer);
|
|
1625
|
+
};
|
|
1626
|
+
}, [externalCostTracker]);
|
|
1627
|
+
useEffect3(() => {
|
|
1628
|
+
if (isStreaming || !idlePlaceholder) {
|
|
1629
|
+
setGradientColors([]);
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
gradientPhaseRef.current = 0;
|
|
1633
|
+
setGradientColors(getGradientColors(idlePlaceholder, 1, IDLE_GRADIENT_STOPS));
|
|
1634
|
+
const interval = setInterval(() => {
|
|
1635
|
+
gradientPhaseRef.current = (gradientPhaseRef.current + GRADIENT_ANIMATION.idlePhaseStep) % 1;
|
|
1636
|
+
setGradientColors(getGradientColors(idlePlaceholder, 1 - gradientPhaseRef.current, IDLE_GRADIENT_STOPS));
|
|
1637
|
+
}, GRADIENT_ANIMATION.idleInterval);
|
|
1638
|
+
return () => clearInterval(interval);
|
|
1639
|
+
}, [isStreaming, idlePlaceholder]);
|
|
1640
|
+
useEffect3(() => {
|
|
1641
|
+
if (!isStreaming || !streamingPlaceholder) {
|
|
1642
|
+
setStreamingGradientColors([]);
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
streamingPhaseRef.current = 0;
|
|
1646
|
+
setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1, STREAMING_GRADIENT_STOPS));
|
|
1647
|
+
const interval = setInterval(() => {
|
|
1648
|
+
streamingPhaseRef.current = (streamingPhaseRef.current + GRADIENT_ANIMATION.streamingPhaseStep) % 1;
|
|
1649
|
+
setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1 - streamingPhaseRef.current, STREAMING_GRADIENT_STOPS));
|
|
1650
|
+
}, GRADIENT_ANIMATION.streamingInterval);
|
|
1651
|
+
return () => clearInterval(interval);
|
|
1652
|
+
}, [isStreaming, streamingPlaceholder]);
|
|
1653
|
+
const handleSubmit = useCallback2(async (value) => {
|
|
565
1654
|
const trimmed = value.trim();
|
|
566
1655
|
if (!trimmed) return;
|
|
567
1656
|
if (trimmed.startsWith("/")) {
|
|
1657
|
+
if (trimmed.toLowerCase() === "/model") {
|
|
1658
|
+
const curIdx = modelOptions.indexOf(activeModel);
|
|
1659
|
+
setModelSelectIndex(curIdx >= 0 ? curIdx : 0);
|
|
1660
|
+
setSelectingModel(true);
|
|
1661
|
+
setInput("");
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
568
1664
|
const cmd = commandRegistry.get(trimmed.toLowerCase());
|
|
569
1665
|
if (cmd) {
|
|
570
1666
|
const result = cmd.handler();
|
|
@@ -573,8 +1669,9 @@ function ChatSession({ providerCount, toolCount, verbose, apiKey, baseUrl, onLau
|
|
|
573
1669
|
process.exit(0);
|
|
574
1670
|
return;
|
|
575
1671
|
case "clear":
|
|
576
|
-
|
|
1672
|
+
setDisplayMessages([]);
|
|
577
1673
|
setInput("");
|
|
1674
|
+
sessionRef.current?.reset();
|
|
578
1675
|
return;
|
|
579
1676
|
case "navigate":
|
|
580
1677
|
setInput("");
|
|
@@ -585,7 +1682,7 @@ function ChatSession({ providerCount, toolCount, verbose, apiKey, baseUrl, onLau
|
|
|
585
1682
|
}
|
|
586
1683
|
return;
|
|
587
1684
|
case "text":
|
|
588
|
-
|
|
1685
|
+
setDisplayMessages((prev) => [
|
|
589
1686
|
...prev,
|
|
590
1687
|
{ role: "user", content: trimmed },
|
|
591
1688
|
{ role: "assistant", content: result.content }
|
|
@@ -594,7 +1691,7 @@ function ChatSession({ providerCount, toolCount, verbose, apiKey, baseUrl, onLau
|
|
|
594
1691
|
return;
|
|
595
1692
|
}
|
|
596
1693
|
}
|
|
597
|
-
|
|
1694
|
+
setDisplayMessages((prev) => [
|
|
598
1695
|
...prev,
|
|
599
1696
|
{ role: "user", content: trimmed },
|
|
600
1697
|
{ role: "assistant", content: `\u672A\u77E5\u547D\u4EE4\uFF1A${trimmed}\u3002\u8F93\u5165 /help \u67E5\u770B\u3002` }
|
|
@@ -602,64 +1699,263 @@ function ChatSession({ providerCount, toolCount, verbose, apiKey, baseUrl, onLau
|
|
|
602
1699
|
setInput("");
|
|
603
1700
|
return;
|
|
604
1701
|
}
|
|
605
|
-
|
|
1702
|
+
if (!sessionRef.current) {
|
|
1703
|
+
setDisplayMessages((prev) => [
|
|
1704
|
+
...prev,
|
|
1705
|
+
{ role: "user", content: trimmed },
|
|
1706
|
+
{ role: "assistant", content: "\u26A0 \u65E0\u6CD5\u8FDE\u63A5\u5230 Provider\u3002\u8BF7\u68C0\u67E5 API Key \u548C\u7F51\u7EDC\u914D\u7F6E\u3002" }
|
|
1707
|
+
]);
|
|
1708
|
+
setInput("");
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
setDisplayMessages((prev) => [
|
|
606
1712
|
...prev,
|
|
607
|
-
{ role: "user", content: trimmed }
|
|
608
|
-
{ role: "assistant", content: "dskcode AI \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C07\u7AE0\uFF09\u3002\u5F53\u524D\u4E3A CLI \u6846\u67B6\u6F14\u793A\u6A21\u5F0F\u3002" }
|
|
1713
|
+
{ role: "user", content: trimmed }
|
|
609
1714
|
]);
|
|
610
1715
|
setInput("");
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
1716
|
+
setIsStreaming(true);
|
|
1717
|
+
setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));
|
|
1718
|
+
setCurrentContent("");
|
|
1719
|
+
setCurrentToolCalls([]);
|
|
1720
|
+
setCurrentUsage(void 0);
|
|
1721
|
+
setCurrentElapsed(void 0);
|
|
1722
|
+
setCurrentCost(void 0);
|
|
1723
|
+
setStreamingModel(void 0);
|
|
1724
|
+
setStreamError(void 0);
|
|
1725
|
+
currentContentRef.current = "";
|
|
1726
|
+
currentToolCallsRef.current = [];
|
|
1727
|
+
currentUsageRef.current = void 0;
|
|
1728
|
+
currentElapsedRef.current = void 0;
|
|
1729
|
+
currentCostRef.current = void 0;
|
|
1730
|
+
currentModelRef.current = void 0;
|
|
1731
|
+
streamErrorRef.current = void 0;
|
|
1732
|
+
const session = sessionRef.current;
|
|
1733
|
+
const abortController = new AbortController();
|
|
1734
|
+
abortRef.current = abortController;
|
|
1735
|
+
try {
|
|
1736
|
+
for await (const event of session.chat(trimmed)) {
|
|
1737
|
+
if (abortController.signal.aborted) break;
|
|
1738
|
+
switch (event.type) {
|
|
1739
|
+
case "text_delta":
|
|
1740
|
+
setCurrentContent((prev) => {
|
|
1741
|
+
const next = prev + event.content;
|
|
1742
|
+
currentContentRef.current = next;
|
|
1743
|
+
return next;
|
|
1744
|
+
});
|
|
1745
|
+
break;
|
|
1746
|
+
case "tool_calls":
|
|
1747
|
+
setCurrentToolCalls((prev) => {
|
|
1748
|
+
const next = [...prev, ...event.calls];
|
|
1749
|
+
currentToolCallsRef.current = next;
|
|
1750
|
+
return next;
|
|
1751
|
+
});
|
|
1752
|
+
break;
|
|
1753
|
+
case "usage":
|
|
1754
|
+
setCurrentUsage(event.usage);
|
|
1755
|
+
setStreamingModel(event.model);
|
|
1756
|
+
currentUsageRef.current = event.usage;
|
|
1757
|
+
currentModelRef.current = event.model;
|
|
1758
|
+
break;
|
|
1759
|
+
case "done":
|
|
1760
|
+
setCurrentElapsed(event.elapsed);
|
|
1761
|
+
currentElapsedRef.current = event.elapsed;
|
|
1762
|
+
break;
|
|
1763
|
+
case "error":
|
|
1764
|
+
setStreamError(event.error.message);
|
|
1765
|
+
streamErrorRef.current = event.error.message;
|
|
1766
|
+
break;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
} catch (err) {
|
|
1770
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1771
|
+
setStreamError(msg);
|
|
1772
|
+
streamErrorRef.current = msg;
|
|
1773
|
+
} finally {
|
|
1774
|
+
setIsStreaming(false);
|
|
1775
|
+
setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));
|
|
1776
|
+
abortRef.current = null;
|
|
1777
|
+
const finContent = currentContentRef.current;
|
|
1778
|
+
const finToolCalls = currentToolCallsRef.current.length > 0 ? currentToolCallsRef.current : void 0;
|
|
1779
|
+
const finStreamError = streamErrorRef.current;
|
|
1780
|
+
if (finContent || finToolCalls || finStreamError) {
|
|
1781
|
+
const completed = {
|
|
1782
|
+
content: finStreamError ? `\u26A0 \u8BF7\u6C42\u51FA\u9519\uFF1A${finStreamError}` : finContent || "",
|
|
1783
|
+
toolCalls: finToolCalls,
|
|
1784
|
+
usage: currentUsageRef.current,
|
|
1785
|
+
elapsed: currentElapsedRef.current,
|
|
1786
|
+
cost: currentCostRef.current,
|
|
1787
|
+
model: currentModelRef.current
|
|
1788
|
+
};
|
|
1789
|
+
setDisplayMessages((prev) => [
|
|
1790
|
+
...prev,
|
|
1791
|
+
{
|
|
1792
|
+
role: "assistant",
|
|
1793
|
+
content: completed.content,
|
|
1794
|
+
assistantDetail: completed
|
|
1795
|
+
}
|
|
1796
|
+
]);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
}, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls]);
|
|
1800
|
+
useEffect3(() => {
|
|
1801
|
+
if (!isStreaming && externalCostTracker) {
|
|
1802
|
+
setTodayCost(externalCostTracker.todayTotalCost);
|
|
1803
|
+
}
|
|
1804
|
+
}, [isStreaming, externalCostTracker]);
|
|
1805
|
+
useEffect3(() => {
|
|
1806
|
+
if (currentUsage && streamingModel && !currentCost) {
|
|
1807
|
+
import("./models-NQGNKB4T.js").then(({ calculateCost: calculateCost2 }) => {
|
|
1808
|
+
const cost = calculateCost2(currentUsage, streamingModel);
|
|
1809
|
+
setCurrentCost(cost.totalCost);
|
|
1810
|
+
currentCostRef.current = cost.totalCost;
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
}, [currentUsage, streamingModel, currentCost]);
|
|
1814
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
|
|
1815
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", marginBottom: 1, children: [
|
|
1816
|
+
/* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
|
|
615
1817
|
const colorIndex = (i + offset) % CYBER_PALETTE.length;
|
|
616
|
-
return /* @__PURE__ */
|
|
1818
|
+
return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text6, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }) }, i);
|
|
617
1819
|
}) }),
|
|
618
|
-
/* @__PURE__ */
|
|
619
|
-
/* @__PURE__ */
|
|
1820
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", justifyContent: "center", children: [
|
|
1821
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "#00ff41", children: [
|
|
620
1822
|
" \u2714 ",
|
|
621
1823
|
"\u5DF2\u52A0\u8F7D ",
|
|
622
1824
|
providerCount,
|
|
623
1825
|
" \u4E2A Provider"
|
|
624
1826
|
] }),
|
|
625
|
-
/* @__PURE__ */
|
|
1827
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "#00ffff", children: [
|
|
626
1828
|
" \u2139 ",
|
|
627
1829
|
"\u5DF2\u5C31\u7EEA ",
|
|
628
1830
|
toolCount,
|
|
629
1831
|
" \u4E2A\u5DE5\u5177"
|
|
630
1832
|
] }),
|
|
631
|
-
|
|
1833
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "#00ffff", children: [
|
|
1834
|
+
" \u{1F527} \u6A21\u578B ",
|
|
1835
|
+
SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel
|
|
1836
|
+
] }),
|
|
1837
|
+
cmdTips.length > 0 && (() => {
|
|
1838
|
+
const tip = cmdTips[cmdTipIndex % cmdTips.length];
|
|
1839
|
+
if (!tip) return null;
|
|
1840
|
+
const text = `${tip.name} ${tip.desc}`;
|
|
1841
|
+
return /* @__PURE__ */ jsxs5(Text6, { children: [
|
|
1842
|
+
/* @__PURE__ */ jsx5(Text6, { color: "#808080", children: " \u{1F4A1} " }),
|
|
1843
|
+
cmdTipGradientColors.length > 0 ? text.split("").map((ch, i) => /* @__PURE__ */ jsx5(Text6, { color: cmdTipGradientColors[i] || void 0, children: ch }, i)) : /* @__PURE__ */ jsx5(Text6, { color: "#808080", children: text })
|
|
1844
|
+
] });
|
|
1845
|
+
})(),
|
|
1846
|
+
verbose ? /* @__PURE__ */ jsx5(Text6, { color: "#ff1493", children: " \u26A1 Verbose" }) : null
|
|
632
1847
|
] }),
|
|
633
|
-
/* @__PURE__ */
|
|
634
|
-
"\
|
|
635
|
-
|
|
636
|
-
|
|
1848
|
+
/* @__PURE__ */ jsxs5(Box5, { flexGrow: 1, flexDirection: "column", justifyContent: "center", alignItems: "flex-end", children: [
|
|
1849
|
+
balanceLoading && balance === null ? /* @__PURE__ */ jsx5(Text6, { color: "yellow", children: " \u23F3 \u67E5\u8BE2\u4F59\u989D..." }) : balance !== null ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", children: [
|
|
1850
|
+
/* @__PURE__ */ jsx5(Text6, { color: "yellow", children: "\u{1F4B0} " }),
|
|
1851
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "yellow", children: [
|
|
1852
|
+
"\u4F59\u989D \xA5",
|
|
1853
|
+
balance.toFixed(2)
|
|
1854
|
+
] })
|
|
1855
|
+
] }) : null,
|
|
1856
|
+
todayCost !== null ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", children: [
|
|
1857
|
+
/* @__PURE__ */ jsx5(Text6, { color: "cyan", children: "\u{1F4CA} " }),
|
|
1858
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "cyan", children: [
|
|
1859
|
+
"\u4ECA\u65E5 \xA5",
|
|
1860
|
+
formatMoney(todayCost).replace("\xA5", "")
|
|
1861
|
+
] })
|
|
1862
|
+
] }) : null
|
|
1863
|
+
] })
|
|
637
1864
|
] }),
|
|
638
|
-
/* @__PURE__ */
|
|
639
|
-
/* @__PURE__ */
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
1865
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
1866
|
+
/* @__PURE__ */ jsx5(Static, { items: displayMessages, children: (msg, i) => {
|
|
1867
|
+
if (msg.role === "user") {
|
|
1868
|
+
return /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
1869
|
+
/* @__PURE__ */ jsx5(Box5, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text6, { bold: true, color: "#00ff41", children: "\u{1F464}" }) }),
|
|
1870
|
+
/* @__PURE__ */ jsx5(Box5, { flexGrow: 1, children: /* @__PURE__ */ jsx5(Text6, { wrap: "wrap", children: msg.content }) })
|
|
1871
|
+
] }, i);
|
|
1872
|
+
}
|
|
1873
|
+
const detail = msg.assistantDetail;
|
|
1874
|
+
return /* @__PURE__ */ jsx5(
|
|
1875
|
+
AssistantMessage,
|
|
1876
|
+
{
|
|
1877
|
+
content: msg.content,
|
|
1878
|
+
toolCalls: detail?.toolCalls,
|
|
1879
|
+
isStreaming: false,
|
|
1880
|
+
usage: detail?.usage,
|
|
1881
|
+
elapsed: detail?.elapsed,
|
|
1882
|
+
cost: detail?.cost,
|
|
1883
|
+
model: detail?.model
|
|
1884
|
+
},
|
|
1885
|
+
i
|
|
1886
|
+
);
|
|
1887
|
+
} }),
|
|
1888
|
+
isStreaming && /* @__PURE__ */ jsx5(
|
|
1889
|
+
AssistantMessage,
|
|
646
1890
|
{
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
placeholder: "\u8F93\u5165\u4F60\u7684\u95EE\u9898..."
|
|
1891
|
+
content: currentContent,
|
|
1892
|
+
toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0,
|
|
1893
|
+
isStreaming: true
|
|
651
1894
|
}
|
|
652
|
-
)
|
|
1895
|
+
),
|
|
1896
|
+
isStreaming && !currentContent && currentToolCalls.length === 0 && /* @__PURE__ */ jsx5(Box5, { marginTop: 1, marginLeft: 4, children: /* @__PURE__ */ jsx5(Spinner, { type: "dots", label: "\u601D\u8003\u4E2D..." }) }),
|
|
1897
|
+
!isStreaming && streamError && /* @__PURE__ */ jsx5(Box5, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs5(Text6, { color: "red", children: [
|
|
1898
|
+
"\u26A0 ",
|
|
1899
|
+
streamError
|
|
1900
|
+
] }) })
|
|
653
1901
|
] }),
|
|
654
|
-
/* @__PURE__ */
|
|
655
|
-
|
|
1902
|
+
selectingModel ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
1903
|
+
/* @__PURE__ */ jsx5(Text6, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }),
|
|
1904
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
1905
|
+
/* @__PURE__ */ jsx5(Text6, { bold: true, color: "#00ffff", children: "\u9009\u62E9\u6A21\u578B\uFF1A" }),
|
|
1906
|
+
modelOptions.map((id, i) => {
|
|
1907
|
+
const meta = SUPPORTED_MODELS[id];
|
|
1908
|
+
const isCurrent = id === activeModel;
|
|
1909
|
+
const isSelected = i === modelSelectIndex;
|
|
1910
|
+
const marker = isSelected ? " > " : " ";
|
|
1911
|
+
const suffix = isCurrent ? " (\u5F53\u524D)" : "";
|
|
1912
|
+
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1913
|
+
/* @__PURE__ */ jsxs5(
|
|
1914
|
+
Text6,
|
|
1915
|
+
{
|
|
1916
|
+
color: isSelected ? "#00ff41" : void 0,
|
|
1917
|
+
bold: isSelected,
|
|
1918
|
+
children: [
|
|
1919
|
+
marker,
|
|
1920
|
+
meta.displayName,
|
|
1921
|
+
suffix
|
|
1922
|
+
]
|
|
1923
|
+
}
|
|
1924
|
+
),
|
|
1925
|
+
isSelected && /* @__PURE__ */ jsxs5(Text6, { color: "#808080", children: [
|
|
1926
|
+
" \u2014 ",
|
|
1927
|
+
id
|
|
1928
|
+
] })
|
|
1929
|
+
] }, id);
|
|
1930
|
+
}),
|
|
1931
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
|
|
1932
|
+
] }),
|
|
1933
|
+
/* @__PURE__ */ jsx5(Text6, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
|
|
1934
|
+
] }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
1935
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
|
|
1936
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1937
|
+
/* @__PURE__ */ jsx5(Box5, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text6, { bold: true, color: "#00ff41", children: "\u26A1" }) }),
|
|
1938
|
+
/* @__PURE__ */ jsx5(Box5, { flexGrow: 1, children: !input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? /* @__PURE__ */ jsx5(Text6, { children: idlePlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx5(Text6, { color: gradientColors[i] ?? void 0, children: ch }, i)) }) : !input && isStreaming && streamingPlaceholder && streamingGradientColors.length > 0 ? /* @__PURE__ */ jsx5(Text6, { children: streamingPlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx5(Text6, { color: streamingGradientColors[i] ?? void 0, children: ch }, i)) }) : /* @__PURE__ */ jsx5(
|
|
1939
|
+
TextInput,
|
|
1940
|
+
{
|
|
1941
|
+
value: input,
|
|
1942
|
+
onChange: setInput,
|
|
1943
|
+
onSubmit: handleSubmit,
|
|
1944
|
+
placeholder: ""
|
|
1945
|
+
}
|
|
1946
|
+
) })
|
|
1947
|
+
] }),
|
|
1948
|
+
/* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text6, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) })
|
|
1949
|
+
] }),
|
|
1950
|
+
doubleCtrlC && !isStreaming && /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) }),
|
|
1951
|
+
isStreaming && /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "yellow", dimColor: true, children: " \u63D0\u793A\uFF1A\u6309 Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42" }) })
|
|
656
1952
|
] });
|
|
657
1953
|
}
|
|
658
1954
|
|
|
659
1955
|
// src/ui/GamePicker.tsx
|
|
660
|
-
import { Box as
|
|
1956
|
+
import { Box as Box6, Text as Text7, useInput as useInput2 } from "ink";
|
|
661
1957
|
import { useState as useState4, useCallback as useCallback3 } from "react";
|
|
662
|
-
import { jsx as
|
|
1958
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
663
1959
|
function GamePicker({ games, onSelect, onExit, onBackToChat }) {
|
|
664
1960
|
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
665
1961
|
const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));
|
|
@@ -687,18 +1983,18 @@ function GamePicker({ games, onSelect, onExit, onBackToChat }) {
|
|
|
687
1983
|
[games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC]
|
|
688
1984
|
)
|
|
689
1985
|
);
|
|
690
|
-
return /* @__PURE__ */
|
|
691
|
-
/* @__PURE__ */
|
|
692
|
-
/* @__PURE__ */
|
|
1986
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1987
|
+
/* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(Text7, { bold: true, color: "#00ffff", children: "\u{1F3AE} \u6E38\u620F\u5217\u8868" }) }),
|
|
1988
|
+
/* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: games.map((game, index) => {
|
|
693
1989
|
const isSelected = index === selectedIndex;
|
|
694
|
-
return /* @__PURE__ */
|
|
695
|
-
/* @__PURE__ */
|
|
696
|
-
/* @__PURE__ */
|
|
697
|
-
/* @__PURE__ */
|
|
1990
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "row", children: [
|
|
1991
|
+
/* @__PURE__ */ jsx6(Box6, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx6(Text7, { bold: true, color: "#00ff41", children: "\u25B8 " }) : /* @__PURE__ */ jsx6(Text7, { children: " " }) }),
|
|
1992
|
+
/* @__PURE__ */ jsx6(Box6, { width: 20, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text7, { bold: true, color: isSelected ? "#00ff41" : "#ffffff", children: game.name }) }),
|
|
1993
|
+
/* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsx6(Text7, { color: "#888888", children: game.description }) })
|
|
698
1994
|
] }, game.id);
|
|
699
1995
|
}) }),
|
|
700
|
-
/* @__PURE__ */
|
|
701
|
-
doubleCtrlC && /* @__PURE__ */
|
|
1996
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text7, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) }),
|
|
1997
|
+
doubleCtrlC && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text7, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
|
|
702
1998
|
] });
|
|
703
1999
|
}
|
|
704
2000
|
|
|
@@ -715,9 +2011,9 @@ function listGames() {
|
|
|
715
2011
|
}
|
|
716
2012
|
|
|
717
2013
|
// src/game/brick-breaker/index.tsx
|
|
718
|
-
import { Box as
|
|
719
|
-
import { useState as useState5, useEffect as useEffect4, useRef as
|
|
720
|
-
import { jsx as
|
|
2014
|
+
import { Box as Box7, Text as Text8, useInput as useInput3, render as render2 } from "ink";
|
|
2015
|
+
import { useState as useState5, useEffect as useEffect4, useRef as useRef3, useCallback as useCallback4 } from "react";
|
|
2016
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
721
2017
|
var GAME_WIDTH = 40;
|
|
722
2018
|
var GAME_HEIGHT = 18;
|
|
723
2019
|
var PADDLE_WIDTH = 9;
|
|
@@ -850,9 +2146,9 @@ function buildBoard(state) {
|
|
|
850
2146
|
function BrickBreakerGame({ onExit: _onExit }) {
|
|
851
2147
|
const [initialLevel, setInitialLevel] = useState5(1);
|
|
852
2148
|
const [selectingLevel, setSelectingLevel] = useState5(true);
|
|
853
|
-
const stateRef =
|
|
2149
|
+
const stateRef = useRef3(createInitialState(initialLevel));
|
|
854
2150
|
const [tick, setTick] = useState5(0);
|
|
855
|
-
const onExitRef =
|
|
2151
|
+
const onExitRef = useRef3(_onExit);
|
|
856
2152
|
onExitRef.current = _onExit;
|
|
857
2153
|
useEffect4(() => {
|
|
858
2154
|
if (selectingLevel) return;
|
|
@@ -907,49 +2203,49 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
907
2203
|
const board = buildBoard(s);
|
|
908
2204
|
const def = getLevel(s.level);
|
|
909
2205
|
void tick;
|
|
910
|
-
return /* @__PURE__ */
|
|
911
|
-
/* @__PURE__ */
|
|
912
|
-
/* @__PURE__ */
|
|
2206
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
2207
|
+
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "row", children: [
|
|
2208
|
+
/* @__PURE__ */ jsx7(Box7, { width: 20, children: /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
913
2209
|
"\u5173\u5361 ",
|
|
914
2210
|
s.level,
|
|
915
2211
|
": ",
|
|
916
|
-
/* @__PURE__ */
|
|
2212
|
+
/* @__PURE__ */ jsx7(Text8, { color: "cyan", children: def.desc })
|
|
917
2213
|
] }) }),
|
|
918
|
-
/* @__PURE__ */
|
|
2214
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
919
2215
|
"\u5206\u6570: ",
|
|
920
|
-
/* @__PURE__ */
|
|
2216
|
+
/* @__PURE__ */ jsx7(Text8, { color: "yellow", children: String(s.score).padStart(3, "0") })
|
|
921
2217
|
] }) }),
|
|
922
|
-
/* @__PURE__ */
|
|
2218
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
923
2219
|
"\u751F\u547D: ",
|
|
924
|
-
/* @__PURE__ */
|
|
2220
|
+
/* @__PURE__ */ jsx7(Text8, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) })
|
|
925
2221
|
] }) }),
|
|
926
|
-
/* @__PURE__ */
|
|
2222
|
+
/* @__PURE__ */ jsx7(Box7, { width: 10, children: /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
927
2223
|
"\u7816\u5757: ",
|
|
928
|
-
/* @__PURE__ */
|
|
2224
|
+
/* @__PURE__ */ jsx7(Text8, { color: "cyan", children: aliveCount })
|
|
929
2225
|
] }) }),
|
|
930
|
-
/* @__PURE__ */
|
|
2226
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text8, { color: s.paused ? "gray" : "green", children: [
|
|
931
2227
|
"[",
|
|
932
2228
|
s.paused ? "\u6682\u505C" : "\u8FD0\u884C\u4E2D",
|
|
933
2229
|
"]"
|
|
934
2230
|
] }) })
|
|
935
2231
|
] }),
|
|
936
|
-
/* @__PURE__ */
|
|
937
|
-
/* @__PURE__ */
|
|
2232
|
+
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
2233
|
+
/* @__PURE__ */ jsxs7(Text8, { children: [
|
|
938
2234
|
"\u250C",
|
|
939
2235
|
"\u2500".repeat(GAME_WIDTH),
|
|
940
2236
|
"\u2510"
|
|
941
2237
|
] }),
|
|
942
|
-
/* @__PURE__ */
|
|
943
|
-
/* @__PURE__ */
|
|
2238
|
+
/* @__PURE__ */ jsx7(Text8, { children: selectingLevel ? " \x1B[90m\u9009\u62E9\u5173\u5361\u540E\u5F00\u59CB...\x1B[0m" : board }),
|
|
2239
|
+
/* @__PURE__ */ jsxs7(Text8, { children: [
|
|
944
2240
|
"\u2514",
|
|
945
2241
|
"\u2500".repeat(GAME_WIDTH),
|
|
946
2242
|
"\u2518"
|
|
947
2243
|
] })
|
|
948
2244
|
] }),
|
|
949
|
-
selectingLevel && /* @__PURE__ */
|
|
950
|
-
/* @__PURE__ */
|
|
951
|
-
/* @__PURE__ */
|
|
952
|
-
/* @__PURE__ */
|
|
2245
|
+
selectingLevel && /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2246
|
+
/* @__PURE__ */ jsx7(Text8, { bold: true, color: "yellow", children: "\u9009\u62E9\u5173\u5361" }),
|
|
2247
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "row", flexWrap: "wrap", children: LEVELS.map((lv, i) => /* @__PURE__ */ jsx7(Box7, { width: 22, children: /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
2248
|
+
/* @__PURE__ */ jsx7(Text8, { color: initialLevel === i + 1 ? "green" : "white", children: i + 1 === 10 ? "0" : String(i + 1) }),
|
|
953
2249
|
". ",
|
|
954
2250
|
lv.desc,
|
|
955
2251
|
" (",
|
|
@@ -958,16 +2254,16 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
958
2254
|
lv.cols,
|
|
959
2255
|
")"
|
|
960
2256
|
] }) }, i)) }),
|
|
961
|
-
/* @__PURE__ */
|
|
2257
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6309\u6570\u5B57\u9009\u5173 q \u9000\u51FA" }) })
|
|
962
2258
|
] }),
|
|
963
|
-
!selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */
|
|
964
|
-
/* @__PURE__ */
|
|
965
|
-
/* @__PURE__ */
|
|
2259
|
+
!selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
|
|
2260
|
+
/* @__PURE__ */ jsx7(Text8, { bold: true, color: s.gameOver ? "red" : "green", children: s.gameOver ? "\u6E38\u620F\u7ED3\u675F\uFF01" : "\u606D\u559C\u901A\u5173\uFF01" }),
|
|
2261
|
+
/* @__PURE__ */ jsxs7(Text8, { children: [
|
|
966
2262
|
" \u5206\u6570: ",
|
|
967
|
-
/* @__PURE__ */
|
|
2263
|
+
/* @__PURE__ */ jsx7(Text8, { color: "yellow", children: s.score })
|
|
968
2264
|
] })
|
|
969
2265
|
] }),
|
|
970
|
-
!selectingLevel && /* @__PURE__ */
|
|
2266
|
+
!selectingLevel && /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: s.gameOver || s.win ? /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 r \u91CD\u5F00 l \u9009\u5173 q \u9000\u51FA" }) : /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 p \u6682\u505C q \u9000\u51FA" }) })
|
|
971
2267
|
] });
|
|
972
2268
|
}
|
|
973
2269
|
var brick_breaker_default = {
|
|
@@ -977,7 +2273,7 @@ var brick_breaker_default = {
|
|
|
977
2273
|
play: async () => {
|
|
978
2274
|
await new Promise((resolve) => {
|
|
979
2275
|
const { unmount } = render2(
|
|
980
|
-
/* @__PURE__ */
|
|
2276
|
+
/* @__PURE__ */ jsx7(BrickBreakerGame, { onExit: () => {
|
|
981
2277
|
unmount();
|
|
982
2278
|
resolve();
|
|
983
2279
|
} })
|
|
@@ -987,9 +2283,9 @@ var brick_breaker_default = {
|
|
|
987
2283
|
};
|
|
988
2284
|
|
|
989
2285
|
// src/game/coder-check/index.tsx
|
|
990
|
-
import { Box as
|
|
991
|
-
import { useState as useState6, useEffect as useEffect5, useRef as
|
|
992
|
-
import { jsx as
|
|
2286
|
+
import { Box as Box8, Text as Text9, useInput as useInput4, render as render3 } from "ink";
|
|
2287
|
+
import { useState as useState6, useEffect as useEffect5, useRef as useRef4, useCallback as useCallback5 } from "react";
|
|
2288
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
993
2289
|
var GAME_W = 66;
|
|
994
2290
|
var GAME_H = 20;
|
|
995
2291
|
var SCORE_H = 6;
|
|
@@ -1380,10 +2676,10 @@ function buildGameView(s, scoreLines, scoreColor, message) {
|
|
|
1380
2676
|
return rows;
|
|
1381
2677
|
}
|
|
1382
2678
|
function CoderCheck({ onExit: _onExit }) {
|
|
1383
|
-
const stateRef =
|
|
2679
|
+
const stateRef = useRef4(createInitialState2());
|
|
1384
2680
|
const [tick, setTick] = useState6(0);
|
|
1385
2681
|
const [colorOffset, setColorOffset] = useState6(0);
|
|
1386
|
-
const onExitRef =
|
|
2682
|
+
const onExitRef = useRef4(_onExit);
|
|
1387
2683
|
onExitRef.current = _onExit;
|
|
1388
2684
|
useEffect5(() => {
|
|
1389
2685
|
const timer = setInterval(() => {
|
|
@@ -1460,44 +2756,44 @@ function CoderCheck({ onExit: _onExit }) {
|
|
|
1460
2756
|
const scoreLines = buildScoreLines(scoreStr);
|
|
1461
2757
|
const view = buildGameView(s, scoreLines, scoreColor, s.message);
|
|
1462
2758
|
void tick;
|
|
1463
|
-
return /* @__PURE__ */
|
|
1464
|
-
/* @__PURE__ */
|
|
2759
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
|
|
2760
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "row", children: /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
1465
2761
|
"\u751F\u547D ",
|
|
1466
|
-
/* @__PURE__ */
|
|
2762
|
+
/* @__PURE__ */ jsx8(Text9, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) }),
|
|
1467
2763
|
" ",
|
|
1468
2764
|
"\u901F\u5EA6 ",
|
|
1469
|
-
/* @__PURE__ */
|
|
2765
|
+
/* @__PURE__ */ jsxs8(Text9, { color: "cyan", children: [
|
|
1470
2766
|
"Lv.",
|
|
1471
2767
|
Math.floor(s.speed * 10)
|
|
1472
2768
|
] })
|
|
1473
2769
|
] }) }),
|
|
1474
|
-
!s.gameOver && s.target && /* @__PURE__ */
|
|
2770
|
+
!s.gameOver && s.target && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
1475
2771
|
"\u6253\u5B57: ",
|
|
1476
|
-
/* @__PURE__ */
|
|
1477
|
-
/* @__PURE__ */
|
|
2772
|
+
/* @__PURE__ */ jsx8(Text9, { color: "green", children: s.typed }),
|
|
2773
|
+
/* @__PURE__ */ jsx8(Text9, { color: "white", children: s.target.slice(s.typed.length) })
|
|
1478
2774
|
] }) }),
|
|
1479
|
-
/* @__PURE__ */
|
|
1480
|
-
/* @__PURE__ */
|
|
2775
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
2776
|
+
/* @__PURE__ */ jsxs8(Text9, { children: [
|
|
1481
2777
|
"\u250C",
|
|
1482
2778
|
"\u2500".repeat(GAME_W),
|
|
1483
2779
|
"\u2510"
|
|
1484
2780
|
] }),
|
|
1485
|
-
view.map((row, i) => /* @__PURE__ */
|
|
1486
|
-
/* @__PURE__ */
|
|
2781
|
+
view.map((row, i) => /* @__PURE__ */ jsx8(Text9, { children: `\u2502${row}\u2502` }, i)),
|
|
2782
|
+
/* @__PURE__ */ jsxs8(Text9, { children: [
|
|
1487
2783
|
"\u2514",
|
|
1488
2784
|
"\u2500".repeat(GAME_W),
|
|
1489
2785
|
"\u2518"
|
|
1490
2786
|
] })
|
|
1491
2787
|
] }),
|
|
1492
|
-
s.gameOver && /* @__PURE__ */
|
|
1493
|
-
/* @__PURE__ */
|
|
1494
|
-
/* @__PURE__ */
|
|
2788
|
+
s.gameOver && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
2789
|
+
/* @__PURE__ */ jsx8(Text9, { bold: true, color: "red", children: "\u6E38\u620F\u7ED3\u675F\uFF01" }),
|
|
2790
|
+
/* @__PURE__ */ jsxs8(Text9, { children: [
|
|
1495
2791
|
" \u5F97\u5206: ",
|
|
1496
|
-
/* @__PURE__ */
|
|
2792
|
+
/* @__PURE__ */ jsx8(Text9, { color: "yellow", children: s.score }),
|
|
1497
2793
|
" r \u91CD\u5F00 q \u9000\u51FA"
|
|
1498
2794
|
] })
|
|
1499
2795
|
] }),
|
|
1500
|
-
/* @__PURE__ */
|
|
2796
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text9, { dimColor: true, children: "\u6253\u5B57\u6D88\u9664\u5355\u8BCD \u7A7A\u683C/Ctrl+P\u6682\u505C Ctrl+Q\u9000\u51FA" }) })
|
|
1501
2797
|
] });
|
|
1502
2798
|
}
|
|
1503
2799
|
var coder_check_default = {
|
|
@@ -1507,7 +2803,7 @@ var coder_check_default = {
|
|
|
1507
2803
|
play: async () => {
|
|
1508
2804
|
await new Promise((resolve) => {
|
|
1509
2805
|
const { unmount } = render3(
|
|
1510
|
-
/* @__PURE__ */
|
|
2806
|
+
/* @__PURE__ */ jsx8(CoderCheck, { onExit: () => {
|
|
1511
2807
|
unmount();
|
|
1512
2808
|
resolve();
|
|
1513
2809
|
} })
|
|
@@ -1528,10 +2824,10 @@ import { render as render4 } from "ink";
|
|
|
1528
2824
|
import chalk3 from "chalk";
|
|
1529
2825
|
|
|
1530
2826
|
// src/stock/StockList.tsx
|
|
1531
|
-
import { Box as
|
|
2827
|
+
import { Box as Box9, Text as Text10, useInput as useInput5 } from "ink";
|
|
1532
2828
|
import { useState as useState7, useCallback as useCallback6, useEffect as useEffect6 } from "react";
|
|
1533
2829
|
import asciichart from "asciichart";
|
|
1534
|
-
import { jsx as
|
|
2830
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1535
2831
|
var MINUTE_API = "https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1";
|
|
1536
2832
|
function normalizeApiCode(code) {
|
|
1537
2833
|
if (code.startsWith("sh") || code.startsWith("sz")) return code;
|
|
@@ -1728,57 +3024,57 @@ function StockList({ codes, onExit, onBackToChat }) {
|
|
|
1728
3024
|
);
|
|
1729
3025
|
if (detailView) {
|
|
1730
3026
|
if (detailLoading) {
|
|
1731
|
-
return /* @__PURE__ */
|
|
3027
|
+
return /* @__PURE__ */ jsx9(Box9, { paddingLeft: 1, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
|
|
1732
3028
|
}
|
|
1733
3029
|
return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown, currentTime);
|
|
1734
3030
|
}
|
|
1735
|
-
return /* @__PURE__ */
|
|
1736
|
-
/* @__PURE__ */
|
|
1737
|
-
/* @__PURE__ */
|
|
1738
|
-
/* @__PURE__ */
|
|
3031
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
3032
|
+
/* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
3033
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, color: "#00ffff", children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
|
|
3034
|
+
/* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
|
|
1739
3035
|
" \u{1F550} ",
|
|
1740
3036
|
currentTime
|
|
1741
3037
|
] }),
|
|
1742
|
-
/* @__PURE__ */
|
|
3038
|
+
/* @__PURE__ */ jsx9(Text10, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` ${countdown}s \u540E\u81EA\u52A8\u5237\u65B0` })
|
|
1743
3039
|
] }),
|
|
1744
|
-
/* @__PURE__ */
|
|
1745
|
-
/* @__PURE__ */
|
|
1746
|
-
/* @__PURE__ */
|
|
1747
|
-
/* @__PURE__ */
|
|
1748
|
-
/* @__PURE__ */
|
|
1749
|
-
/* @__PURE__ */
|
|
1750
|
-
/* @__PURE__ */
|
|
1751
|
-
/* @__PURE__ */
|
|
1752
|
-
/* @__PURE__ */
|
|
1753
|
-
/* @__PURE__ */
|
|
3040
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
3041
|
+
/* @__PURE__ */ jsx9(Box9, { width: 3 }),
|
|
3042
|
+
/* @__PURE__ */ jsx9(Box9, { width: 9, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u4EE3\u7801" }) }),
|
|
3043
|
+
/* @__PURE__ */ jsx9(Box9, { width: 16, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u540D\u79F0" }) }),
|
|
3044
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
|
|
3045
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u6DA8\u8DCC\u5E45" }) }),
|
|
3046
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
|
|
3047
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u6700\u9AD8" }) }),
|
|
3048
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u6700\u4F4E" }) }),
|
|
3049
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u6210\u4EA4\u989D" }) })
|
|
1754
3050
|
] }),
|
|
1755
|
-
/* @__PURE__ */
|
|
1756
|
-
/* @__PURE__ */
|
|
3051
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
|
|
3052
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", children: stocks.map((stock, index) => {
|
|
1757
3053
|
const isSelected = index === selectedIndex;
|
|
1758
3054
|
const isUp = stock.changePercent >= 0;
|
|
1759
3055
|
const color = isUp ? "#ff1493" : "#00ff41";
|
|
1760
|
-
return /* @__PURE__ */
|
|
1761
|
-
/* @__PURE__ */
|
|
1762
|
-
/* @__PURE__ */
|
|
1763
|
-
/* @__PURE__ */
|
|
1764
|
-
/* @__PURE__ */
|
|
1765
|
-
/* @__PURE__ */
|
|
3056
|
+
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
3057
|
+
/* @__PURE__ */ jsx9(Box9, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#00ffff", children: "\u25B8 " }) : /* @__PURE__ */ jsx9(Text10, { children: " " }) }),
|
|
3058
|
+
/* @__PURE__ */ jsx9(Box9, { width: 9, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: isSelected ? "#00ffff" : "#ffffff", children: stock.code }) }),
|
|
3059
|
+
/* @__PURE__ */ jsx9(Box9, { width: 16, children: /* @__PURE__ */ jsx9(Text10, { color: isSelected ? "#ffffff" : "#cccccc", children: stock.name }) }),
|
|
3060
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color, children: formatPrice(stock.price) }) }),
|
|
3061
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsxs9(Text10, { color, children: [
|
|
1766
3062
|
isUp ? "+" : "",
|
|
1767
3063
|
stock.changePercent.toFixed(2),
|
|
1768
3064
|
"%"
|
|
1769
3065
|
] }) }),
|
|
1770
|
-
/* @__PURE__ */
|
|
3066
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsxs9(Text10, { color, children: [
|
|
1771
3067
|
isUp ? "+" : "",
|
|
1772
3068
|
stock.changeAmount.toFixed(3)
|
|
1773
3069
|
] }) }),
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1776
|
-
/* @__PURE__ */
|
|
3070
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsx9(Text10, { color: "#cccccc", children: formatPrice(stock.high) }) }),
|
|
3071
|
+
/* @__PURE__ */ jsx9(Box9, { width: 12, children: /* @__PURE__ */ jsx9(Text10, { color: "#888888", children: formatPrice(stock.low) }) }),
|
|
3072
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { color: "#888888", children: formatAmount(stock.amount) }) })
|
|
1777
3073
|
] }, stock.code);
|
|
1778
3074
|
}) }),
|
|
1779
|
-
/* @__PURE__ */
|
|
1780
|
-
/* @__PURE__ */
|
|
1781
|
-
doubleCtrlC && /* @__PURE__ */
|
|
3075
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 q \u8FD4\u56DE` }) }),
|
|
3076
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ~/.dskcode/settings.json` }) }),
|
|
3077
|
+
doubleCtrlC && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text10, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
|
|
1782
3078
|
] });
|
|
1783
3079
|
}
|
|
1784
3080
|
function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
@@ -1796,33 +3092,33 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
|
1796
3092
|
raw = raw.replaceAll("\u256D", "\u250C").replaceAll("\u256E", "\u2510").replaceAll("\u2570", "\u2514").replaceAll("\u256F", "\u2518");
|
|
1797
3093
|
chartLines = raw.split("\n");
|
|
1798
3094
|
}
|
|
1799
|
-
return /* @__PURE__ */
|
|
1800
|
-
/* @__PURE__ */
|
|
1801
|
-
/* @__PURE__ */
|
|
1802
|
-
/* @__PURE__ */
|
|
3095
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingLeft: 1, children: [
|
|
3096
|
+
/* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
3097
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
3098
|
+
/* @__PURE__ */ jsxs9(Text10, { bold: true, color: "#00ffff", children: [
|
|
1803
3099
|
" \u{1F4CA} ",
|
|
1804
3100
|
stock.name,
|
|
1805
3101
|
" "
|
|
1806
3102
|
] }),
|
|
1807
|
-
/* @__PURE__ */
|
|
1808
|
-
currentTime && /* @__PURE__ */
|
|
3103
|
+
/* @__PURE__ */ jsx9(Text10, { dimColor: true, children: stock.code }),
|
|
3104
|
+
currentTime && /* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
|
|
1809
3105
|
" \u{1F550} ",
|
|
1810
3106
|
currentTime
|
|
1811
3107
|
] })
|
|
1812
3108
|
] }),
|
|
1813
|
-
/* @__PURE__ */
|
|
3109
|
+
/* @__PURE__ */ jsx9(Text10, { dimColor: true, children: `${countdown}s \u540E\u5237\u65B0` })
|
|
1814
3110
|
] }),
|
|
1815
|
-
/* @__PURE__ */
|
|
1816
|
-
/* @__PURE__ */
|
|
1817
|
-
/* @__PURE__ */
|
|
3111
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
3112
|
+
/* @__PURE__ */ jsx9(Box9, { width: 16, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#888888", children: "\u5F53\u524D\u4EF7" }) }),
|
|
3113
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { bold: true, color: colorCode, children: [
|
|
1818
3114
|
arrow,
|
|
1819
3115
|
" ",
|
|
1820
3116
|
formatPrice(stock.price)
|
|
1821
3117
|
] }) })
|
|
1822
3118
|
] }),
|
|
1823
|
-
/* @__PURE__ */
|
|
1824
|
-
/* @__PURE__ */
|
|
1825
|
-
/* @__PURE__ */
|
|
3119
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
3120
|
+
/* @__PURE__ */ jsx9(Box9, { width: 16, children: /* @__PURE__ */ jsx9(Text10, { color: "#888888", children: "\u6DA8\u8DCC\u5E45" }) }),
|
|
3121
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: colorCode, children: [
|
|
1826
3122
|
isUp ? "+" : "",
|
|
1827
3123
|
stock.changePercent.toFixed(2),
|
|
1828
3124
|
"%",
|
|
@@ -1831,13 +3127,13 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
|
1831
3127
|
stock.changeAmount.toFixed(3)
|
|
1832
3128
|
] }) })
|
|
1833
3129
|
] }),
|
|
1834
|
-
chartLines.length > 0 && /* @__PURE__ */
|
|
1835
|
-
/* @__PURE__ */
|
|
3130
|
+
chartLines.length > 0 && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: chartLines.map((line, i) => /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { color: colorCode, children: line || " " }) }, i)) }),
|
|
3131
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: " Space/q \u8FD4\u56DE\u5217\u8868" }) })
|
|
1836
3132
|
] });
|
|
1837
3133
|
}
|
|
1838
3134
|
|
|
1839
3135
|
// src/cli/index.tsx
|
|
1840
|
-
import { jsx as
|
|
3136
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1841
3137
|
var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion", "game", "stock"];
|
|
1842
3138
|
function createCli() {
|
|
1843
3139
|
const program2 = new Command();
|
|
@@ -1863,14 +3159,19 @@ function createCli() {
|
|
|
1863
3159
|
const result = await loadAndValidate();
|
|
1864
3160
|
ctx = { ...ctx, config: result.config };
|
|
1865
3161
|
}
|
|
1866
|
-
|
|
3162
|
+
const costTracker = new CostTracker({
|
|
3163
|
+
budgetLimit: ctx?.config.budgetLimit ?? 0,
|
|
3164
|
+
tokenBudgetLimit: ctx?.config.tokenBudgetLimit ?? 0
|
|
3165
|
+
});
|
|
3166
|
+
startChat(ctx, costTracker);
|
|
1867
3167
|
});
|
|
1868
|
-
function startChat(ctx) {
|
|
3168
|
+
function startChat(ctx, costTracker) {
|
|
1869
3169
|
const defaultProvider = ctx?.config.providers.find(
|
|
1870
3170
|
(p) => p.name === (ctx?.config.defaultProvider ?? "deepseek")
|
|
1871
3171
|
);
|
|
3172
|
+
const model = defaultProvider?.model ?? "deepseek-v4-flash";
|
|
1872
3173
|
const chatApp = renderApp(
|
|
1873
|
-
/* @__PURE__ */
|
|
3174
|
+
/* @__PURE__ */ jsx10(
|
|
1874
3175
|
ChatSession,
|
|
1875
3176
|
{
|
|
1876
3177
|
providerCount: ctx?.config.providers.length ?? 1,
|
|
@@ -1878,24 +3179,26 @@ function createCli() {
|
|
|
1878
3179
|
verbose: ctx?.verbose ?? false,
|
|
1879
3180
|
apiKey: defaultProvider?.apiKey,
|
|
1880
3181
|
baseUrl: defaultProvider?.baseUrl ?? "https://api.deepseek.com",
|
|
3182
|
+
costTracker,
|
|
3183
|
+
model,
|
|
1881
3184
|
onLaunchGame: () => {
|
|
1882
3185
|
chatApp.unmount();
|
|
1883
3186
|
setImmediate(() => {
|
|
1884
3187
|
initGames();
|
|
1885
3188
|
const games = listGames();
|
|
1886
3189
|
const { unmount } = render4(
|
|
1887
|
-
/* @__PURE__ */
|
|
3190
|
+
/* @__PURE__ */ jsx10(
|
|
1888
3191
|
GamePicker,
|
|
1889
3192
|
{
|
|
1890
3193
|
games,
|
|
1891
3194
|
onSelect: async (game) => {
|
|
1892
3195
|
unmount();
|
|
1893
3196
|
await game.play();
|
|
1894
|
-
startChat(ctx);
|
|
3197
|
+
startChat(ctx, costTracker);
|
|
1895
3198
|
},
|
|
1896
3199
|
onBackToChat: () => {
|
|
1897
3200
|
unmount();
|
|
1898
|
-
setImmediate(() => startChat(ctx));
|
|
3201
|
+
setImmediate(() => startChat(ctx, costTracker));
|
|
1899
3202
|
}
|
|
1900
3203
|
}
|
|
1901
3204
|
),
|
|
@@ -1908,13 +3211,13 @@ function createCli() {
|
|
|
1908
3211
|
setImmediate(() => {
|
|
1909
3212
|
const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
1910
3213
|
const stockApp = renderApp(
|
|
1911
|
-
/* @__PURE__ */
|
|
3214
|
+
/* @__PURE__ */ jsx10(
|
|
1912
3215
|
StockList,
|
|
1913
3216
|
{
|
|
1914
3217
|
codes: defaultStockCodes,
|
|
1915
3218
|
onBackToChat: () => {
|
|
1916
3219
|
stockApp.unmount();
|
|
1917
|
-
setImmediate(() => startChat(ctx));
|
|
3220
|
+
setImmediate(() => startChat(ctx, costTracker));
|
|
1918
3221
|
},
|
|
1919
3222
|
onExit: () => process.exit(0)
|
|
1920
3223
|
}
|
|
@@ -1972,10 +3275,10 @@ compdef _dskcode_completion dskcode`);
|
|
|
1972
3275
|
program2.command("stock").description("\u67E5\u770B\u81EA\u9009\u80A1\u5B9E\u65F6\u884C\u60C5").argument("[codes...]", "\u80A1\u7968\u4EE3\u7801\uFF08\u7A7A\u683C\u5206\u9694\uFF09\uFF0C\u5982 513090 600519").action(async function(codes) {
|
|
1973
3276
|
const ctx = this.dskcodeCtx;
|
|
1974
3277
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
1975
|
-
const globalConfigPath =
|
|
3278
|
+
const globalConfigPath = join3(home, ".dskcode", "settings.json");
|
|
1976
3279
|
let globalConfigHasStock = false;
|
|
1977
3280
|
try {
|
|
1978
|
-
const raw = await
|
|
3281
|
+
const raw = await readFile3(globalConfigPath, "utf-8");
|
|
1979
3282
|
const parsed = JSON.parse(raw);
|
|
1980
3283
|
const stock = parsed.stock;
|
|
1981
3284
|
globalConfigHasStock = Array.isArray(stock?.symbols) && stock.symbols.length > 0;
|
|
@@ -1995,7 +3298,7 @@ compdef _dskcode_completion dskcode`);
|
|
|
1995
3298
|
const freshResult = await loadAndValidate();
|
|
1996
3299
|
const codeList = codes && codes.length > 0 ? codes : freshResult.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
1997
3300
|
const app = renderApp(
|
|
1998
|
-
/* @__PURE__ */
|
|
3301
|
+
/* @__PURE__ */ jsx10(
|
|
1999
3302
|
StockList,
|
|
2000
3303
|
{
|
|
2001
3304
|
codes: codeList,
|
|
@@ -2024,7 +3327,7 @@ compdef _dskcode_completion dskcode`);
|
|
|
2024
3327
|
}
|
|
2025
3328
|
const selectedGame = await new Promise((resolve) => {
|
|
2026
3329
|
const { unmount } = render4(
|
|
2027
|
-
/* @__PURE__ */
|
|
3330
|
+
/* @__PURE__ */ jsx10(
|
|
2028
3331
|
GamePicker,
|
|
2029
3332
|
{
|
|
2030
3333
|
games,
|