dskcode 0.1.4 → 0.1.6

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/index.js CHANGED
@@ -1,21 +1,278 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- loadConfig
4
- } from "./chunk-GMSRSNWA.js";
5
2
 
6
3
  // src/cli/index.tsx
7
4
  import { Command } from "commander";
8
5
 
6
+ // src/config/loader.ts
7
+ import { existsSync, watch } from "fs";
8
+ import { mkdir, readFile, writeFile } from "fs/promises";
9
+ import { join } from "path";
10
+ var defaultConfig = {
11
+ defaultProvider: "deepseek",
12
+ maxTokens: 8192,
13
+ temperature: 0.7,
14
+ maxToolRounds: 20,
15
+ providers: [
16
+ {
17
+ name: "deepseek",
18
+ baseUrl: "https://api.deepseek.com",
19
+ model: "deepseek-v4-flash"
20
+ }
21
+ ],
22
+ tools: [
23
+ { name: "read_file", enabled: true },
24
+ { name: "write_file", enabled: true },
25
+ { name: "edit_file", enabled: true },
26
+ { name: "bash", enabled: true },
27
+ { name: "glob", enabled: true },
28
+ { name: "grep", enabled: true },
29
+ { name: "ls", enabled: true },
30
+ { name: "fetch", enabled: true }
31
+ ],
32
+ plugins: [],
33
+ stock: {
34
+ symbols: [
35
+ { code: "sh000001" },
36
+ { code: "sz399300" },
37
+ { code: "sh601899" }
38
+ ]
39
+ }
40
+ };
41
+ function resolveConfigFiles(configPath) {
42
+ if (configPath) {
43
+ return [configPath];
44
+ }
45
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
46
+ return [
47
+ join(home, ".dskcode", "settings.json"),
48
+ join(process.cwd(), ".dskcode", "settings.json")
49
+ ];
50
+ }
51
+ function mergeConfig(base, overlay) {
52
+ const result = { ...base };
53
+ if (overlay.defaultProvider !== void 0) {
54
+ result.defaultProvider = overlay.defaultProvider;
55
+ }
56
+ if (overlay.verbose !== void 0) {
57
+ result.verbose = overlay.verbose;
58
+ }
59
+ if (overlay.maxTokens !== void 0) {
60
+ result.maxTokens = overlay.maxTokens;
61
+ }
62
+ if (overlay.temperature !== void 0) {
63
+ result.temperature = overlay.temperature;
64
+ }
65
+ if (overlay.maxToolRounds !== void 0) {
66
+ result.maxToolRounds = overlay.maxToolRounds;
67
+ }
68
+ if (overlay.providers !== void 0) {
69
+ result.providers = overlay.providers;
70
+ }
71
+ if (overlay.tools !== void 0) {
72
+ result.tools = overlay.tools;
73
+ }
74
+ if (overlay.plugins !== void 0) {
75
+ result.plugins = overlay.plugins;
76
+ }
77
+ if (overlay.stock !== void 0) {
78
+ result.stock = overlay.stock;
79
+ }
80
+ return result;
81
+ }
82
+ var ENV_PREFIX = "DSKCODE_";
83
+ var ENV_MAP = {
84
+ [`${ENV_PREFIX}DEFAULT_PROVIDER`]: "defaultProvider",
85
+ [`${ENV_PREFIX}VERBOSE`]: "verbose",
86
+ [`${ENV_PREFIX}MAX_TOKENS`]: "maxTokens",
87
+ [`${ENV_PREFIX}TEMPERATURE`]: "temperature",
88
+ [`${ENV_PREFIX}MAX_TOOL_ROUNDS`]: "maxToolRounds"
89
+ };
90
+ function applyEnvVars(config) {
91
+ for (const [envKey, configKey] of Object.entries(ENV_MAP)) {
92
+ const raw = process.env[envKey];
93
+ if (raw === void 0) continue;
94
+ const cfg = config;
95
+ switch (configKey) {
96
+ case "verbose":
97
+ case "defaultProvider": {
98
+ cfg[configKey] = raw;
99
+ break;
100
+ }
101
+ case "maxTokens":
102
+ case "maxToolRounds": {
103
+ const n = Number(raw);
104
+ if (Number.isFinite(n) && n > 0) {
105
+ cfg[configKey] = n;
106
+ }
107
+ break;
108
+ }
109
+ case "temperature": {
110
+ const n = Number(raw);
111
+ if (Number.isFinite(n) && n >= 0 && n <= 2) {
112
+ cfg[configKey] = n;
113
+ }
114
+ break;
115
+ }
116
+ }
117
+ }
118
+ const apiKey = process.env.DEEPSEEK_API_KEY;
119
+ if (apiKey) {
120
+ const deepseek = config.providers.find((p) => p.name === "deepseek");
121
+ if (deepseek && !deepseek.apiKey) {
122
+ deepseek.apiKey = apiKey;
123
+ }
124
+ if (!deepseek) {
125
+ config.providers.unshift({
126
+ name: "deepseek",
127
+ baseUrl: "https://api.deepseek.com",
128
+ model: "deepseek-v4-flash",
129
+ apiKey
130
+ });
131
+ }
132
+ }
133
+ return config;
134
+ }
135
+ function applyCliOverrides(config, flags) {
136
+ if (flags.verbose !== void 0) {
137
+ config.verbose = flags.verbose;
138
+ }
139
+ if (flags.model !== void 0) {
140
+ const provider = config.providers.find(
141
+ (p) => p.name === config.defaultProvider
142
+ );
143
+ if (provider) {
144
+ provider.model = flags.model;
145
+ }
146
+ }
147
+ if (flags.maxTokens !== void 0 && flags.maxTokens > 0) {
148
+ config.maxTokens = flags.maxTokens;
149
+ }
150
+ if (flags.temperature !== void 0 && flags.temperature >= 0 && flags.temperature <= 2) {
151
+ config.temperature = flags.temperature;
152
+ }
153
+ return config;
154
+ }
155
+ function validateConfig(config) {
156
+ const errors = [];
157
+ if (!config.providers || config.providers.length === 0) {
158
+ errors.push({
159
+ field: "providers",
160
+ 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"
161
+ });
162
+ }
163
+ for (let i = 0; i < config.providers.length; i++) {
164
+ const p = config.providers[i];
165
+ if (!p.name) {
166
+ errors.push({
167
+ field: `providers[${i}].name`,
168
+ message: `\u7B2C ${i + 1} \u4E2A Provider \u7F3A\u5C11 name \u5B57\u6BB5\u3002`
169
+ });
170
+ }
171
+ if (!p.model) {
172
+ errors.push({
173
+ field: `providers[${i}].model`,
174
+ message: `Provider "${p.name || i}" \u7F3A\u5C11 model \u5B57\u6BB5\u3002`
175
+ });
176
+ }
177
+ }
178
+ if (config.defaultProvider) {
179
+ const exists = config.providers.some(
180
+ (p) => p.name === config.defaultProvider
181
+ );
182
+ if (!exists) {
183
+ errors.push({
184
+ field: "defaultProvider",
185
+ message: `\u9ED8\u8BA4 Provider "${config.defaultProvider}" \u672A\u5728 providers \u4E2D\u5B9A\u4E49\u3002`
186
+ });
187
+ }
188
+ }
189
+ if (config.temperature !== void 0 && (config.temperature < 0 || config.temperature > 2)) {
190
+ errors.push({
191
+ field: "temperature",
192
+ message: "temperature \u5FC5\u987B\u5728 0.0 ~ 2.0 \u4E4B\u95F4\u3002"
193
+ });
194
+ }
195
+ if (config.maxToolRounds !== void 0 && config.maxToolRounds < 1) {
196
+ errors.push({
197
+ field: "maxToolRounds",
198
+ message: "maxToolRounds \u5FC5\u987B\u5927\u4E8E\u7B49\u4E8E 1\u3002"
199
+ });
200
+ }
201
+ return errors;
202
+ }
203
+ async function loadConfig(configPath) {
204
+ const filePaths = resolveConfigFiles(configPath);
205
+ let config = structuredClone(defaultConfig);
206
+ for (const filePath of filePaths) {
207
+ try {
208
+ const raw = await readFile(filePath, "utf-8");
209
+ const parsed = JSON.parse(raw);
210
+ config = mergeConfig(config, parsed);
211
+ } catch {
212
+ }
213
+ }
214
+ config = applyEnvVars(config);
215
+ return config;
216
+ }
217
+ async function loadAndValidate(configPath) {
218
+ const config = await loadConfig(configPath);
219
+ const errors = validateConfig(config);
220
+ return { config, errors };
221
+ }
222
+ async function saveApiKey(apiKey) {
223
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
224
+ const configDir = join(home, ".dskcode");
225
+ const configFile = join(configDir, "settings.json");
226
+ await mkdir(configDir, { recursive: true });
227
+ let configData;
228
+ try {
229
+ const raw = await readFile(configFile, "utf-8");
230
+ configData = JSON.parse(raw);
231
+ } catch {
232
+ configData = structuredClone(defaultConfig);
233
+ }
234
+ const providers = configData.providers ?? [];
235
+ const existing = providers.find((p) => p.name === "deepseek");
236
+ if (existing) {
237
+ existing.apiKey = apiKey;
238
+ } else {
239
+ providers.push({
240
+ name: "deepseek",
241
+ apiKey,
242
+ baseUrl: "https://api.deepseek.com",
243
+ model: "deepseek-v4-flash"
244
+ });
245
+ }
246
+ configData.providers = providers;
247
+ await writeFile(configFile, JSON.stringify(configData, null, 2), "utf-8");
248
+ return configFile;
249
+ }
250
+
9
251
  // src/cli/middleware.ts
