dskcode 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -2
- package/dist/index.js +576 -128
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -29,7 +29,14 @@ var defaultConfig = {
|
|
|
29
29
|
{ name: "ls", enabled: true },
|
|
30
30
|
{ name: "fetch", enabled: true }
|
|
31
31
|
],
|
|
32
|
-
plugins: []
|
|
32
|
+
plugins: [],
|
|
33
|
+
stock: {
|
|
34
|
+
symbols: [
|
|
35
|
+
{ code: "sh000001" },
|
|
36
|
+
{ code: "sz399300" },
|
|
37
|
+
{ code: "sh601899" }
|
|
38
|
+
]
|
|
39
|
+
}
|
|
33
40
|
};
|
|
34
41
|
function resolveConfigFiles(configPath) {
|
|
35
42
|
if (configPath) {
|
|
@@ -67,6 +74,9 @@ function mergeConfig(base, overlay) {
|
|
|
67
74
|
if (overlay.plugins !== void 0) {
|
|
68
75
|
result.plugins = overlay.plugins;
|
|
69
76
|
}
|
|
77
|
+
if (overlay.stock !== void 0) {
|
|
78
|
+
result.stock = overlay.stock;
|
|
79
|
+
}
|
|
70
80
|
return result;
|
|
71
81
|
}
|
|
72
82
|
var ENV_PREFIX = "DSKCODE_";
|
|
@@ -78,28 +88,28 @@ var ENV_MAP = {
|
|
|
78
88
|
[`${ENV_PREFIX}MAX_TOOL_ROUNDS`]: "maxToolRounds"
|
|
79
89
|
};
|
|
80
90
|
function applyEnvVars(config) {
|
|
91
|
+
const result = { ...config, providers: [...config.providers] };
|
|
81
92
|
for (const [envKey, configKey] of Object.entries(ENV_MAP)) {
|
|
82
93
|
const raw = process.env[envKey];
|
|
83
94
|
if (raw === void 0) continue;
|
|
84
|
-
const cfg = config;
|
|
85
95
|
switch (configKey) {
|
|
86
96
|
case "verbose":
|
|
87
97
|
case "defaultProvider": {
|
|
88
|
-
|
|
98
|
+
result[configKey] = raw;
|
|
89
99
|
break;
|
|
90
100
|
}
|
|
91
101
|
case "maxTokens":
|
|
92
102
|
case "maxToolRounds": {
|
|
93
103
|
const n = Number(raw);
|
|
94
104
|
if (Number.isFinite(n) && n > 0) {
|
|
95
|
-
|
|
105
|
+
result[configKey] = n;
|
|
96
106
|
}
|
|
97
107
|
break;
|
|
98
108
|
}
|
|
99
109
|
case "temperature": {
|
|
100
110
|
const n = Number(raw);
|
|
101
111
|
if (Number.isFinite(n) && n >= 0 && n <= 2) {
|
|
102
|
-
|
|
112
|
+
result[configKey] = n;
|
|
103
113
|
}
|
|
104
114
|
break;
|
|
105
115
|
}
|
|
@@ -107,12 +117,14 @@ function applyEnvVars(config) {
|
|
|
107
117
|
}
|
|
108
118
|
const apiKey = process.env.DEEPSEEK_API_KEY;
|
|
109
119
|
if (apiKey) {
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
120
|
+
const deepseekIdx = result.providers.findIndex((p) => p.name === "deepseek");
|
|
121
|
+
if (deepseekIdx !== -1) {
|
|
122
|
+
const existing = result.providers[deepseekIdx];
|
|
123
|
+
if (!existing.apiKey) {
|
|
124
|
+
result.providers[deepseekIdx] = { ...existing, apiKey };
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
result.providers.unshift({
|
|
116
128
|
name: "deepseek",
|
|
117
129
|
baseUrl: "https://api.deepseek.com",
|
|
118
130
|
model: "deepseek-v4-flash",
|
|
@@ -120,27 +132,31 @@ function applyEnvVars(config) {
|
|
|
120
132
|
});
|
|
121
133
|
}
|
|
122
134
|
}
|
|
123
|
-
return
|
|
135
|
+
return result;
|
|
124
136
|
}
|
|
125
137
|
function applyCliOverrides(config, flags) {
|
|
138
|
+
const result = { ...config, providers: [...config.providers] };
|
|
126
139
|
if (flags.verbose !== void 0) {
|
|
127
|
-
|
|
140
|
+
result.verbose = flags.verbose;
|
|
128
141
|
}
|
|
129
142
|
if (flags.model !== void 0) {
|
|
130
|
-
const
|
|
131
|
-
(p) => p.name ===
|
|
143
|
+
const providerIdx = result.providers.findIndex(
|
|
144
|
+
(p) => p.name === result.defaultProvider
|
|
132
145
|
);
|
|
133
|
-
if (
|
|
134
|
-
|
|
146
|
+
if (providerIdx !== -1) {
|
|
147
|
+
result.providers[providerIdx] = {
|
|
148
|
+
...result.providers[providerIdx],
|
|
149
|
+
model: flags.model
|
|
150
|
+
};
|
|
135
151
|
}
|
|
136
152
|
}
|
|
137
153
|
if (flags.maxTokens !== void 0 && flags.maxTokens > 0) {
|
|
138
|
-
|
|
154
|
+
result.maxTokens = flags.maxTokens;
|
|
139
155
|
}
|
|
140
156
|
if (flags.temperature !== void 0 && flags.temperature >= 0 && flags.temperature <= 2) {
|
|
141
|
-
|
|
157
|
+
result.temperature = flags.temperature;
|
|
142
158
|
}
|
|
143
|
-
return
|
|
159
|
+
return result;
|
|
144
160
|
}
|
|
145
161
|
function validateConfig(config) {
|
|
146
162
|
const errors = [];
|
|
@@ -182,6 +198,12 @@ function validateConfig(config) {
|
|
|
182
198
|
message: "temperature \u5FC5\u987B\u5728 0.0 ~ 2.0 \u4E4B\u95F4\u3002"
|
|
183
199
|
});
|
|
184
200
|
}
|
|
201
|
+
if (config.maxTokens !== void 0 && config.maxTokens < 1) {
|
|
202
|
+
errors.push({
|
|
203
|
+
field: "maxTokens",
|
|
204
|
+
message: "maxTokens \u5FC5\u987B\u5927\u4E8E\u7B49\u4E8E 1\u3002"
|
|
205
|
+
});
|
|
206
|
+
}
|
|
185
207
|
if (config.maxToolRounds !== void 0 && config.maxToolRounds < 1) {
|
|
186
208
|
errors.push({
|
|
187
209
|
field: "maxToolRounds",
|
|
@@ -214,11 +236,12 @@ async function saveApiKey(apiKey) {
|
|
|
214
236
|
const configDir = join(home, ".dskcode");
|
|
215
237
|
const configFile = join(configDir, "settings.json");
|
|
216
238
|
await mkdir(configDir, { recursive: true });
|
|
217
|
-
let configData
|
|
239
|
+
let configData;
|
|
218
240
|
try {
|
|
219
241
|
const raw = await readFile(configFile, "utf-8");
|
|
220
242
|
configData = JSON.parse(raw);
|
|
221
243
|
} catch {
|
|
244
|
+
configData = structuredClone(defaultConfig);
|
|
222
245
|
}
|
|
223
246
|
const providers = configData.providers ?? [];
|
|
224
247
|
const existing = providers.find((p) => p.name === "deepseek");
|
|
@@ -312,6 +335,10 @@ function customHelp(program2) {
|
|
|
312
335
|
lines.push(" dskcode setup");
|
|
313
336
|
lines.push(` ${chalk.dim("# \u751F\u6210 shell \u81EA\u52A8\u8865\u5168")}`);
|
|
314
337
|
lines.push(" dskcode completion");
|
|
338
|
+
lines.push(` ${chalk.dim("# \u67E5\u770B\u81EA\u9009\u80A1\u884C\u60C5")}`);
|
|
339
|
+
lines.push(" dskcode stock");
|
|
340
|
+
lines.push(` ${chalk.dim("# \u67E5\u770B\u6307\u5B9A\u80A1\u7968\u884C\u60C5")}`);
|
|
341
|
+
lines.push(" dskcode stock sh513090 sz000001");
|
|
315
342
|
lines.push("");
|
|
316
343
|
return lines.join("\n");
|
|
317
344
|
}
|
|
@@ -324,36 +351,32 @@ function hasApiKey(providers) {
|
|
|
324
351
|
if (process.env.DEEPSEEK_API_KEY) return true;
|
|
325
352
|
return false;
|
|
326
353
|
}
|
|
354
|
+
var MIN_API_KEY_LENGTH = 10;
|
|
327
355
|
async function promptForApiKey() {
|
|
328
|
-
console.log(
|
|
329
|
-
|
|
330
|
-
);
|
|
331
|
-
console.log(
|
|
332
|
-
|
|
333
|
-
);
|
|
334
|
-
console.log(
|
|
335
|
-
chalk2.dim(" \xB7 \u73AF\u5883\u53D8\u91CF: export DEEPSEEK_API_KEY=sk-xxx")
|
|
336
|
-
);
|
|
337
|
-
console.log(
|
|
338
|
-
chalk2.dim(" \xB7 \u914D\u7F6E\u6587\u4EF6: ~/.dskcode/settings.json")
|
|
339
|
-
);
|
|
340
|
-
console.log(
|
|
341
|
-
chalk2.dim(" \xB7 \u4E0B\u9762\u76F4\u63A5\u8F93\u5165\uFF0C\u81EA\u52A8\u4FDD\u5B58\u5230\u5168\u5C40\u914D\u7F6E\n")
|
|
342
|
-
);
|
|
356
|
+
console.log(chalk2.yellow("\n \u26A0 \u672A\u68C0\u6D4B\u5230 API Key \u914D\u7F6E"));
|
|
357
|
+
console.log(chalk2.dim(" \u4F60\u53EF\u4EE5\u901A\u8FC7\u4EE5\u4E0B\u4EFB\u4E00\u65B9\u5F0F\u914D\u7F6E\uFF1A"));
|
|
358
|
+
console.log(chalk2.dim(" \xB7 \u73AF\u5883\u53D8\u91CF: export DEEPSEEK_API_KEY=sk-xxx"));
|
|
359
|
+
console.log(chalk2.dim(" \xB7 \u914D\u7F6E\u6587\u4EF6: ~/.dskcode/settings.json"));
|
|
360
|
+
console.log(chalk2.dim(" \xB7 \u4E0B\u9762\u76F4\u63A5\u8F93\u5165\uFF0C\u81EA\u52A8\u4FDD\u5B58\u5230\u5168\u5C40\u914D\u7F6E\n"));
|
|
343
361
|
const rl = createInterface({
|
|
344
362
|
input: process.stdin,
|
|
345
363
|
output: process.stdout
|
|
346
364
|
});
|
|
347
365
|
return new Promise((resolve) => {
|
|
366
|
+
let resolved = false;
|
|
348
367
|
const cleanup = () => {
|
|
368
|
+
if (resolved) return;
|
|
369
|
+
resolved = true;
|
|
370
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
349
371
|
rl.close();
|
|
350
372
|
};
|
|
351
|
-
|
|
373
|
+
const onKeypress = (_, key) => {
|
|
352
374
|
if (key.ctrl && key.name === "c") {
|
|
353
375
|
cleanup();
|
|
354
376
|
resolve(null);
|
|
355
377
|
}
|
|
356
|
-
}
|
|
378
|
+
};
|
|
379
|
+
process.stdin.on("keypress", onKeypress);
|
|
357
380
|
rl.question(
|
|
358
381
|
` ${chalk2.cyan("\u{1F511}")} ${chalk2.bold("\u8BF7\u8F93\u5165\u4F60\u7684 DeepSeek API Key:")} `,
|
|
359
382
|
(answer) => {
|
|
@@ -364,7 +387,7 @@ async function promptForApiKey() {
|
|
|
364
387
|
resolve(null);
|
|
365
388
|
return;
|
|
366
389
|
}
|
|
367
|
-
if (trimmed.length <
|
|
390
|
+
if (trimmed.length < MIN_API_KEY_LENGTH) {
|
|
368
391
|
console.log(chalk2.red(" \u2716 API Key \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u957F\u5EA6\u81F3\u5C11 10 \u4F4D"));
|
|
369
392
|
resolve(null);
|
|
370
393
|
return;
|
|
@@ -378,7 +401,7 @@ async function promptForApiKey() {
|
|
|
378
401
|
// src/ui/RenderScope.tsx
|
|
379
402
|
import { render } from "ink";
|
|
380
403
|
function renderApp(node) {
|
|
381
|
-
const { waitUntilExit, clear, unmount } = render(node);
|
|
404
|
+
const { waitUntilExit, clear, unmount } = render(node, { exitOnCtrlC: false });
|
|
382
405
|
return { waitUntilExit: waitUntilExit(), clear, unmount };
|
|
383
406
|
}
|
|
384
407
|
|
|
@@ -395,12 +418,6 @@ import { jsxs as jsxs2 } from "react/jsx-runtime";
|
|
|
395
418
|
import { Box as Box2, Text as Text3 } from "ink";
|
|
396
419
|
import { useEffect, useState } from "react";
|
|
397
420
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
398
|
-
|
|
399
|
-
// src/ui/ChatSession.tsx
|
|
400
|
-
import { Box as Box3, Text as Text4 } from "ink";
|
|
401
|
-
import TextInput from "ink-text-input";
|
|
402
|
-
import { useEffect as useEffect2, useState as useState2, useCallback } from "react";
|
|
403
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
404
421
|
var CYBER_PALETTE = ["#00ffff", "#ff00ff", "#00ff41", "#ff1493", "#8b00ff"];
|
|
405
422
|
var LOGO_LINES = [
|
|
406
423
|
" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
@@ -410,57 +427,119 @@ var LOGO_LINES = [
|
|
|
410
427
|
" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557",
|
|
411
428
|
" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"
|
|
412
429
|
];
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
"/version": { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => "dskcode v0.0.0" }
|
|
428
|
-
};
|
|
429
|
-
function ChatSession({ providerCount, toolCount, verbose }) {
|
|
430
|
-
const [offset, setOffset] = useState2(0);
|
|
431
|
-
const [messages, setMessages] = useState2([]);
|
|
432
|
-
const [input, setInput] = useState2("");
|
|
430
|
+
|
|
431
|
+
// src/ui/ChatSession.tsx
|
|
432
|
+
import { Box as Box3, Text as Text4, useInput } from "ink";
|
|
433
|
+
import TextInput from "ink-text-input";
|
|
434
|
+
import { useEffect as useEffect3, useState as useState3, useCallback as useCallback2 } from "react";
|
|
435
|
+
|
|
436
|
+
// src/ui/useDoubleCtrlC.ts
|
|
437
|
+
import { useState as useState2, useCallback, useEffect as useEffect2, useRef } from "react";
|
|
438
|
+
var CTRL_C_TIMEOUT_MS = 1500;
|
|
439
|
+
function useDoubleCtrlC(onExit) {
|
|
440
|
+
const [doubleCtrlC, setDoubleCtrlC] = useState2(false);
|
|
441
|
+
const timerRef = useRef(null);
|
|
442
|
+
const onExitRef = useRef(onExit);
|
|
443
|
+
onExitRef.current = onExit;
|
|
433
444
|
useEffect2(() => {
|
|
445
|
+
return () => {
|
|
446
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
447
|
+
};
|
|
448
|
+
}, []);
|
|
449
|
+
const handleCtrlC = useCallback(() => {
|
|
450
|
+
if (doubleCtrlC) {
|
|
451
|
+
onExitRef.current();
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
setDoubleCtrlC(true);
|
|
455
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
456
|
+
timerRef.current = setTimeout(() => {
|
|
457
|
+
setDoubleCtrlC(false);
|
|
458
|
+
timerRef.current = null;
|
|
459
|
+
}, CTRL_C_TIMEOUT_MS);
|
|
460
|
+
}, [doubleCtrlC]);
|
|
461
|
+
return { doubleCtrlC, handleCtrlC };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/ui/ChatSession.tsx
|
|
465
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
466
|
+
var commandRegistry = /* @__PURE__ */ new Map();
|
|
467
|
+
function registerCommand(name, cmd) {
|
|
468
|
+
commandRegistry.set(name, cmd);
|
|
469
|
+
}
|
|
470
|
+
function getRegisteredCommands() {
|
|
471
|
+
return commandRegistry;
|
|
472
|
+
}
|
|
473
|
+
registerCommand("/exit", { desc: "\u9000\u51FA\u5BF9\u8BDD", handler: () => ({ kind: "exit" }) });
|
|
474
|
+
registerCommand("/quit", { desc: "\u9000\u51FA\u5BF9\u8BDD", handler: () => ({ kind: "exit" }) });
|
|
475
|
+
registerCommand("/help", {
|
|
476
|
+
desc: "\u663E\u793A\u5E2E\u52A9\u4FE1\u606F",
|
|
477
|
+
handler: () => {
|
|
478
|
+
const commands = getRegisteredCommands();
|
|
479
|
+
const lines = ["\u53EF\u7528\u547D\u4EE4\uFF1A"];
|
|
480
|
+
for (const [name, cmd] of commands) {
|
|
481
|
+
lines.push(` ${name.padEnd(16)}${cmd.desc}`);
|
|
482
|
+
}
|
|
483
|
+
return { kind: "text", content: lines.join("\n") };
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
registerCommand("/clear", { desc: "\u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2", handler: () => ({ kind: "clear" }) });
|
|
487
|
+
registerCommand("/version", { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => ({ kind: "text", content: "dskcode v0.0.0" }) });
|
|
488
|
+
registerCommand("/game", { desc: "\u542F\u52A8\u6E38\u620F", handler: () => ({ kind: "navigate", target: "game" }) });
|
|
489
|
+
registerCommand("/stock", { desc: "\u67E5\u770B\u80A1\u7968\u884C\u60C5", handler: () => ({ kind: "navigate", target: "stock" }) });
|
|
490
|
+
function ChatSession({ providerCount, toolCount, verbose, onLaunchGame, onLaunchStock }) {
|
|
491
|
+
const [offset, setOffset] = useState3(0);
|
|
492
|
+
const [messages, setMessages] = useState3([]);
|
|
493
|
+
const [input, setInput] = useState3("");
|
|
494
|
+
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => process.exit(0));
|
|
495
|
+
useInput(
|
|
496
|
+
useCallback2(
|
|
497
|
+
(input2, key) => {
|
|
498
|
+
if (input2 === "c" && key.ctrl) {
|
|
499
|
+
handleCtrlC();
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
[handleCtrlC]
|
|
503
|
+
)
|
|
504
|
+
);
|
|
505
|
+
useEffect3(() => {
|
|
434
506
|
const timer = setInterval(() => {
|
|
435
507
|
setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);
|
|
436
508
|
}, 500);
|
|
437
509
|
return () => clearInterval(timer);
|
|
438
510
|
}, []);
|
|
439
|
-
const handleSubmit =
|
|
511
|
+
const handleSubmit = useCallback2((value) => {
|
|
440
512
|
const trimmed = value.trim();
|
|
441
513
|
if (!trimmed) return;
|
|
442
514
|
if (trimmed.startsWith("/")) {
|
|
443
|
-
const cmd =
|
|
515
|
+
const cmd = commandRegistry.get(trimmed.toLowerCase());
|
|
444
516
|
if (cmd) {
|
|
445
|
-
if (trimmed.toLowerCase() === "/exit" || trimmed.toLowerCase() === "/quit") {
|
|
446
|
-
process.exit(0);
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
if (trimmed.toLowerCase() === "/clear") {
|
|
450
|
-
setMessages([]);
|
|
451
|
-
setInput("");
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
517
|
const result = cmd.handler();
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
518
|
+
switch (result.kind) {
|
|
519
|
+
case "exit":
|
|
520
|
+
process.exit(0);
|
|
521
|
+
return;
|
|
522
|
+
case "clear":
|
|
523
|
+
setMessages([]);
|
|
524
|
+
setInput("");
|
|
525
|
+
return;
|
|
526
|
+
case "navigate":
|
|
527
|
+
setInput("");
|
|
528
|
+
if (result.target === "game") {
|
|
529
|
+
onLaunchGame?.();
|
|
530
|
+
} else if (result.target === "stock") {
|
|
531
|
+
onLaunchStock?.();
|
|
532
|
+
}
|
|
533
|
+
return;
|
|
534
|
+
case "text":
|
|
535
|
+
setMessages((prev) => [
|
|
536
|
+
...prev,
|
|
537
|
+
{ role: "user", content: trimmed },
|
|
538
|
+
{ role: "assistant", content: result.content }
|
|
539
|
+
]);
|
|
540
|
+
setInput("");
|
|
541
|
+
return;
|
|
461
542
|
}
|
|
462
|
-
setInput("");
|
|
463
|
-
return;
|
|
464
543
|
}
|
|
465
544
|
setMessages((prev) => [
|
|
466
545
|
...prev,
|
|
@@ -476,7 +555,7 @@ function ChatSession({ providerCount, toolCount, verbose }) {
|
|
|
476
555
|
{ 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" }
|
|
477
556
|
]);
|
|
478
557
|
setInput("");
|
|
479
|
-
}, []);
|
|
558
|
+
}, [onLaunchGame, onLaunchStock]);
|
|
480
559
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
|
|
481
560
|
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", marginBottom: 1, children: [
|
|
482
561
|
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
|
|
@@ -515,19 +594,26 @@ function ChatSession({ providerCount, toolCount, verbose }) {
|
|
|
515
594
|
}
|
|
516
595
|
) })
|
|
517
596
|
] }),
|
|
518
|
-
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text4, { color: "#00ffff", dimColor: true, children: " " + "\u2500".repeat(36) }) })
|
|
597
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text4, { color: "#00ffff", dimColor: true, children: " " + "\u2500".repeat(36) }) }),
|
|
598
|
+
doubleCtrlC && /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text4, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
|
|
519
599
|
] });
|
|
520
600
|
}
|
|
521
601
|
|
|
522
602
|
// src/ui/GamePicker.tsx
|
|
523
|
-
import { Box as Box4, Text as Text5, useInput } from "ink";
|
|
524
|
-
import { useState as
|
|
603
|
+
import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
|
|
604
|
+
import { useState as useState4, useCallback as useCallback3 } from "react";
|
|
525
605
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
526
|
-
function GamePicker({ games, onSelect, onExit }) {
|
|
527
|
-
const [selectedIndex, setSelectedIndex] =
|
|
528
|
-
|
|
529
|
-
|
|
606
|
+
function GamePicker({ games, onSelect, onExit, onBackToChat }) {
|
|
607
|
+
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
608
|
+
const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));
|
|
609
|
+
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(exitAction);
|
|
610
|
+
useInput2(
|
|
611
|
+
useCallback3(
|
|
530
612
|
(input, key) => {
|
|
613
|
+
if (input === "c" && key.ctrl) {
|
|
614
|
+
handleCtrlC();
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
531
617
|
if (games.length === 0) return;
|
|
532
618
|
if (key.upArrow || input === "k") {
|
|
533
619
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : games.length - 1);
|
|
@@ -537,10 +623,11 @@ function GamePicker({ games, onSelect, onExit }) {
|
|
|
537
623
|
const game = games[selectedIndex];
|
|
538
624
|
if (game) onSelect(game);
|
|
539
625
|
} else if (key.escape || input === "q") {
|
|
540
|
-
|
|
626
|
+
if (onBackToChat) onBackToChat();
|
|
627
|
+
else onExit?.();
|
|
541
628
|
}
|
|
542
629
|
},
|
|
543
|
-
[games, selectedIndex, onSelect, onExit]
|
|
630
|
+
[games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC]
|
|
544
631
|
)
|
|
545
632
|
);
|
|
546
633
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
@@ -553,7 +640,8 @@ function GamePicker({ games, onSelect, onExit }) {
|
|
|
553
640
|
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text5, { color: "#888888", children: game.description }) })
|
|
554
641
|
] }, game.id);
|
|
555
642
|
}) }),
|
|
556
|
-
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text5, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) })
|
|
643
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text5, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) }),
|
|
644
|
+
doubleCtrlC && /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text5, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
|
|
557
645
|
] });
|
|
558
646
|
}
|
|
559
647
|
|
|
@@ -570,8 +658,8 @@ function listGames() {
|
|
|
570
658
|
}
|
|
571
659
|
|
|
572
660
|
// src/game/brick-breaker/index.tsx
|
|
573
|
-
import { Box as Box5, Text as Text6, useInput as
|
|
574
|
-
import { useState as
|
|
661
|
+
import { Box as Box5, Text as Text6, useInput as useInput3, render as render2 } from "ink";
|
|
662
|
+
import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback4 } from "react";
|
|
575
663
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
576
664
|
var GAME_WIDTH = 40;
|
|
577
665
|
var GAME_HEIGHT = 18;
|
|
@@ -703,13 +791,13 @@ function buildBoard(state) {
|
|
|
703
791
|
return lines.map((l) => `\u2502${l}\u2502`).join("\n");
|
|
704
792
|
}
|
|
705
793
|
function BrickBreakerGame({ onExit: _onExit }) {
|
|
706
|
-
const [initialLevel, setInitialLevel] =
|
|
707
|
-
const [selectingLevel, setSelectingLevel] =
|
|
708
|
-
const stateRef =
|
|
709
|
-
const [tick, setTick] =
|
|
710
|
-
const onExitRef =
|
|
794
|
+
const [initialLevel, setInitialLevel] = useState5(1);
|
|
795
|
+
const [selectingLevel, setSelectingLevel] = useState5(true);
|
|
796
|
+
const stateRef = useRef2(createInitialState(initialLevel));
|
|
797
|
+
const [tick, setTick] = useState5(0);
|
|
798
|
+
const onExitRef = useRef2(_onExit);
|
|
711
799
|
onExitRef.current = _onExit;
|
|
712
|
-
|
|
800
|
+
useEffect4(() => {
|
|
713
801
|
if (selectingLevel) return;
|
|
714
802
|
const interval = setInterval(() => {
|
|
715
803
|
update(stateRef.current);
|
|
@@ -717,18 +805,18 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
717
805
|
}, 80);
|
|
718
806
|
return () => clearInterval(interval);
|
|
719
807
|
}, [selectingLevel]);
|
|
720
|
-
const restart =
|
|
808
|
+
const restart = useCallback4((level) => {
|
|
721
809
|
const lv = level ?? stateRef.current.level;
|
|
722
810
|
stateRef.current = createInitialState(lv);
|
|
723
811
|
setInitialLevel(lv);
|
|
724
812
|
setSelectingLevel(false);
|
|
725
813
|
setTick(0);
|
|
726
814
|
}, []);
|
|
727
|
-
const startLevelSelect =
|
|
815
|
+
const startLevelSelect = useCallback4(() => {
|
|
728
816
|
setSelectingLevel(true);
|
|
729
817
|
}, []);
|
|
730
|
-
|
|
731
|
-
|
|
818
|
+
useInput3(
|
|
819
|
+
useCallback4((input, key) => {
|
|
732
820
|
const s2 = stateRef.current;
|
|
733
821
|
if (selectingLevel) {
|
|
734
822
|
if (input >= "1" && input <= "9") {
|
|
@@ -842,8 +930,8 @@ var brick_breaker_default = {
|
|
|
842
930
|
};
|
|
843
931
|
|
|
844
932
|
// src/game/coder-check/index.tsx
|
|
845
|
-
import { Box as Box6, Text as Text7, useInput as
|
|
846
|
-
import { useState as
|
|
933
|
+
import { Box as Box6, Text as Text7, useInput as useInput4, render as render3 } from "ink";
|
|
934
|
+
import { useState as useState6, useEffect as useEffect5, useRef as useRef3, useCallback as useCallback5 } from "react";
|
|
847
935
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
848
936
|
var GAME_W = 66;
|
|
849
937
|
var GAME_H = 20;
|
|
@@ -1235,26 +1323,26 @@ function buildGameView(s, scoreLines, scoreColor, message) {
|
|
|
1235
1323
|
return rows;
|
|
1236
1324
|
}
|
|
1237
1325
|
function CoderCheck({ onExit: _onExit }) {
|
|
1238
|
-
const stateRef =
|
|
1239
|
-
const [tick, setTick] =
|
|
1240
|
-
const [colorOffset, setColorOffset] =
|
|
1241
|
-
const onExitRef =
|
|
1326
|
+
const stateRef = useRef3(createInitialState2());
|
|
1327
|
+
const [tick, setTick] = useState6(0);
|
|
1328
|
+
const [colorOffset, setColorOffset] = useState6(0);
|
|
1329
|
+
const onExitRef = useRef3(_onExit);
|
|
1242
1330
|
onExitRef.current = _onExit;
|
|
1243
|
-
|
|
1331
|
+
useEffect5(() => {
|
|
1244
1332
|
const timer = setInterval(() => {
|
|
1245
1333
|
setColorOffset((prev) => (prev + 1) % CYBER_PALETTE2.length);
|
|
1246
1334
|
}, 400);
|
|
1247
1335
|
return () => clearInterval(timer);
|
|
1248
1336
|
}, []);
|
|
1249
|
-
|
|
1337
|
+
useEffect5(() => {
|
|
1250
1338
|
const interval = setInterval(() => {
|
|
1251
1339
|
update2(stateRef.current);
|
|
1252
1340
|
setTick((t) => t + 1);
|
|
1253
1341
|
}, 60);
|
|
1254
1342
|
return () => clearInterval(interval);
|
|
1255
1343
|
}, []);
|
|
1256
|
-
|
|
1257
|
-
|
|
1344
|
+
useInput4(
|
|
1345
|
+
useCallback5((input, key) => {
|
|
1258
1346
|
const s2 = stateRef.current;
|
|
1259
1347
|
if (s2.gameOver) {
|
|
1260
1348
|
if (input === "r") {
|
|
@@ -1381,8 +1469,296 @@ function initGames() {
|
|
|
1381
1469
|
// src/cli/index.tsx
|
|
1382
1470
|
import { render as render4 } from "ink";
|
|
1383
1471
|
import chalk3 from "chalk";
|
|
1384
|
-
|
|
1385
|
-
|
|
1472
|
+
|
|
1473
|
+
// src/stock/StockList.tsx
|
|
1474
|
+
import { Box as Box7, Text as Text8, useInput as useInput5 } from "ink";
|
|
1475
|
+
import { useState as useState7, useCallback as useCallback6, useEffect as useEffect6 } from "react";
|
|
1476
|
+
import asciichart from "asciichart";
|
|
1477
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1478
|
+
var MINUTE_API = "https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1";
|
|
1479
|
+
function normalizeApiCode(code) {
|
|
1480
|
+
if (code.startsWith("sh") || code.startsWith("sz")) return code;
|
|
1481
|
+
if (/^60|^68|^51/.test(code)) return "sh" + code;
|
|
1482
|
+
if (/^00|^30|^39/.test(code)) return "sz" + code;
|
|
1483
|
+
return "sh" + code;
|
|
1484
|
+
}
|
|
1485
|
+
async function fetchStockMinute(code) {
|
|
1486
|
+
const url = MINUTE_API.replace("{code}", normalizeApiCode(code));
|
|
1487
|
+
try {
|
|
1488
|
+
const resp = await fetch(url);
|
|
1489
|
+
const json = await resp.json();
|
|
1490
|
+
if (json.code !== 0) return null;
|
|
1491
|
+
const stockKey = normalizeApiCode(code);
|
|
1492
|
+
const stockData = json.data?.[stockKey];
|
|
1493
|
+
if (!stockData) return null;
|
|
1494
|
+
const rawMinutes = stockData.data?.data ?? [];
|
|
1495
|
+
const prices = [];
|
|
1496
|
+
for (const line of rawMinutes) {
|
|
1497
|
+
const parts = line.split(" ");
|
|
1498
|
+
if (parts.length >= 2) {
|
|
1499
|
+
const p = parseFloat(parts[1]);
|
|
1500
|
+
if (!isNaN(p)) prices.push(p);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
const qtKey = stockKey;
|
|
1504
|
+
const qt = stockData.qt?.[qtKey];
|
|
1505
|
+
let quote = null;
|
|
1506
|
+
if (qt && qt.length >= 35) {
|
|
1507
|
+
quote = {
|
|
1508
|
+
code,
|
|
1509
|
+
name: qt[1] ?? "",
|
|
1510
|
+
price: parseFloat(qt[3] ?? "0"),
|
|
1511
|
+
changePercent: parseFloat(qt[32] ?? "0"),
|
|
1512
|
+
changeAmount: parseFloat(qt[31] ?? "0"),
|
|
1513
|
+
high: parseFloat(qt[33] ?? "0"),
|
|
1514
|
+
low: parseFloat(qt[34] ?? "0"),
|
|
1515
|
+
volume: parseInt(qt[6] ?? "0", 10)
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
return {
|
|
1519
|
+
prices,
|
|
1520
|
+
quote,
|
|
1521
|
+
date: stockData.data?.date ?? ""
|
|
1522
|
+
};
|
|
1523
|
+
} catch {
|
|
1524
|
+
return null;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
var minuteCache = /* @__PURE__ */ new Map();
|
|
1528
|
+
function cacheMinute(code, prices) {
|
|
1529
|
+
minuteCache.set(code, prices);
|
|
1530
|
+
}
|
|
1531
|
+
var FALLBACK_STOCKS = [
|
|
1532
|
+
{ code: "000001", name: "\u4E0A\u8BC1\u6307\u6570", price: 3150, changePercent: 0.35, changeAmount: 11.02, high: 3160, low: 3140, volume: 28543e4 },
|
|
1533
|
+
{ code: "399006", name: "\u521B\u4E1A\u677F\u6307", price: 1820, changePercent: -0.52, changeAmount: -9.5, high: 1835, low: 1815, volume: 9865e4 },
|
|
1534
|
+
{ code: "601688", name: "\u534E\u6CF0\u8BC1\u5238", price: 14.25, changePercent: 1.05, changeAmount: 0.15, high: 14.38, low: 14.1, volume: 452100 }
|
|
1535
|
+
];
|
|
1536
|
+
async function fetchStocks(codes) {
|
|
1537
|
+
const results = await Promise.all(
|
|
1538
|
+
codes.map(async (code) => {
|
|
1539
|
+
const data = await fetchStockMinute(code);
|
|
1540
|
+
if (data?.quote) {
|
|
1541
|
+
if (data.prices.length > 0) cacheMinute(code, data.prices);
|
|
1542
|
+
return data.quote;
|
|
1543
|
+
}
|
|
1544
|
+
return null;
|
|
1545
|
+
})
|
|
1546
|
+
);
|
|
1547
|
+
const real = results.filter((r) => r !== null);
|
|
1548
|
+
if (real.length > 0) return real;
|
|
1549
|
+
return FALLBACK_STOCKS;
|
|
1550
|
+
}
|
|
1551
|
+
function formatPrice(p) {
|
|
1552
|
+
return p >= 100 ? p.toFixed(2) : p.toFixed(3);
|
|
1553
|
+
}
|
|
1554
|
+
function formatVolume(v) {
|
|
1555
|
+
if (v >= 1e4) return (v / 1e4).toFixed(1) + "\u4E07";
|
|
1556
|
+
return v.toLocaleString();
|
|
1557
|
+
}
|
|
1558
|
+
function latestPoints(data, maxPoints = 60) {
|
|
1559
|
+
if (data.length <= maxPoints) return data;
|
|
1560
|
+
return data.slice(data.length - maxPoints);
|
|
1561
|
+
}
|
|
1562
|
+
function StockList({ codes, onExit, onBackToChat }) {
|
|
1563
|
+
const [stocks, setStocks] = useState7([]);
|
|
1564
|
+
const [selectedIndex, setSelectedIndex] = useState7(0);
|
|
1565
|
+
const [loading, setLoading] = useState7(true);
|
|
1566
|
+
const [lastUpdate, setLastUpdate] = useState7("");
|
|
1567
|
+
const [detailView, setDetailView] = useState7(null);
|
|
1568
|
+
const [detailPrices, setDetailPrices] = useState7(null);
|
|
1569
|
+
const [detailLoading, setDetailLoading] = useState7(false);
|
|
1570
|
+
const [detailCountdown, setDetailCountdown] = useState7(10);
|
|
1571
|
+
const [countdown, setCountdown] = useState7(5);
|
|
1572
|
+
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(onExit);
|
|
1573
|
+
const loadData = useCallback6(async () => {
|
|
1574
|
+
setLoading(true);
|
|
1575
|
+
try {
|
|
1576
|
+
const data = await fetchStocks(codes ?? []);
|
|
1577
|
+
setStocks(data);
|
|
1578
|
+
setLastUpdate((/* @__PURE__ */ new Date()).toLocaleTimeString());
|
|
1579
|
+
} catch {
|
|
1580
|
+
}
|
|
1581
|
+
setLoading(false);
|
|
1582
|
+
}, [codes]);
|
|
1583
|
+
useEffect6(() => {
|
|
1584
|
+
loadData();
|
|
1585
|
+
}, [loadData]);
|
|
1586
|
+
useEffect6(() => {
|
|
1587
|
+
const interval = setInterval(() => {
|
|
1588
|
+
setCountdown((prev) => {
|
|
1589
|
+
if (prev <= 1) {
|
|
1590
|
+
loadData();
|
|
1591
|
+
return 5;
|
|
1592
|
+
}
|
|
1593
|
+
return prev - 1;
|
|
1594
|
+
});
|
|
1595
|
+
}, 1e3);
|
|
1596
|
+
return () => clearInterval(interval);
|
|
1597
|
+
}, [loadData]);
|
|
1598
|
+
useEffect6(() => {
|
|
1599
|
+
if (!detailView) {
|
|
1600
|
+
setDetailPrices(null);
|
|
1601
|
+
setDetailLoading(false);
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
const loadDetail = () => {
|
|
1605
|
+
minuteCache.delete(detailView.code);
|
|
1606
|
+
fetchStockMinute(detailView.code).then((data) => {
|
|
1607
|
+
if (data && data.prices.length > 0) {
|
|
1608
|
+
cacheMinute(detailView.code, data.prices);
|
|
1609
|
+
setDetailPrices(data.prices);
|
|
1610
|
+
}
|
|
1611
|
+
});
|
|
1612
|
+
};
|
|
1613
|
+
loadDetail();
|
|
1614
|
+
setDetailCountdown(10);
|
|
1615
|
+
const timer = setInterval(loadDetail, 1e4);
|
|
1616
|
+
return () => clearInterval(timer);
|
|
1617
|
+
}, [detailView]);
|
|
1618
|
+
useEffect6(() => {
|
|
1619
|
+
if (!detailView) return;
|
|
1620
|
+
const timer = setInterval(() => {
|
|
1621
|
+
setDetailCountdown((prev) => prev > 0 ? prev - 1 : 10);
|
|
1622
|
+
}, 1e3);
|
|
1623
|
+
return () => clearInterval(timer);
|
|
1624
|
+
}, [detailView]);
|
|
1625
|
+
useInput5(
|
|
1626
|
+
useCallback6(
|
|
1627
|
+
(input, key) => {
|
|
1628
|
+
if (input === "c" && key.ctrl) {
|
|
1629
|
+
handleCtrlC();
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
if (detailView) {
|
|
1633
|
+
if (key.escape || input === "q" || input === " ") {
|
|
1634
|
+
setDetailView(null);
|
|
1635
|
+
}
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
if (stocks.length === 0) return;
|
|
1639
|
+
if (key.upArrow || input === "k") {
|
|
1640
|
+
setSelectedIndex((prev) => prev > 0 ? prev - 1 : stocks.length - 1);
|
|
1641
|
+
} else if (key.downArrow || input === "j") {
|
|
1642
|
+
setSelectedIndex((prev) => prev < stocks.length - 1 ? prev + 1 : 0);
|
|
1643
|
+
} else if (key.return) {
|
|
1644
|
+
const stock = stocks[selectedIndex];
|
|
1645
|
+
if (stock) setDetailView(stock);
|
|
1646
|
+
} else if (key.escape || input === "q") {
|
|
1647
|
+
if (onBackToChat) onBackToChat();
|
|
1648
|
+
else onExit();
|
|
1649
|
+
} else if (input === "r") {
|
|
1650
|
+
setCountdown(5);
|
|
1651
|
+
loadData();
|
|
1652
|
+
}
|
|
1653
|
+
},
|
|
1654
|
+
[stocks, selectedIndex, detailView, onExit, onBackToChat, loadData, handleCtrlC]
|
|
1655
|
+
)
|
|
1656
|
+
);
|
|
1657
|
+
if (detailView) {
|
|
1658
|
+
if (detailLoading) {
|
|
1659
|
+
return /* @__PURE__ */ jsx7(Box7, { paddingLeft: 1, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
|
|
1660
|
+
}
|
|
1661
|
+
return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown);
|
|
1662
|
+
}
|
|
1663
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1664
|
+
/* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
1665
|
+
/* @__PURE__ */ jsx7(Text8, { bold: true, color: "#00ffff", children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
|
|
1666
|
+
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` \u6BCF ${countdown}s \u81EA\u52A8\u5237\u65B0` })
|
|
1667
|
+
] }),
|
|
1668
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1669
|
+
/* @__PURE__ */ jsx7(Box7, { width: 3 }),
|
|
1670
|
+
/* @__PURE__ */ jsx7(Box7, { width: 9, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u4EE3\u7801" }) }),
|
|
1671
|
+
/* @__PURE__ */ jsx7(Box7, { width: 16, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u540D\u79F0" }) }),
|
|
1672
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
|
|
1673
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6DA8\u8DCC\u5E45" }) }),
|
|
1674
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
|
|
1675
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6700\u9AD8" }) }),
|
|
1676
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6700\u4F4E" }) }),
|
|
1677
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6210\u4EA4\u91CF" }) })
|
|
1678
|
+
] }),
|
|
1679
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
|
|
1680
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: stocks.map((stock, index) => {
|
|
1681
|
+
const isSelected = index === selectedIndex;
|
|
1682
|
+
const isUp = stock.changePercent >= 0;
|
|
1683
|
+
const color = isUp ? "#ff1493" : "#00ff41";
|
|
1684
|
+
return /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1685
|
+
/* @__PURE__ */ jsx7(Box7, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx7(Text8, { bold: true, color: "#00ffff", children: "\u25B8 " }) : /* @__PURE__ */ jsx7(Text8, { children: " " }) }),
|
|
1686
|
+
/* @__PURE__ */ jsx7(Box7, { width: 9, children: /* @__PURE__ */ jsx7(Text8, { bold: true, color: isSelected ? "#00ffff" : "#ffffff", children: stock.code }) }),
|
|
1687
|
+
/* @__PURE__ */ jsx7(Box7, { width: 16, children: /* @__PURE__ */ jsx7(Text8, { color: isSelected ? "#ffffff" : "#cccccc", children: stock.name }) }),
|
|
1688
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { bold: true, color, children: formatPrice(stock.price) }) }),
|
|
1689
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsxs7(Text8, { color, children: [
|
|
1690
|
+
isUp ? "+" : "",
|
|
1691
|
+
stock.changePercent.toFixed(2),
|
|
1692
|
+
"%"
|
|
1693
|
+
] }) }),
|
|
1694
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsxs7(Text8, { color, children: [
|
|
1695
|
+
isUp ? "+" : "",
|
|
1696
|
+
stock.changeAmount.toFixed(3)
|
|
1697
|
+
] }) }),
|
|
1698
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { color: "#cccccc", children: formatPrice(stock.high) }) }),
|
|
1699
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { color: "#cccccc", children: formatPrice(stock.low) }) }),
|
|
1700
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { color: "#888888", children: formatVolume(stock.volume) }) })
|
|
1701
|
+
] }, stock.code);
|
|
1702
|
+
}) }),
|
|
1703
|
+
/* @__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` }) }),
|
|
1704
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate}` }) }),
|
|
1705
|
+
doubleCtrlC && /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text8, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
|
|
1706
|
+
] });
|
|
1707
|
+
}
|
|
1708
|
+
function renderDetail(stock, _onBack, prices, countdown = 10) {
|
|
1709
|
+
const isUp = stock.changePercent >= 0;
|
|
1710
|
+
const colorCode = isUp ? "#ff1493" : "#00ff41";
|
|
1711
|
+
const arrow = isUp ? "\u25B2" : "\u25BC";
|
|
1712
|
+
let chartLines = [];
|
|
1713
|
+
if (prices && prices.length > 0) {
|
|
1714
|
+
const chartColor = isUp ? asciichart.red : asciichart.green;
|
|
1715
|
+
const latest = latestPoints(prices, 60);
|
|
1716
|
+
let raw = asciichart.plot(latest, {
|
|
1717
|
+
height: 10,
|
|
1718
|
+
colors: [chartColor]
|
|
1719
|
+
});
|
|
1720
|
+
raw = raw.replaceAll("\u256D", "\u250C").replaceAll("\u256E", "\u2510").replaceAll("\u2570", "\u2514").replaceAll("\u256F", "\u2518");
|
|
1721
|
+
chartLines = raw.split("\n");
|
|
1722
|
+
}
|
|
1723
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingLeft: 1, children: [
|
|
1724
|
+
/* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
1725
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1726
|
+
/* @__PURE__ */ jsxs7(Text8, { bold: true, color: "#00ffff", children: [
|
|
1727
|
+
" \u{1F4CA} ",
|
|
1728
|
+
stock.name,
|
|
1729
|
+
" "
|
|
1730
|
+
] }),
|
|
1731
|
+
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: stock.code })
|
|
1732
|
+
] }),
|
|
1733
|
+
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: `\u6BCF ${countdown}s \u5237\u65B0` })
|
|
1734
|
+
] }),
|
|
1735
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1736
|
+
/* @__PURE__ */ jsx7(Box7, { width: 16, children: /* @__PURE__ */ jsx7(Text8, { bold: true, color: "#888888", children: "\u5F53\u524D\u4EF7" }) }),
|
|
1737
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text8, { bold: true, color: colorCode, children: [
|
|
1738
|
+
arrow,
|
|
1739
|
+
" ",
|
|
1740
|
+
formatPrice(stock.price)
|
|
1741
|
+
] }) })
|
|
1742
|
+
] }),
|
|
1743
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1744
|
+
/* @__PURE__ */ jsx7(Box7, { width: 16, children: /* @__PURE__ */ jsx7(Text8, { color: "#888888", children: "\u6DA8\u8DCC\u5E45" }) }),
|
|
1745
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text8, { color: colorCode, children: [
|
|
1746
|
+
isUp ? "+" : "",
|
|
1747
|
+
stock.changePercent.toFixed(2),
|
|
1748
|
+
"%",
|
|
1749
|
+
" ",
|
|
1750
|
+
isUp ? "+" : "",
|
|
1751
|
+
stock.changeAmount.toFixed(3)
|
|
1752
|
+
] }) })
|
|
1753
|
+
] }),
|
|
1754
|
+
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)) }),
|
|
1755
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: " Space/q \u8FD4\u56DE\u5217\u8868" }) })
|
|
1756
|
+
] });
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
// src/cli/index.tsx
|
|
1760
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1761
|
+
var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion", "game", "stock"];
|
|
1386
1762
|
function createCli() {
|
|
1387
1763
|
const program2 = new Command();
|
|
1388
1764
|
program2.exitOverride();
|
|
@@ -1407,18 +1783,64 @@ function createCli() {
|
|
|
1407
1783
|
const result = await loadAndValidate();
|
|
1408
1784
|
ctx = { ...ctx, config: result.config };
|
|
1409
1785
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1786
|
+
startChat(ctx);
|
|
1787
|
+
});
|
|
1788
|
+
function startChat(ctx) {
|
|
1789
|
+
const chatApp = renderApp(
|
|
1790
|
+
/* @__PURE__ */ jsx8(
|
|
1412
1791
|
ChatSession,
|
|
1413
1792
|
{
|
|
1414
1793
|
providerCount: ctx?.config.providers.length ?? 1,
|
|
1415
1794
|
toolCount: ctx?.config.tools.length ?? 0,
|
|
1416
|
-
verbose: ctx?.verbose ?? false
|
|
1795
|
+
verbose: ctx?.verbose ?? false,
|
|
1796
|
+
onLaunchGame: () => {
|
|
1797
|
+
chatApp.unmount();
|
|
1798
|
+
setImmediate(() => {
|
|
1799
|
+
initGames();
|
|
1800
|
+
const games = listGames();
|
|
1801
|
+
const { unmount } = render4(
|
|
1802
|
+
/* @__PURE__ */ jsx8(
|
|
1803
|
+
GamePicker,
|
|
1804
|
+
{
|
|
1805
|
+
games,
|
|
1806
|
+
onSelect: async (game) => {
|
|
1807
|
+
unmount();
|
|
1808
|
+
await game.play();
|
|
1809
|
+
startChat(ctx);
|
|
1810
|
+
},
|
|
1811
|
+
onBackToChat: () => {
|
|
1812
|
+
unmount();
|
|
1813
|
+
setImmediate(() => startChat(ctx));
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
),
|
|
1817
|
+
{ exitOnCtrlC: false }
|
|
1818
|
+
);
|
|
1819
|
+
});
|
|
1820
|
+
},
|
|
1821
|
+
onLaunchStock: () => {
|
|
1822
|
+
chatApp.unmount();
|
|
1823
|
+
setImmediate(() => {
|
|
1824
|
+
const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
1825
|
+
const stockApp = renderApp(
|
|
1826
|
+
/* @__PURE__ */ jsx8(
|
|
1827
|
+
StockList,
|
|
1828
|
+
{
|
|
1829
|
+
codes: defaultStockCodes,
|
|
1830
|
+
onBackToChat: () => {
|
|
1831
|
+
stockApp.unmount();
|
|
1832
|
+
setImmediate(() => startChat(ctx));
|
|
1833
|
+
},
|
|
1834
|
+
onExit: () => process.exit(0)
|
|
1835
|
+
}
|
|
1836
|
+
)
|
|
1837
|
+
);
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1417
1840
|
}
|
|
1418
1841
|
)
|
|
1419
1842
|
);
|
|
1420
|
-
|
|
1421
|
-
});
|
|
1843
|
+
}
|
|
1422
1844
|
program2.command("run").description("\u6267\u884C\u4E00\u6B21\u6027\u4EFB\u52A1").argument("[prompt...]", "\u4EFB\u52A1\u63CF\u8FF0").option("--model <name>", "\u6307\u5B9A\u4F7F\u7528\u7684\u6A21\u578B").action(async function(_prompt) {
|
|
1423
1845
|
console.log("dskcode run \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C07\u7AE0\uFF09");
|
|
1424
1846
|
});
|
|
@@ -1455,12 +1877,27 @@ _dskcode_completion() {
|
|
|
1455
1877
|
"init:\u751F\u6210\u9879\u76EE\u8BB0\u5FC6\u6587\u4EF6"
|
|
1456
1878
|
"completion:\u8F93\u51FA shell \u81EA\u52A8\u8865\u5168\u8BF4\u660E"
|
|
1457
1879
|
"game:\u5185\u7F6E\u5C0F\u6E38\u620F"
|
|
1880
|
+
"stock:\u67E5\u770B\u81EA\u9009\u80A1\u5B9E\u65F6\u884C\u60C5"
|
|
1458
1881
|
)
|
|
1459
1882
|
_describe 'dskcode commands' commands
|
|
1460
1883
|
}
|
|
1461
1884
|
compdef _dskcode_completion dskcode`);
|
|
1462
1885
|
}
|
|
1463
1886
|
});
|
|
1887
|
+
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) {
|
|
1888
|
+
const ctx = this.dskcodeCtx;
|
|
1889
|
+
const codeList = codes && codes.length > 0 ? codes : ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
1890
|
+
const app = renderApp(
|
|
1891
|
+
/* @__PURE__ */ jsx8(
|
|
1892
|
+
StockList,
|
|
1893
|
+
{
|
|
1894
|
+
codes: codeList,
|
|
1895
|
+
onExit: () => process.exit(0)
|
|
1896
|
+
}
|
|
1897
|
+
)
|
|
1898
|
+
);
|
|
1899
|
+
await app.waitUntilExit;
|
|
1900
|
+
});
|
|
1464
1901
|
initGames();
|
|
1465
1902
|
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) {
|
|
1466
1903
|
if (name) {
|
|
@@ -1480,7 +1917,7 @@ compdef _dskcode_completion dskcode`);
|
|
|
1480
1917
|
}
|
|
1481
1918
|
const selectedGame = await new Promise((resolve) => {
|
|
1482
1919
|
const { unmount } = render4(
|
|
1483
|
-
/* @__PURE__ */
|
|
1920
|
+
/* @__PURE__ */ jsx8(
|
|
1484
1921
|
GamePicker,
|
|
1485
1922
|
{
|
|
1486
1923
|
games,
|
|
@@ -1493,7 +1930,8 @@ compdef _dskcode_completion dskcode`);
|
|
|
1493
1930
|
resolve(null);
|
|
1494
1931
|
}
|
|
1495
1932
|
}
|
|
1496
|
-
)
|
|
1933
|
+
),
|
|
1934
|
+
{ exitOnCtrlC: false }
|
|
1497
1935
|
);
|
|
1498
1936
|
});
|
|
1499
1937
|
if (selectedGame) {
|
|
@@ -1520,8 +1958,18 @@ var ExitCode = {
|
|
|
1520
1958
|
};
|
|
1521
1959
|
|
|
1522
1960
|
// src/index.ts
|
|
1961
|
+
var sigintCount = 0;
|
|
1962
|
+
var sigintTimer = null;
|
|
1523
1963
|
process.on("SIGINT", () => {
|
|
1524
|
-
|
|
1964
|
+
sigintCount++;
|
|
1965
|
+
if (sigintCount >= 2) {
|
|
1966
|
+
process.exit(ExitCode.SIGINT);
|
|
1967
|
+
}
|
|
1968
|
+
process.stdout.write("\n \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode\n");
|
|
1969
|
+
if (sigintTimer) clearTimeout(sigintTimer);
|
|
1970
|
+
sigintTimer = setTimeout(() => {
|
|
1971
|
+
sigintCount = 0;
|
|
1972
|
+
}, 1500);
|
|
1525
1973
|
});
|
|
1526
1974
|
var program = createCli();
|
|
1527
1975
|
try {
|