10
252
  async function loadConfigMiddleware() {
11
253
  const opts = this.optsWithGlobals();
12
254
  const verbose = opts.verbose ?? false;
13
255
  let config;
256
+ const errors = [];
14
257
  try {
15
- config = await loadConfig(opts.config);
258
+ const result = await loadAndValidate(opts.config);
259
+ config = result.config;
260
+ if (result.errors.length > 0) {
261
+ for (const e of result.errors) {
262
+ errors.push(e.message);
263
+ }
264
+ }
16
265
  } catch {
17
- const { defaultConfig } = await import("./config-42MSCTVE.js");
18
- config = defaultConfig;
266
+ config = structuredClone(defaultConfig);
267
+ }
268
+ config = applyCliOverrides(config, {
269
+ verbose,
270
+ model: opts.model
271
+ });
272
+ if (errors.length > 0 && verbose) {
273
+ for (const msg of errors) {
274
+ console.error(` \u26A0 ${msg}`);
275
+ }
19
276
  }
20
277
  return { config, verbose };
21
278
  }
@@ -66,10 +323,73 @@ function customHelp(program2) {
66
323
  lines.push(" dskcode setup");
67
324
  lines.push(` ${chalk.dim("# \u751F\u6210 shell \u81EA\u52A8\u8865\u5168")}`);
68
325
  lines.push(" dskcode completion");
326
+ lines.push(` ${chalk.dim("# \u67E5\u770B\u81EA\u9009\u80A1\u884C\u60C5")}`);
327
+ lines.push(" dskcode stock");
328
+ lines.push(` ${chalk.dim("# \u67E5\u770B\u6307\u5B9A\u80A1\u7968\u884C\u60C5")}`);
329
+ lines.push(" dskcode stock sh513090 sz000001");
69
330
  lines.push("");
70
331
  return lines.join("\n");
71
332
  }
72
333
 
334
+ // src/cli/api-key-setup.ts
335
+ import { createInterface } from "readline";
336
+ import chalk2 from "chalk";
337
+ function hasApiKey(providers) {
338
+ if (providers.some((p) => p.apiKey)) return true;
339
+ if (process.env.DEEPSEEK_API_KEY) return true;
340
+ return false;
341
+ }
342
+ async function promptForApiKey() {
343
+ console.log(
344
+ chalk2.yellow("\n \u26A0 \u672A\u68C0\u6D4B\u5230 API Key \u914D\u7F6E")
345
+ );
346
+ console.log(
347
+ chalk2.dim(" \u4F60\u53EF\u4EE5\u901A\u8FC7\u4EE5\u4E0B\u4EFB\u4E00\u65B9\u5F0F\u914D\u7F6E\uFF1A")
348
+ );
349
+ console.log(
350
+ chalk2.dim(" \xB7 \u73AF\u5883\u53D8\u91CF: export DEEPSEEK_API_KEY=sk-xxx")
351
+ );
352
+ console.log(
353
+ chalk2.dim(" \xB7 \u914D\u7F6E\u6587\u4EF6: ~/.dskcode/settings.json")
354
+ );
355
+ console.log(
356
+ chalk2.dim(" \xB7 \u4E0B\u9762\u76F4\u63A5\u8F93\u5165\uFF0C\u81EA\u52A8\u4FDD\u5B58\u5230\u5168\u5C40\u914D\u7F6E\n")
357
+ );
358
+ const rl = createInterface({
359
+ input: process.stdin,
360
+ output: process.stdout
361
+ });
362
+ return new Promise((resolve) => {
363
+ const cleanup = () => {
364
+ rl.close();
365
+ };
366
+ process.stdin.on("keypress", (_, key) => {
367
+ if (key.ctrl && key.name === "c") {
368
+ cleanup();
369
+ resolve(null);
370
+ }
371
+ });
372
+ rl.question(
373
+ ` ${chalk2.cyan("\u{1F511}")} ${chalk2.bold("\u8BF7\u8F93\u5165\u4F60\u7684 DeepSeek API Key:")} `,
374
+ (answer) => {
375
+ cleanup();
376
+ const trimmed = answer.trim();
377
+ if (!trimmed) {
378
+ console.log(chalk2.red(" \u2716 API Key \u4E0D\u80FD\u4E3A\u7A7A"));
379
+ resolve(null);
380
+ return;
381
+ }
382
+ if (trimmed.length < 10) {
383
+ console.log(chalk2.red(" \u2716 API Key \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u957F\u5EA6\u81F3\u5C11 10 \u4F4D"));
384
+ resolve(null);
385
+ return;
386
+ }
387
+ resolve(trimmed);
388
+ }
389
+ );
390
+ });
391
+ }
392
+
73
393
  // src/ui/RenderScope.tsx
74
394
  import { render } from "ink";
75
395
  function renderApp(node) {
@@ -1075,26 +1395,317 @@ function initGames() {
1075
1395
 
1076
1396
  // src/cli/index.tsx
1077
1397
  import { render as render4 } from "ink";
1078
- import chalk2 from "chalk";
1079
- import { jsx as jsx7 } from "react/jsx-runtime";
1080
- var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion", "game"];
1398
+ import chalk3 from "chalk";
1399
+
1400
+ // src/stock/StockList.tsx
1401
+ import { Box as Box7, Text as Text8, useInput as useInput4 } from "ink";
1402
+ import { useState as useState6, useCallback as useCallback5, useEffect as useEffect5 } from "react";
1403
+ import asciichart from "asciichart";
1404
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1405
+ var MINUTE_API = "https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1";
1406
+ function toApiCode(code) {
1407
+ if (/^sh|^sz/.test(code)) return code;
1408
+ if (/^60/.test(code) || /^68/.test(code)) return "sh" + code;
1409
+ if (/^00/.test(code) || /^30/.test(code) || /^39/.test(code)) return "sz" + code;
1410
+ if (/^51/.test(code)) return "sh" + code;
1411
+ return "sh" + code;
1412
+ }
1413
+ async function fetchStockMinute(code) {
1414
+ const url = MINUTE_API.replace("{code}", toApiCode(code));
1415
+ try {
1416
+ const resp = await fetch(url);
1417
+ const json = await resp.json();
1418
+ if (json.code !== 0) return null;
1419
+ const stockKey = toApiCode(code);
1420
+ const stockData = json.data?.[stockKey];
1421
+ if (!stockData) return null;
1422
+ const rawMinutes = stockData.data?.data ?? [];
1423
+ const prices = [];
1424
+ for (const line of rawMinutes) {
1425
+ const parts = line.split(" ");
1426
+ if (parts.length >= 2) {
1427
+ const p = parseFloat(parts[1]);
1428
+ if (!isNaN(p)) prices.push(p);
1429
+ }
1430
+ }
1431
+ const qtKey = stockKey;
1432
+ const qt = stockData.qt?.[qtKey];
1433
+ let quote = null;
1434
+ if (qt && qt.length >= 35) {
1435
+ quote = {
1436
+ code,
1437
+ name: qt[1] ?? "",
1438
+ price: parseFloat(qt[3] ?? "0"),
1439
+ changePercent: parseFloat(qt[32] ?? "0"),
1440
+ changeAmount: parseFloat(qt[31] ?? "0"),
1441
+ high: parseFloat(qt[33] ?? "0"),
1442
+ low: parseFloat(qt[34] ?? "0"),
1443
+ volume: parseInt(qt[6] ?? "0", 10)
1444
+ };
1445
+ }
1446
+ return {
1447
+ prices,
1448
+ quote,
1449
+ date: stockData.data?.date ?? ""
1450
+ };
1451
+ } catch {
1452
+ return null;
1453
+ }
1454
+ }
1455
+ var minuteCache = /* @__PURE__ */ new Map();
1456
+ function cacheMinute(code, prices) {
1457
+ minuteCache.set(code, prices);
1458
+ }
1459
+ var FALLBACK_STOCKS = [
1460
+ { code: "000001", name: "\u4E0A\u8BC1\u6307\u6570", price: 3150, changePercent: 0.35, changeAmount: 11.02, high: 3160, low: 3140, volume: 28543e4 },
1461
+ { code: "399006", name: "\u521B\u4E1A\u677F\u6307", price: 1820, changePercent: -0.52, changeAmount: -9.5, high: 1835, low: 1815, volume: 9865e4 },
1462
+ { code: "601688", name: "\u534E\u6CF0\u8BC1\u5238", price: 14.25, changePercent: 1.05, changeAmount: 0.15, high: 14.38, low: 14.1, volume: 452100 }
1463
+ ];
1464
+ async function fetchStocks(codes) {
1465
+ const results = await Promise.all(
1466
+ codes.map(async (code) => {
1467
+ const data = await fetchStockMinute(code);
1468
+ if (data?.quote) {
1469
+ if (data.prices.length > 0) cacheMinute(code, data.prices);
1470
+ return data.quote;
1471
+ }
1472
+ return null;
1473
+ })
1474
+ );
1475
+ const real = results.filter((r) => r !== null);
1476
+ if (real.length > 0) return real;
1477
+ return FALLBACK_STOCKS;
1478
+ }
1479
+ function formatPrice(p) {
1480
+ return p >= 100 ? p.toFixed(2) : p.toFixed(3);
1481
+ }
1482
+ function formatVolume(v) {
1483
+ if (v >= 1e4) return (v / 1e4).toFixed(1) + "\u4E07";
1484
+ return v.toLocaleString();
1485
+ }
1486
+ function latestPoints(data, maxPoints = 60) {
1487
+ if (data.length <= maxPoints) return data;
1488
+ return data.slice(data.length - maxPoints);
1489
+ }
1490
+ function StockList({ codes, onExit }) {
1491
+ const [stocks, setStocks] = useState6([]);
1492
+ const [selectedIndex, setSelectedIndex] = useState6(0);
1493
+ const [loading, setLoading] = useState6(true);
1494
+ const [lastUpdate, setLastUpdate] = useState6("");
1495
+ const [detailView, setDetailView] = useState6(null);
1496
+ const [detailPrices, setDetailPrices] = useState6(null);
1497
+ const [detailLoading, setDetailLoading] = useState6(false);
1498
+ const [detailCountdown, setDetailCountdown] = useState6(10);
1499
+ const [countdown, setCountdown] = useState6(5);
1500
+ const loadData = useCallback5(async () => {
1501
+ setLoading(true);
1502
+ try {
1503
+ const data = await fetchStocks(codes ?? []);
1504
+ setStocks(data);
1505
+ setLastUpdate((/* @__PURE__ */ new Date()).toLocaleTimeString());
1506
+ } catch {
1507
+ }
1508
+ setLoading(false);
1509
+ }, [codes]);
1510
+ useEffect5(() => {
1511
+ loadData();
1512
+ }, [loadData]);
1513
+ useEffect5(() => {
1514
+ const interval = setInterval(() => {
1515
+ setCountdown((prev) => {
1516
+ if (prev <= 1) {
1517
+ loadData();
1518
+ return 5;
1519
+ }
1520
+ return prev - 1;
1521
+ });
1522
+ }, 1e3);
1523
+ return () => clearInterval(interval);
1524
+ }, [loadData]);
1525
+ useEffect5(() => {
1526
+ if (!detailView) {
1527
+ setDetailPrices(null);
1528
+ setDetailLoading(false);
1529
+ return;
1530
+ }
1531
+ const loadDetail = () => {
1532
+ minuteCache.delete(detailView.code);
1533
+ fetchStockMinute(detailView.code).then((data) => {
1534
+ if (data && data.prices.length > 0) {
1535
+ cacheMinute(detailView.code, data.prices);
1536
+ setDetailPrices(data.prices);
1537
+ }
1538
+ });
1539
+ };
1540
+ loadDetail();
1541
+ setDetailCountdown(10);
1542
+ const timer = setInterval(loadDetail, 1e4);
1543
+ return () => clearInterval(timer);
1544
+ }, [detailView]);
1545
+ useEffect5(() => {
1546
+ if (!detailView) return;
1547
+ const timer = setInterval(() => {
1548
+ setDetailCountdown((prev) => prev > 0 ? prev - 1 : 10);
1549
+ }, 1e3);
1550
+ return () => clearInterval(timer);
1551
+ }, [detailView]);
1552
+ useInput4(
1553
+ useCallback5(
1554
+ (input, key) => {
1555
+ if (detailView) {
1556
+ if (key.escape || input === "q" || input === " ") {
1557
+ setDetailView(null);
1558
+ }
1559
+ return;
1560
+ }
1561
+ if (stocks.length === 0) return;
1562
+ if (key.upArrow || input === "k") {
1563
+ setSelectedIndex((prev) => prev > 0 ? prev - 1 : stocks.length - 1);
1564
+ } else if (key.downArrow || input === "j") {
1565
+ setSelectedIndex((prev) => prev < stocks.length - 1 ? prev + 1 : 0);
1566
+ } else if (key.return) {
1567
+ const stock = stocks[selectedIndex];
1568
+ if (stock) setDetailView(stock);
1569
+ } else if (key.escape || input === "q") {
1570
+ onExit();
1571
+ } else if (input === "r") {
1572
+ setCountdown(5);
1573
+ loadData();
1574
+ }
1575
+ },
1576
+ [stocks, selectedIndex, detailView, onExit, loadData]
1577
+ )
1578
+ );
1579
+ if (detailView) {
1580
+ if (detailLoading) {
1581
+ return /* @__PURE__ */ jsx7(Box7, { paddingLeft: 1, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
1582
+ }
1583
+ return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown);
1584
+ }
1585
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1586
+ /* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, justifyContent: "space-between", children: [
1587
+ /* @__PURE__ */ jsx7(Text8, { bold: true, color: "#00ffff", children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
1588
+ /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` \u6BCF ${countdown}s \u81EA\u52A8\u5237\u65B0` })
1589
+ ] }),
1590
+ /* @__PURE__ */ jsxs7(Box7, { children: [
1591
+ /* @__PURE__ */ jsx7(Box7, { width: 3 }),
1592
+ /* @__PURE__ */ jsx7(Box7, { width: 9, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u4EE3\u7801" }) }),
1593
+ /* @__PURE__ */ jsx7(Box7, { width: 16, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u540D\u79F0" }) }),
1594
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
1595
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6DA8\u8DCC\u5E45" }) }),
1596
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
1597
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6700\u9AD8" }) }),
1598
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6700\u4F4E" }) }),
1599
+ /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6210\u4EA4\u91CF" }) })
1600
+ ] }),
1601
+ /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
1602
+ /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: stocks.map((stock, index) => {
1603
+ const isSelected = index === selectedIndex;
1604
+ const isUp = stock.changePercent >= 0;
1605
+ const color = isUp ? "#ff1493" : "#00ff41";
1606
+ return /* @__PURE__ */ jsxs7(Box7, { children: [
1607
+ /* @__PURE__ */ jsx7(Box7, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx7(Text8, { bold: true, color: "#00ffff", children: "\u25B8 " }) : /* @__PURE__ */ jsx7(Text8, { children: " " }) }),
1608
+ /* @__PURE__ */ jsx7(Box7, { width: 9, children: /* @__PURE__ */ jsx7(Text8, { bold: true, color: isSelected ? "#00ffff" : "#ffffff", children: stock.code }) }),
1609
+ /* @__PURE__ */ jsx7(Box7, { width: 16, children: /* @__PURE__ */ jsx7(Text8, { color: isSelected ? "#ffffff" : "#cccccc", children: stock.name }) }),
1610
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { bold: true, color, children: formatPrice(stock.price) }) }),
1611
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsxs7(Text8, { color, children: [
1612
+ isUp ? "+" : "",
1613
+ stock.changePercent.toFixed(2),
1614
+ "%"
1615
+ ] }) }),
1616
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsxs7(Text8, { color, children: [
1617
+ isUp ? "+" : "",
1618
+ stock.changeAmount.toFixed(3)
1619
+ ] }) }),
1620
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { color: "#cccccc", children: formatPrice(stock.high) }) }),
1621
+ /* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { color: "#cccccc", children: formatPrice(stock.low) }) }),
1622
+ /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { color: "#888888", children: formatVolume(stock.volume) }) })
1623
+ ] }, stock.code);
1624
+ }) }),
1625
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 q \u8FD4\u56DE` }) }),
1626
+ /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate}` }) })
1627
+ ] });
1628
+ }
1629
+ function renderDetail(stock, _onBack, prices, countdown = 10) {
1630
+ const isUp = stock.changePercent >= 0;
1631
+ const colorCode = isUp ? "#ff1493" : "#00ff41";
1632
+ const arrow = isUp ? "\u25B2" : "\u25BC";
1633
+ let chartLines = [];
1634
+ if (prices && prices.length > 0) {
1635
+ const chartColor = isUp ? asciichart.red : asciichart.green;
1636
+ const latest = latestPoints(prices, 60);
1637
+ let raw = asciichart.plot(latest, {
1638
+ height: 10,
1639
+ colors: [chartColor]
1640
+ });
1641
+ raw = raw.replaceAll("\u256D", "\u250C").replaceAll("\u256E", "\u2510").replaceAll("\u2570", "\u2514").replaceAll("\u256F", "\u2518");
1642
+ chartLines = raw.split("\n");
1643
+ }
1644
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingLeft: 1, children: [
1645
+ /* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, justifyContent: "space-between", children: [
1646
+ /* @__PURE__ */ jsxs7(Box7, { children: [
1647
+ /* @__PURE__ */ jsxs7(Text8, { bold: true, color: "#00ffff", children: [
1648
+ " \u{1F4CA} ",
1649
+ stock.name,
1650
+ " "
1651
+ ] }),
1652
+ /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: stock.code })
1653
+ ] }),
1654
+ /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: `\u6BCF ${countdown}s \u5237\u65B0` })
1655
+ ] }),
1656
+ /* @__PURE__ */ jsxs7(Box7, { children: [
1657
+ /* @__PURE__ */ jsx7(Box7, { width: 16, children: /* @__PURE__ */ jsx7(Text8, { bold: true, color: "#888888", children: "\u5F53\u524D\u4EF7" }) }),
1658
+ /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text8, { bold: true, color: colorCode, children: [
1659
+ arrow,
1660
+ " ",
1661
+ formatPrice(stock.price)
1662
+ ] }) })
1663
+ ] }),
1664
+ /* @__PURE__ */ jsxs7(Box7, { children: [
1665
+ /* @__PURE__ */ jsx7(Box7, { width: 16, children: /* @__PURE__ */ jsx7(Text8, { color: "#888888", children: "\u6DA8\u8DCC\u5E45" }) }),
1666
+ /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text8, { color: colorCode, children: [
1667
+ isUp ? "+" : "",
1668
+ stock.changePercent.toFixed(2),
1669
+ "%",
1670
+ " ",
1671
+ isUp ? "+" : "",
1672
+ stock.changeAmount.toFixed(3)
1673
+ ] }) })
1674
+ ] }),
1675
+ chartLines.length > 0 && /* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexDirection: "column", children: chartLines.map((line, i) => /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { color: colorCode, children: line || " " }) }, i)) }),
1676
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: " Space/q \u8FD4\u56DE\u5217\u8868" }) })
1677
+ ] });
1678
+ }
1679
+
1680
+ // src/cli/index.tsx
1681
+ import { jsx as jsx8 } from "react/jsx-runtime";
1682
+ var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion", "game", "stock"];
1081
1683
  function createCli() {
1082
1684
  const program2 = new Command();
1083
1685
  program2.exitOverride();
1084
1686
  program2.name("dskcode").description("\u57FA\u4E8E DeepSeek \u7684 AI \u7F16\u7A0B\u52A9\u624B\u7EC8\u7AEF\u5DE5\u5177").version("0.0.0", "-V, --version", "\u663E\u793A\u7248\u672C\u53F7").option("--verbose", "\u5F00\u542F\u8BE6\u7EC6\u65E5\u5FD7\u8F93\u51FA").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84");
1085
1687
  program2.helpInformation = () => customHelp(program2);
1086
- program2.hook("preAction", async (thisCommand) => {
1688
+ program2.hook("preAction", async (thisCommand, actionCommand) => {
1087
1689
  const ctx = await loadConfigMiddleware.call(thisCommand);
1088
- thisCommand.dskcodeCtx = ctx;
1690
+ actionCommand.dskcodeCtx = ctx;
1089
1691
  });
1090
1692
  program2.command("chat").description("\u542F\u52A8\u4EA4\u4E92\u5F0F\u5BF9\u8BDD\u4F1A\u8BDD").action(async function() {
1091
1693
  if (!process.stdin.isTTY) {
1092
1694
  console.error("dskcode chat \u9700\u8981\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u3002\u5982\u9700\u6267\u884C\u4E00\u6B21\u6027\u4EFB\u52A1\uFF0C\u8BF7\u4F7F\u7528 dskcode run\u3002");
1093
1695
  process.exit(1);
1094
1696
  }
1095
- const ctx = this.dskcodeCtx;
1697
+ let ctx = this.dskcodeCtx;
1698
+ if (ctx && !hasApiKey(ctx.config.providers)) {
1699
+ const key = await promptForApiKey();
1700
+ if (!key) process.exit(1);
1701
+ const savedPath = await saveApiKey(key);
1702
+ console.log(` ${chalk3.green("\u2714")} API Key \u5DF2\u4FDD\u5B58\u5230 ${chalk3.dim(savedPath)}
1703
+ `);
1704
+ const result = await loadAndValidate();
1705
+ ctx = { ...ctx, config: result.config };
1706
+ }
1096
1707
  const app = renderApp(
1097
- /* @__PURE__ */ jsx7(
1708
+ /* @__PURE__ */ jsx8(
1098
1709
  ChatSession,
1099
1710
  {
1100
1711
  providerCount: ctx?.config.providers.length ?? 1,
@@ -1141,12 +1752,26 @@ _dskcode_completion() {
1141
1752
  "init:\u751F\u6210\u9879\u76EE\u8BB0\u5FC6\u6587\u4EF6"
1142
1753
  "completion:\u8F93\u51FA shell \u81EA\u52A8\u8865\u5168\u8BF4\u660E"
1143
1754
  "game:\u5185\u7F6E\u5C0F\u6E38\u620F"
1755
+ "stock:\u67E5\u770B\u81EA\u9009\u80A1\u5B9E\u65F6\u884C\u60C5"
1144
1756
  )
1145
1757
  _describe 'dskcode commands' commands
1146
1758
  }
1147
1759
  compdef _dskcode_completion dskcode`);
1148
1760
  }
1149
1761
  });
1762
+ 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) {
1763
+ const codeList = codes && codes.length > 0 ? codes : ["sh000001", "sz399006", "sh601688"];
1764
+ const app = renderApp(
1765
+ /* @__PURE__ */ jsx8(
1766
+ StockList,
1767
+ {
1768
+ codes: codeList,
1769
+ onExit: () => process.exit(0)
1770
+ }
1771
+ )
1772
+ );
1773
+ await app.waitUntilExit;
1774
+ });
1150
1775
  initGames();
1151
1776
  program2.command("game").description("\u542F\u52A8\u5185\u7F6E\u5C0F\u6E38\u620F").argument("[name]", "\u6E38\u620F\u540D\u79F0\uFF0C\u4E0D\u6307\u5B9A\u5219\u663E\u793A\u4EA4\u4E92\u5F0F\u6E38\u620F\u5217\u8868").action(async function(name) {
1152
1777
  if (name) {
@@ -1166,7 +1791,7 @@ compdef _dskcode_completion dskcode`);
1166
1791
  }
1167
1792
  const selectedGame = await new Promise((resolve) => {
1168
1793
  const { unmount } = render4(
1169
- /* @__PURE__ */ jsx7(
1794
+ /* @__PURE__ */ jsx8(
1170
1795
  GamePicker,
1171
1796
  {
1172
1797
  games,
@@ -1184,7 +1809,7 @@ compdef _dskcode_completion dskcode`);
1184
1809
  });
1185
1810
  if (selectedGame) {
1186
1811
  console.log(`
1187
- \u542F\u52A8\u6E38\u620F: ${chalk2.green(selectedGame.name)}
1812
+ \u542F\u52A8\u6E38\u620F: ${chalk3.green(selectedGame.name)}
1188
1813
  `);
1189
1814
  await selectedGame.play();
1190
1815
  }