dskcode 0.1.6 → 0.1.8
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 +23 -4
- package/dist/index.js +367 -165
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -88,28 +88,28 @@ var ENV_MAP = {
|
|
|
88
88
|
[`${ENV_PREFIX}MAX_TOOL_ROUNDS`]: "maxToolRounds"
|
|
89
89
|
};
|
|
90
90
|
function applyEnvVars(config) {
|
|
91
|
+
const result = { ...config, providers: [...config.providers] };
|
|
91
92
|
for (const [envKey, configKey] of Object.entries(ENV_MAP)) {
|
|
92
93
|
const raw = process.env[envKey];
|
|
93
94
|
if (raw === void 0) continue;
|
|
94
|
-
const cfg = config;
|
|
95
95
|
switch (configKey) {
|
|
96
96
|
case "verbose":
|
|
97
97
|
case "defaultProvider": {
|
|
98
|
-
|
|
98
|
+
result[configKey] = raw;
|
|
99
99
|
break;
|
|
100
100
|
}
|
|
101
101
|
case "maxTokens":
|
|
102
102
|
case "maxToolRounds": {
|
|
103
103
|
const n = Number(raw);
|
|
104
104
|
if (Number.isFinite(n) && n > 0) {
|
|
105
|
-
|
|
105
|
+
result[configKey] = n;
|
|
106
106
|
}
|
|
107
107
|
break;
|
|
108
108
|
}
|
|
109
109
|
case "temperature": {
|
|
110
110
|
const n = Number(raw);
|
|
111
111
|
if (Number.isFinite(n) && n >= 0 && n <= 2) {
|
|
112
|
-
|
|
112
|
+
result[configKey] = n;
|
|
113
113
|
}
|
|
114
114
|
break;
|
|
115
115
|
}
|
|
@@ -117,12 +117,14 @@ function applyEnvVars(config) {
|
|
|
117
117
|
}
|
|
118
118
|
const apiKey = process.env.DEEPSEEK_API_KEY;
|
|
119
119
|
if (apiKey) {
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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({
|
|
126
128
|
name: "deepseek",
|
|
127
129
|
baseUrl: "https://api.deepseek.com",
|
|
128
130
|
model: "deepseek-v4-flash",
|
|
@@ -130,27 +132,31 @@ function applyEnvVars(config) {
|
|
|
130
132
|
});
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
|
-
return
|
|
135
|
+
return result;
|
|
134
136
|
}
|
|
135
137
|
function applyCliOverrides(config, flags) {
|
|
138
|
+
const result = { ...config, providers: [...config.providers] };
|
|
136
139
|
if (flags.verbose !== void 0) {
|
|
137
|
-
|
|
140
|
+
result.verbose = flags.verbose;
|
|
138
141
|
}
|
|
139
142
|
if (flags.model !== void 0) {
|
|
140
|
-
const
|
|
141
|
-
(p) => p.name ===
|
|
143
|
+
const providerIdx = result.providers.findIndex(
|
|
144
|
+
(p) => p.name === result.defaultProvider
|
|
142
145
|
);
|
|
143
|
-
if (
|
|
144
|
-
|
|
146
|
+
if (providerIdx !== -1) {
|
|
147
|
+
result.providers[providerIdx] = {
|
|
148
|
+
...result.providers[providerIdx],
|
|
149
|
+
model: flags.model
|
|
150
|
+
};
|
|
145
151
|
}
|
|
146
152
|
}
|
|
147
153
|
if (flags.maxTokens !== void 0 && flags.maxTokens > 0) {
|
|
148
|
-
|
|
154
|
+
result.maxTokens = flags.maxTokens;
|
|
149
155
|
}
|
|
150
156
|
if (flags.temperature !== void 0 && flags.temperature >= 0 && flags.temperature <= 2) {
|
|
151
|
-
|
|
157
|
+
result.temperature = flags.temperature;
|
|
152
158
|
}
|
|
153
|
-
return
|
|
159
|
+
return result;
|
|
154
160
|
}
|
|
155
161
|
function validateConfig(config) {
|
|
156
162
|
const errors = [];
|
|
@@ -192,6 +198,12 @@ function validateConfig(config) {
|
|
|
192
198
|
message: "temperature \u5FC5\u987B\u5728 0.0 ~ 2.0 \u4E4B\u95F4\u3002"
|
|
193
199
|
});
|
|
194
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
|
+
}
|
|
195
207
|
if (config.maxToolRounds !== void 0 && config.maxToolRounds < 1) {
|
|
196
208
|
errors.push({
|
|
197
209
|
field: "maxToolRounds",
|
|
@@ -247,6 +259,22 @@ async function saveApiKey(apiKey) {
|
|
|
247
259
|
await writeFile(configFile, JSON.stringify(configData, null, 2), "utf-8");
|
|
248
260
|
return configFile;
|
|
249
261
|
}
|
|
262
|
+
async function saveStockConfig(symbols) {
|
|
263
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
264
|
+
const configDir = join(home, ".dskcode");
|
|
265
|
+
const configFile = join(configDir, "settings.json");
|
|
266
|
+
await mkdir(configDir, { recursive: true });
|
|
267
|
+
let configData;
|
|
268
|
+
try {
|
|
269
|
+
const raw = await readFile(configFile, "utf-8");
|
|
270
|
+
configData = JSON.parse(raw);
|
|
271
|
+
} catch {
|
|
272
|
+
configData = structuredClone(defaultConfig);
|
|
273
|
+
}
|
|
274
|
+
configData.stock = { symbols };
|
|
275
|
+
await writeFile(configFile, JSON.stringify(configData, null, 2), "utf-8");
|
|
276
|
+
return configFile;
|
|
277
|
+
}
|
|
250
278
|
|
|
251
279
|
// src/cli/middleware.ts
|
|
252
280
|
async function loadConfigMiddleware() {
|
|
@@ -339,36 +367,32 @@ function hasApiKey(providers) {
|
|
|
339
367
|
if (process.env.DEEPSEEK_API_KEY) return true;
|
|
340
368
|
return false;
|
|
341
369
|
}
|
|
370
|
+
var MIN_API_KEY_LENGTH = 10;
|
|
342
371
|
async function promptForApiKey() {
|
|
343
|
-
console.log(
|
|
344
|
-
|
|
345
|
-
);
|
|
346
|
-
console.log(
|
|
347
|
-
|
|
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
|
-
);
|
|
372
|
+
console.log(chalk2.yellow("\n \u26A0 \u672A\u68C0\u6D4B\u5230 API Key \u914D\u7F6E"));
|
|
373
|
+
console.log(chalk2.dim(" \u4F60\u53EF\u4EE5\u901A\u8FC7\u4EE5\u4E0B\u4EFB\u4E00\u65B9\u5F0F\u914D\u7F6E\uFF1A"));
|
|
374
|
+
console.log(chalk2.dim(" \xB7 \u73AF\u5883\u53D8\u91CF: export DEEPSEEK_API_KEY=sk-xxx"));
|
|
375
|
+
console.log(chalk2.dim(" \xB7 \u914D\u7F6E\u6587\u4EF6: ~/.dskcode/settings.json"));
|
|
376
|
+
console.log(chalk2.dim(" \xB7 \u4E0B\u9762\u76F4\u63A5\u8F93\u5165\uFF0C\u81EA\u52A8\u4FDD\u5B58\u5230\u5168\u5C40\u914D\u7F6E\n"));
|
|
358
377
|
const rl = createInterface({
|
|
359
378
|
input: process.stdin,
|
|
360
379
|
output: process.stdout
|
|
361
380
|
});
|
|
362
381
|
return new Promise((resolve) => {
|
|
382
|
+
let resolved = false;
|
|
363
383
|
const cleanup = () => {
|
|
384
|
+
if (resolved) return;
|
|
385
|
+
resolved = true;
|
|
386
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
364
387
|
rl.close();
|
|
365
388
|
};
|
|
366
|
-
|
|
389
|
+
const onKeypress = (_, key) => {
|
|
367
390
|
if (key.ctrl && key.name === "c") {
|
|
368
391
|
cleanup();
|
|
369
392
|
resolve(null);
|
|
370
393
|
}
|
|
371
|
-
}
|
|
394
|
+
};
|
|
395
|
+
process.stdin.on("keypress", onKeypress);
|
|
372
396
|
rl.question(
|
|
373
397
|
` ${chalk2.cyan("\u{1F511}")} ${chalk2.bold("\u8BF7\u8F93\u5165\u4F60\u7684 DeepSeek API Key:")} `,
|
|
374
398
|
(answer) => {
|
|
@@ -379,7 +403,7 @@ async function promptForApiKey() {
|
|
|
379
403
|
resolve(null);
|
|
380
404
|
return;
|
|
381
405
|
}
|
|
382
|
-
if (trimmed.length <
|
|
406
|
+
if (trimmed.length < MIN_API_KEY_LENGTH) {
|
|
383
407
|
console.log(chalk2.red(" \u2716 API Key \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u957F\u5EA6\u81F3\u5C11 10 \u4F4D"));
|
|
384
408
|
resolve(null);
|
|
385
409
|
return;
|
|
@@ -390,10 +414,14 @@ async function promptForApiKey() {
|
|
|
390
414
|
});
|
|
391
415
|
}
|
|
392
416
|
|
|
417
|
+
// src/cli/index.tsx
|
|
418
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
419
|
+
import { join as join2 } from "path";
|
|
420
|
+
|
|
393
421
|
// src/ui/RenderScope.tsx
|
|
394
422
|
import { render } from "ink";
|
|
395
423
|
function renderApp(node) {
|
|
396
|
-
const { waitUntilExit, clear, unmount } = render(node);
|
|
424
|
+
const { waitUntilExit, clear, unmount } = render(node, { exitOnCtrlC: false });
|
|
397
425
|
return { waitUntilExit: waitUntilExit(), clear, unmount };
|
|
398
426
|
}
|
|
399
427
|
|
|
@@ -410,12 +438,6 @@ import { jsxs as jsxs2 } from "react/jsx-runtime";
|
|
|
410
438
|
import { Box as Box2, Text as Text3 } from "ink";
|
|
411
439
|
import { useEffect, useState } from "react";
|
|
412
440
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
413
|
-
|
|
414
|
-
// src/ui/ChatSession.tsx
|
|
415
|
-
import { Box as Box3, Text as Text4 } from "ink";
|
|
416
|
-
import TextInput from "ink-text-input";
|
|
417
|
-
import { useEffect as useEffect2, useState as useState2, useCallback } from "react";
|
|
418
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
419
441
|
var CYBER_PALETTE = ["#00ffff", "#ff00ff", "#00ff41", "#ff1493", "#8b00ff"];
|
|
420
442
|
var LOGO_LINES = [
|
|
421
443
|
" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
@@ -425,57 +447,119 @@ var LOGO_LINES = [
|
|
|
425
447
|
" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557",
|
|
426
448
|
" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"
|
|
427
449
|
];
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
"/version": { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => "dskcode v0.0.0" }
|
|
443
|
-
};
|
|
444
|
-
function ChatSession({ providerCount, toolCount, verbose }) {
|
|
445
|
-
const [offset, setOffset] = useState2(0);
|
|
446
|
-
const [messages, setMessages] = useState2([]);
|
|
447
|
-
const [input, setInput] = useState2("");
|
|
450
|
+
|
|
451
|
+
// src/ui/ChatSession.tsx
|
|
452
|
+
import { Box as Box3, Text as Text4, useInput } from "ink";
|
|
453
|
+
import TextInput from "ink-text-input";
|
|
454
|
+
import { useEffect as useEffect3, useState as useState3, useCallback as useCallback2 } from "react";
|
|
455
|
+
|
|
456
|
+
// src/ui/useDoubleCtrlC.ts
|
|
457
|
+
import { useState as useState2, useCallback, useEffect as useEffect2, useRef } from "react";
|
|
458
|
+
var CTRL_C_TIMEOUT_MS = 1500;
|
|
459
|
+
function useDoubleCtrlC(onExit) {
|
|
460
|
+
const [doubleCtrlC, setDoubleCtrlC] = useState2(false);
|
|
461
|
+
const timerRef = useRef(null);
|
|
462
|
+
const onExitRef = useRef(onExit);
|
|
463
|
+
onExitRef.current = onExit;
|
|
448
464
|
useEffect2(() => {
|
|
465
|
+
return () => {
|
|
466
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
467
|
+
};
|
|
468
|
+
}, []);
|
|
469
|
+
const handleCtrlC = useCallback(() => {
|
|
470
|
+
if (doubleCtrlC) {
|
|
471
|
+
onExitRef.current();
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
setDoubleCtrlC(true);
|
|
475
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
476
|
+
timerRef.current = setTimeout(() => {
|
|
477
|
+
setDoubleCtrlC(false);
|
|
478
|
+
timerRef.current = null;
|
|
479
|
+
}, CTRL_C_TIMEOUT_MS);
|
|
480
|
+
}, [doubleCtrlC]);
|
|
481
|
+
return { doubleCtrlC, handleCtrlC };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// src/ui/ChatSession.tsx
|
|
485
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
486
|
+
var commandRegistry = /* @__PURE__ */ new Map();
|
|
487
|
+
function registerCommand(name, cmd) {
|
|
488
|
+
commandRegistry.set(name, cmd);
|
|
489
|
+
}
|
|
490
|
+
function getRegisteredCommands() {
|
|
491
|
+
return commandRegistry;
|
|
492
|
+
}
|
|
493
|
+
registerCommand("/exit", { desc: "\u9000\u51FA\u5BF9\u8BDD", handler: () => ({ kind: "exit" }) });
|
|
494
|
+
registerCommand("/quit", { desc: "\u9000\u51FA\u5BF9\u8BDD", handler: () => ({ kind: "exit" }) });
|
|
495
|
+
registerCommand("/help", {
|
|
496
|
+
desc: "\u663E\u793A\u5E2E\u52A9\u4FE1\u606F",
|
|
497
|
+
handler: () => {
|
|
498
|
+
const commands = getRegisteredCommands();
|
|
499
|
+
const lines = ["\u53EF\u7528\u547D\u4EE4\uFF1A"];
|
|
500
|
+
for (const [name, cmd] of commands) {
|
|
501
|
+
lines.push(` ${name.padEnd(16)}${cmd.desc}`);
|
|
502
|
+
}
|
|
503
|
+
return { kind: "text", content: lines.join("\n") };
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
registerCommand("/clear", { desc: "\u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2", handler: () => ({ kind: "clear" }) });
|
|
507
|
+
registerCommand("/version", { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => ({ kind: "text", content: "dskcode v0.0.0" }) });
|
|
508
|
+
registerCommand("/game", { desc: "\u542F\u52A8\u6E38\u620F", handler: () => ({ kind: "navigate", target: "game" }) });
|
|
509
|
+
registerCommand("/stock", { desc: "\u67E5\u770B\u80A1\u7968\u884C\u60C5", handler: () => ({ kind: "navigate", target: "stock" }) });
|
|
510
|
+
function ChatSession({ providerCount, toolCount, verbose, onLaunchGame, onLaunchStock }) {
|
|
511
|
+
const [offset, setOffset] = useState3(0);
|
|
512
|
+
const [messages, setMessages] = useState3([]);
|
|
513
|
+
const [input, setInput] = useState3("");
|
|
514
|
+
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => process.exit(0));
|
|
515
|
+
useInput(
|
|
516
|
+
useCallback2(
|
|
517
|
+
(input2, key) => {
|
|
518
|
+
if (input2 === "c" && key.ctrl) {
|
|
519
|
+
handleCtrlC();
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
[handleCtrlC]
|
|
523
|
+
)
|
|
524
|
+
);
|
|
525
|
+
useEffect3(() => {
|
|
449
526
|
const timer = setInterval(() => {
|
|
450
527
|
setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);
|
|
451
528
|
}, 500);
|
|
452
529
|
return () => clearInterval(timer);
|
|
453
530
|
}, []);
|
|
454
|
-
const handleSubmit =
|
|
531
|
+
const handleSubmit = useCallback2((value) => {
|
|
455
532
|
const trimmed = value.trim();
|
|
456
533
|
if (!trimmed) return;
|
|
457
534
|
if (trimmed.startsWith("/")) {
|
|
458
|
-
const cmd =
|
|
535
|
+
const cmd = commandRegistry.get(trimmed.toLowerCase());
|
|
459
536
|
if (cmd) {
|
|
460
|
-
if (trimmed.toLowerCase() === "/exit" || trimmed.toLowerCase() === "/quit") {
|
|
461
|
-
process.exit(0);
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
if (trimmed.toLowerCase() === "/clear") {
|
|
465
|
-
setMessages([]);
|
|
466
|
-
setInput("");
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
537
|
const result = cmd.handler();
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
538
|
+
switch (result.kind) {
|
|
539
|
+
case "exit":
|
|
540
|
+
process.exit(0);
|
|
541
|
+
return;
|
|
542
|
+
case "clear":
|
|
543
|
+
setMessages([]);
|
|
544
|
+
setInput("");
|
|
545
|
+
return;
|
|
546
|
+
case "navigate":
|
|
547
|
+
setInput("");
|
|
548
|
+
if (result.target === "game") {
|
|
549
|
+
onLaunchGame?.();
|
|
550
|
+
} else if (result.target === "stock") {
|
|
551
|
+
onLaunchStock?.();
|
|
552
|
+
}
|
|
553
|
+
return;
|
|
554
|
+
case "text":
|
|
555
|
+
setMessages((prev) => [
|
|
556
|
+
...prev,
|
|
557
|
+
{ role: "user", content: trimmed },
|
|
558
|
+
{ role: "assistant", content: result.content }
|
|
559
|
+
]);
|
|
560
|
+
setInput("");
|
|
561
|
+
return;
|
|
476
562
|
}
|
|
477
|
-
setInput("");
|
|
478
|
-
return;
|
|
479
563
|
}
|
|
480
564
|
setMessages((prev) => [
|
|
481
565
|
...prev,
|
|
@@ -491,7 +575,7 @@ function ChatSession({ providerCount, toolCount, verbose }) {
|
|
|
491
575
|
{ 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" }
|
|
492
576
|
]);
|
|
493
577
|
setInput("");
|
|
494
|
-
}, []);
|
|
578
|
+
}, [onLaunchGame, onLaunchStock]);
|
|
495
579
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
|
|
496
580
|
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", marginBottom: 1, children: [
|
|
497
581
|
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
|
|
@@ -530,19 +614,26 @@ function ChatSession({ providerCount, toolCount, verbose }) {
|
|
|
530
614
|
}
|
|
531
615
|
) })
|
|
532
616
|
] }),
|
|
533
|
-
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text4, { color: "#00ffff", dimColor: true, children: " " + "\u2500".repeat(36) }) })
|
|
617
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text4, { color: "#00ffff", dimColor: true, children: " " + "\u2500".repeat(36) }) }),
|
|
618
|
+
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" }) })
|
|
534
619
|
] });
|
|
535
620
|
}
|
|
536
621
|
|
|
537
622
|
// src/ui/GamePicker.tsx
|
|
538
|
-
import { Box as Box4, Text as Text5, useInput } from "ink";
|
|
539
|
-
import { useState as
|
|
623
|
+
import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
|
|
624
|
+
import { useState as useState4, useCallback as useCallback3 } from "react";
|
|
540
625
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
541
|
-
function GamePicker({ games, onSelect, onExit }) {
|
|
542
|
-
const [selectedIndex, setSelectedIndex] =
|
|
543
|
-
|
|
544
|
-
|
|
626
|
+
function GamePicker({ games, onSelect, onExit, onBackToChat }) {
|
|
627
|
+
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
628
|
+
const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));
|
|
629
|
+
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(exitAction);
|
|
630
|
+
useInput2(
|
|
631
|
+
useCallback3(
|
|
545
632
|
(input, key) => {
|
|
633
|
+
if (input === "c" && key.ctrl) {
|
|
634
|
+
handleCtrlC();
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
546
637
|
if (games.length === 0) return;
|
|
547
638
|
if (key.upArrow || input === "k") {
|
|
548
639
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : games.length - 1);
|
|
@@ -552,10 +643,11 @@ function GamePicker({ games, onSelect, onExit }) {
|
|
|
552
643
|
const game = games[selectedIndex];
|
|
553
644
|
if (game) onSelect(game);
|
|
554
645
|
} else if (key.escape || input === "q") {
|
|
555
|
-
|
|
646
|
+
if (onBackToChat) onBackToChat();
|
|
647
|
+
else onExit?.();
|
|
556
648
|
}
|
|
557
649
|
},
|
|
558
|
-
[games, selectedIndex, onSelect, onExit]
|
|
650
|
+
[games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC]
|
|
559
651
|
)
|
|
560
652
|
);
|
|
561
653
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
@@ -568,7 +660,8 @@ function GamePicker({ games, onSelect, onExit }) {
|
|
|
568
660
|
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text5, { color: "#888888", children: game.description }) })
|
|
569
661
|
] }, game.id);
|
|
570
662
|
}) }),
|
|
571
|
-
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text5, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) })
|
|
663
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text5, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) }),
|
|
664
|
+
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" }) })
|
|
572
665
|
] });
|
|
573
666
|
}
|
|
574
667
|
|
|
@@ -585,8 +678,8 @@ function listGames() {
|
|
|
585
678
|
}
|
|
586
679
|
|
|
587
680
|
// src/game/brick-breaker/index.tsx
|
|
588
|
-
import { Box as Box5, Text as Text6, useInput as
|
|
589
|
-
import { useState as
|
|
681
|
+
import { Box as Box5, Text as Text6, useInput as useInput3, render as render2 } from "ink";
|
|
682
|
+
import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback4 } from "react";
|
|
590
683
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
591
684
|
var GAME_WIDTH = 40;
|
|
592
685
|
var GAME_HEIGHT = 18;
|
|
@@ -718,13 +811,13 @@ function buildBoard(state) {
|
|
|
718
811
|
return lines.map((l) => `\u2502${l}\u2502`).join("\n");
|
|
719
812
|
}
|
|
720
813
|
function BrickBreakerGame({ onExit: _onExit }) {
|
|
721
|
-
const [initialLevel, setInitialLevel] =
|
|
722
|
-
const [selectingLevel, setSelectingLevel] =
|
|
723
|
-
const stateRef =
|
|
724
|
-
const [tick, setTick] =
|
|
725
|
-
const onExitRef =
|
|
814
|
+
const [initialLevel, setInitialLevel] = useState5(1);
|
|
815
|
+
const [selectingLevel, setSelectingLevel] = useState5(true);
|
|
816
|
+
const stateRef = useRef2(createInitialState(initialLevel));
|
|
817
|
+
const [tick, setTick] = useState5(0);
|
|
818
|
+
const onExitRef = useRef2(_onExit);
|
|
726
819
|
onExitRef.current = _onExit;
|
|
727
|
-
|
|
820
|
+
useEffect4(() => {
|
|
728
821
|
if (selectingLevel) return;
|
|
729
822
|
const interval = setInterval(() => {
|
|
730
823
|
update(stateRef.current);
|
|
@@ -732,18 +825,18 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
732
825
|
}, 80);
|
|
733
826
|
return () => clearInterval(interval);
|
|
734
827
|
}, [selectingLevel]);
|
|
735
|
-
const restart =
|
|
828
|
+
const restart = useCallback4((level) => {
|
|
736
829
|
const lv = level ?? stateRef.current.level;
|
|
737
830
|
stateRef.current = createInitialState(lv);
|
|
738
831
|
setInitialLevel(lv);
|
|
739
832
|
setSelectingLevel(false);
|
|
740
833
|
setTick(0);
|
|
741
834
|
}, []);
|
|
742
|
-
const startLevelSelect =
|
|
835
|
+
const startLevelSelect = useCallback4(() => {
|
|
743
836
|
setSelectingLevel(true);
|
|
744
837
|
}, []);
|
|
745
|
-
|
|
746
|
-
|
|
838
|
+
useInput3(
|
|
839
|
+
useCallback4((input, key) => {
|
|
747
840
|
const s2 = stateRef.current;
|
|
748
841
|
if (selectingLevel) {
|
|
749
842
|
if (input >= "1" && input <= "9") {
|
|
@@ -857,8 +950,8 @@ var brick_breaker_default = {
|
|
|
857
950
|
};
|
|
858
951
|
|
|
859
952
|
// src/game/coder-check/index.tsx
|
|
860
|
-
import { Box as Box6, Text as Text7, useInput as
|
|
861
|
-
import { useState as
|
|
953
|
+
import { Box as Box6, Text as Text7, useInput as useInput4, render as render3 } from "ink";
|
|
954
|
+
import { useState as useState6, useEffect as useEffect5, useRef as useRef3, useCallback as useCallback5 } from "react";
|
|
862
955
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
863
956
|
var GAME_W = 66;
|
|
864
957
|
var GAME_H = 20;
|
|
@@ -1250,26 +1343,26 @@ function buildGameView(s, scoreLines, scoreColor, message) {
|
|
|
1250
1343
|
return rows;
|
|
1251
1344
|
}
|
|
1252
1345
|
function CoderCheck({ onExit: _onExit }) {
|
|
1253
|
-
const stateRef =
|
|
1254
|
-
const [tick, setTick] =
|
|
1255
|
-
const [colorOffset, setColorOffset] =
|
|
1256
|
-
const onExitRef =
|
|
1346
|
+
const stateRef = useRef3(createInitialState2());
|
|
1347
|
+
const [tick, setTick] = useState6(0);
|
|
1348
|
+
const [colorOffset, setColorOffset] = useState6(0);
|
|
1349
|
+
const onExitRef = useRef3(_onExit);
|
|
1257
1350
|
onExitRef.current = _onExit;
|
|
1258
|
-
|
|
1351
|
+
useEffect5(() => {
|
|
1259
1352
|
const timer = setInterval(() => {
|
|
1260
1353
|
setColorOffset((prev) => (prev + 1) % CYBER_PALETTE2.length);
|
|
1261
1354
|
}, 400);
|
|
1262
1355
|
return () => clearInterval(timer);
|
|
1263
1356
|
}, []);
|
|
1264
|
-
|
|
1357
|
+
useEffect5(() => {
|
|
1265
1358
|
const interval = setInterval(() => {
|
|
1266
1359
|
update2(stateRef.current);
|
|
1267
1360
|
setTick((t) => t + 1);
|
|
1268
1361
|
}, 60);
|
|
1269
1362
|
return () => clearInterval(interval);
|
|
1270
1363
|
}, []);
|
|
1271
|
-
|
|
1272
|
-
|
|
1364
|
+
useInput4(
|
|
1365
|
+
useCallback5((input, key) => {
|
|
1273
1366
|
const s2 = stateRef.current;
|
|
1274
1367
|
if (s2.gameOver) {
|
|
1275
1368
|
if (input === "r") {
|
|
@@ -1398,25 +1491,24 @@ import { render as render4 } from "ink";
|
|
|
1398
1491
|
import chalk3 from "chalk";
|
|
1399
1492
|
|
|
1400
1493
|
// src/stock/StockList.tsx
|
|
1401
|
-
import { Box as Box7, Text as Text8, useInput as
|
|
1402
|
-
import { useState as
|
|
1494
|
+
import { Box as Box7, Text as Text8, useInput as useInput5 } from "ink";
|
|
1495
|
+
import { useState as useState7, useCallback as useCallback6, useEffect as useEffect6 } from "react";
|
|
1403
1496
|
import asciichart from "asciichart";
|
|
1404
1497
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1405
1498
|
var MINUTE_API = "https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1";
|
|
1406
|
-
function
|
|
1407
|
-
if (
|
|
1408
|
-
if (/^60
|
|
1409
|
-
if (/^00
|
|
1410
|
-
if (/^51/.test(code)) return "sh" + code;
|
|
1499
|
+
function normalizeApiCode(code) {
|
|
1500
|
+
if (code.startsWith("sh") || code.startsWith("sz")) return code;
|
|
1501
|
+
if (/^60|^68|^51/.test(code)) return "sh" + code;
|
|
1502
|
+
if (/^00|^30|^39/.test(code)) return "sz" + code;
|
|
1411
1503
|
return "sh" + code;
|
|
1412
1504
|
}
|
|
1413
1505
|
async function fetchStockMinute(code) {
|
|
1414
|
-
const url = MINUTE_API.replace("{code}",
|
|
1506
|
+
const url = MINUTE_API.replace("{code}", normalizeApiCode(code));
|
|
1415
1507
|
try {
|
|
1416
1508
|
const resp = await fetch(url);
|
|
1417
1509
|
const json = await resp.json();
|
|
1418
1510
|
if (json.code !== 0) return null;
|
|
1419
|
-
const stockKey =
|
|
1511
|
+
const stockKey = normalizeApiCode(code);
|
|
1420
1512
|
const stockData = json.data?.[stockKey];
|
|
1421
1513
|
if (!stockData) return null;
|
|
1422
1514
|
const rawMinutes = stockData.data?.data ?? [];
|
|
@@ -1440,7 +1532,9 @@ async function fetchStockMinute(code) {
|
|
|
1440
1532
|
changeAmount: parseFloat(qt[31] ?? "0"),
|
|
1441
1533
|
high: parseFloat(qt[33] ?? "0"),
|
|
1442
1534
|
low: parseFloat(qt[34] ?? "0"),
|
|
1443
|
-
volume: parseInt(qt[6] ?? "0", 10)
|
|
1535
|
+
volume: parseInt(qt[6] ?? "0", 10),
|
|
1536
|
+
// 成交额(单位:万元)→ 转为元
|
|
1537
|
+
amount: parseFloat(qt[37] ?? "0") * 1e4
|
|
1444
1538
|
};
|
|
1445
1539
|
}
|
|
1446
1540
|
return {
|
|
@@ -1457,9 +1551,11 @@ function cacheMinute(code, prices) {
|
|
|
1457
1551
|
minuteCache.set(code, prices);
|
|
1458
1552
|
}
|
|
1459
1553
|
var FALLBACK_STOCKS = [
|
|
1460
|
-
|
|
1461
|
-
{ code: "
|
|
1462
|
-
|
|
1554
|
+
// 上证指数成交额约 1.56 万亿
|
|
1555
|
+
{ code: "000001", name: "\u4E0A\u8BC1\u6307\u6570", price: 3150, changePercent: 0.35, changeAmount: 11.02, high: 3160, low: 3140, volume: 28543e4, amount: 156e10 },
|
|
1556
|
+
// 紫金矿业成交额约 108.5 亿
|
|
1557
|
+
{ code: "601899", name: "\u7D2B\u91D1\u77FF\u4E1A", price: 18.2, changePercent: 1.85, changeAmount: 0.33, high: 18.45, low: 17.9, volume: 59615384, amount: 1085e7 },
|
|
1558
|
+
{ code: "399006", name: "\u521B\u4E1A\u677F\u6307", price: 1820, changePercent: -0.52, changeAmount: -9.5, high: 1835, low: 1815, volume: 9865e4, amount: 1824e7 }
|
|
1463
1559
|
];
|
|
1464
1560
|
async function fetchStocks(codes) {
|
|
1465
1561
|
const results = await Promise.all(
|
|
@@ -1479,25 +1575,37 @@ async function fetchStocks(codes) {
|
|
|
1479
1575
|
function formatPrice(p) {
|
|
1480
1576
|
return p >= 100 ? p.toFixed(2) : p.toFixed(3);
|
|
1481
1577
|
}
|
|
1482
|
-
function
|
|
1483
|
-
if (
|
|
1484
|
-
return
|
|
1578
|
+
function formatAmount(yuan) {
|
|
1579
|
+
if (yuan >= 1e12) return (yuan / 1e12).toFixed(2) + "\u4E07\u4EBF";
|
|
1580
|
+
if (yuan >= 1e8) return (yuan / 1e8).toFixed(2) + "\u4EBF";
|
|
1581
|
+
if (yuan >= 1e4) return (yuan / 1e4).toFixed(1) + "\u4E07";
|
|
1582
|
+
return yuan.toLocaleString();
|
|
1485
1583
|
}
|
|
1486
1584
|
function latestPoints(data, maxPoints = 60) {
|
|
1487
1585
|
if (data.length <= maxPoints) return data;
|
|
1488
1586
|
return data.slice(data.length - maxPoints);
|
|
1489
1587
|
}
|
|
1490
|
-
function StockList({ codes, onExit }) {
|
|
1491
|
-
const [stocks, setStocks] =
|
|
1492
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1493
|
-
const [loading, setLoading] =
|
|
1494
|
-
const [lastUpdate, setLastUpdate] =
|
|
1495
|
-
const [detailView, setDetailView] =
|
|
1496
|
-
const [detailPrices, setDetailPrices] =
|
|
1497
|
-
const [detailLoading, setDetailLoading] =
|
|
1498
|
-
const [detailCountdown, setDetailCountdown] =
|
|
1499
|
-
const [countdown, setCountdown] =
|
|
1500
|
-
const
|
|
1588
|
+
function StockList({ codes, onExit, onBackToChat }) {
|
|
1589
|
+
const [stocks, setStocks] = useState7([]);
|
|
1590
|
+
const [selectedIndex, setSelectedIndex] = useState7(0);
|
|
1591
|
+
const [loading, setLoading] = useState7(true);
|
|
1592
|
+
const [lastUpdate, setLastUpdate] = useState7("");
|
|
1593
|
+
const [detailView, setDetailView] = useState7(null);
|
|
1594
|
+
const [detailPrices, setDetailPrices] = useState7(null);
|
|
1595
|
+
const [detailLoading, setDetailLoading] = useState7(false);
|
|
1596
|
+
const [detailCountdown, setDetailCountdown] = useState7(10);
|
|
1597
|
+
const [countdown, setCountdown] = useState7(5);
|
|
1598
|
+
const [currentTime, setCurrentTime] = useState7(
|
|
1599
|
+
() => (/* @__PURE__ */ new Date()).toLocaleTimeString("zh-CN", { hour12: false })
|
|
1600
|
+
);
|
|
1601
|
+
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(onExit);
|
|
1602
|
+
useEffect6(() => {
|
|
1603
|
+
const timer = setInterval(() => {
|
|
1604
|
+
setCurrentTime((/* @__PURE__ */ new Date()).toLocaleTimeString("zh-CN", { hour12: false }));
|
|
1605
|
+
}, 1e3);
|
|
1606
|
+
return () => clearInterval(timer);
|
|
1607
|
+
}, []);
|
|
1608
|
+
const loadData = useCallback6(async () => {
|
|
1501
1609
|
setLoading(true);
|
|
1502
1610
|
try {
|
|
1503
1611
|
const data = await fetchStocks(codes ?? []);
|
|
@@ -1507,10 +1615,10 @@ function StockList({ codes, onExit }) {
|
|
|
1507
1615
|
}
|
|
1508
1616
|
setLoading(false);
|
|
1509
1617
|
}, [codes]);
|
|
1510
|
-
|
|
1618
|
+
useEffect6(() => {
|
|
1511
1619
|
loadData();
|
|
1512
1620
|
}, [loadData]);
|
|
1513
|
-
|
|
1621
|
+
useEffect6(() => {
|
|
1514
1622
|
const interval = setInterval(() => {
|
|
1515
1623
|
setCountdown((prev) => {
|
|
1516
1624
|
if (prev <= 1) {
|
|
@@ -1522,7 +1630,7 @@ function StockList({ codes, onExit }) {
|
|
|
1522
1630
|
}, 1e3);
|
|
1523
1631
|
return () => clearInterval(interval);
|
|
1524
1632
|
}, [loadData]);
|
|
1525
|
-
|
|
1633
|
+
useEffect6(() => {
|
|
1526
1634
|
if (!detailView) {
|
|
1527
1635
|
setDetailPrices(null);
|
|
1528
1636
|
setDetailLoading(false);
|
|
@@ -1542,16 +1650,20 @@ function StockList({ codes, onExit }) {
|
|
|
1542
1650
|
const timer = setInterval(loadDetail, 1e4);
|
|
1543
1651
|
return () => clearInterval(timer);
|
|
1544
1652
|
}, [detailView]);
|
|
1545
|
-
|
|
1653
|
+
useEffect6(() => {
|
|
1546
1654
|
if (!detailView) return;
|
|
1547
1655
|
const timer = setInterval(() => {
|
|
1548
1656
|
setDetailCountdown((prev) => prev > 0 ? prev - 1 : 10);
|
|
1549
1657
|
}, 1e3);
|
|
1550
1658
|
return () => clearInterval(timer);
|
|
1551
1659
|
}, [detailView]);
|
|
1552
|
-
|
|
1553
|
-
|
|
1660
|
+
useInput5(
|
|
1661
|
+
useCallback6(
|
|
1554
1662
|
(input, key) => {
|
|
1663
|
+
if (input === "c" && key.ctrl) {
|
|
1664
|
+
handleCtrlC();
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1555
1667
|
if (detailView) {
|
|
1556
1668
|
if (key.escape || input === "q" || input === " ") {
|
|
1557
1669
|
setDetailView(null);
|
|
@@ -1567,24 +1679,29 @@ function StockList({ codes, onExit }) {
|
|
|
1567
1679
|
const stock = stocks[selectedIndex];
|
|
1568
1680
|
if (stock) setDetailView(stock);
|
|
1569
1681
|
} else if (key.escape || input === "q") {
|
|
1570
|
-
|
|
1682
|
+
if (onBackToChat) onBackToChat();
|
|
1683
|
+
else onExit();
|
|
1571
1684
|
} else if (input === "r") {
|
|
1572
1685
|
setCountdown(5);
|
|
1573
1686
|
loadData();
|
|
1574
1687
|
}
|
|
1575
1688
|
},
|
|
1576
|
-
[stocks, selectedIndex, detailView, onExit, loadData]
|
|
1689
|
+
[stocks, selectedIndex, detailView, onExit, onBackToChat, loadData, handleCtrlC]
|
|
1577
1690
|
)
|
|
1578
1691
|
);
|
|
1579
1692
|
if (detailView) {
|
|
1580
1693
|
if (detailLoading) {
|
|
1581
1694
|
return /* @__PURE__ */ jsx7(Box7, { paddingLeft: 1, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
|
|
1582
1695
|
}
|
|
1583
|
-
return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown);
|
|
1696
|
+
return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown, currentTime);
|
|
1584
1697
|
}
|
|
1585
1698
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1586
1699
|
/* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
1587
1700
|
/* @__PURE__ */ jsx7(Text8, { bold: true, color: "#00ffff", children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
|
|
1701
|
+
/* @__PURE__ */ jsxs7(Text8, { dimColor: true, children: [
|
|
1702
|
+
" \u{1F550} ",
|
|
1703
|
+
currentTime
|
|
1704
|
+
] }),
|
|
1588
1705
|
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` \u6BCF ${countdown}s \u81EA\u52A8\u5237\u65B0` })
|
|
1589
1706
|
] }),
|
|
1590
1707
|
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
@@ -1596,7 +1713,7 @@ function StockList({ codes, onExit }) {
|
|
|
1596
1713
|
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
|
|
1597
1714
|
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6700\u9AD8" }) }),
|
|
1598
1715
|
/* @__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\
|
|
1716
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "\u6210\u4EA4\u989D" }) })
|
|
1600
1717
|
] }),
|
|
1601
1718
|
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
|
|
1602
1719
|
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: stocks.map((stock, index) => {
|
|
@@ -1618,15 +1735,16 @@ function StockList({ codes, onExit }) {
|
|
|
1618
1735
|
stock.changeAmount.toFixed(3)
|
|
1619
1736
|
] }) }),
|
|
1620
1737
|
/* @__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: "#
|
|
1622
|
-
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { color: "#888888", children:
|
|
1738
|
+
/* @__PURE__ */ jsx7(Box7, { width: 12, children: /* @__PURE__ */ jsx7(Text8, { color: "#888888", children: formatPrice(stock.low) }) }),
|
|
1739
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { color: "#888888", children: formatAmount(stock.amount) }) })
|
|
1623
1740
|
] }, stock.code);
|
|
1624
1741
|
}) }),
|
|
1625
1742
|
/* @__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}` }) })
|
|
1743
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ~/.dskcode/settings.json` }) }),
|
|
1744
|
+
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" }) })
|
|
1627
1745
|
] });
|
|
1628
1746
|
}
|
|
1629
|
-
function renderDetail(stock, _onBack, prices, countdown = 10) {
|
|
1747
|
+
function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
1630
1748
|
const isUp = stock.changePercent >= 0;
|
|
1631
1749
|
const colorCode = isUp ? "#ff1493" : "#00ff41";
|
|
1632
1750
|
const arrow = isUp ? "\u25B2" : "\u25BC";
|
|
@@ -1649,7 +1767,11 @@ function renderDetail(stock, _onBack, prices, countdown = 10) {
|
|
|
1649
1767
|
stock.name,
|
|
1650
1768
|
" "
|
|
1651
1769
|
] }),
|
|
1652
|
-
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: stock.code })
|
|
1770
|
+
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: stock.code }),
|
|
1771
|
+
currentTime && /* @__PURE__ */ jsxs7(Text8, { dimColor: true, children: [
|
|
1772
|
+
" \u{1F550} ",
|
|
1773
|
+
currentTime
|
|
1774
|
+
] })
|
|
1653
1775
|
] }),
|
|
1654
1776
|
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: `\u6BCF ${countdown}s \u5237\u65B0` })
|
|
1655
1777
|
] }),
|
|
@@ -1704,18 +1826,64 @@ function createCli() {
|
|
|
1704
1826
|
const result = await loadAndValidate();
|
|
1705
1827
|
ctx = { ...ctx, config: result.config };
|
|
1706
1828
|
}
|
|
1707
|
-
|
|
1829
|
+
startChat(ctx);
|
|
1830
|
+
});
|
|
1831
|
+
function startChat(ctx) {
|
|
1832
|
+
const chatApp = renderApp(
|
|
1708
1833
|
/* @__PURE__ */ jsx8(
|
|
1709
1834
|
ChatSession,
|
|
1710
1835
|
{
|
|
1711
1836
|
providerCount: ctx?.config.providers.length ?? 1,
|
|
1712
1837
|
toolCount: ctx?.config.tools.length ?? 0,
|
|
1713
|
-
verbose: ctx?.verbose ?? false
|
|
1838
|
+
verbose: ctx?.verbose ?? false,
|
|
1839
|
+
onLaunchGame: () => {
|
|
1840
|
+
chatApp.unmount();
|
|
1841
|
+
setImmediate(() => {
|
|
1842
|
+
initGames();
|
|
1843
|
+
const games = listGames();
|
|
1844
|
+
const { unmount } = render4(
|
|
1845
|
+
/* @__PURE__ */ jsx8(
|
|
1846
|
+
GamePicker,
|
|
1847
|
+
{
|
|
1848
|
+
games,
|
|
1849
|
+
onSelect: async (game) => {
|
|
1850
|
+
unmount();
|
|
1851
|
+
await game.play();
|
|
1852
|
+
startChat(ctx);
|
|
1853
|
+
},
|
|
1854
|
+
onBackToChat: () => {
|
|
1855
|
+
unmount();
|
|
1856
|
+
setImmediate(() => startChat(ctx));
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
),
|
|
1860
|
+
{ exitOnCtrlC: false }
|
|
1861
|
+
);
|
|
1862
|
+
});
|
|
1863
|
+
},
|
|
1864
|
+
onLaunchStock: () => {
|
|
1865
|
+
chatApp.unmount();
|
|
1866
|
+
setImmediate(() => {
|
|
1867
|
+
const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
1868
|
+
const stockApp = renderApp(
|
|
1869
|
+
/* @__PURE__ */ jsx8(
|
|
1870
|
+
StockList,
|
|
1871
|
+
{
|
|
1872
|
+
codes: defaultStockCodes,
|
|
1873
|
+
onBackToChat: () => {
|
|
1874
|
+
stockApp.unmount();
|
|
1875
|
+
setImmediate(() => startChat(ctx));
|
|
1876
|
+
},
|
|
1877
|
+
onExit: () => process.exit(0)
|
|
1878
|
+
}
|
|
1879
|
+
)
|
|
1880
|
+
);
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1714
1883
|
}
|
|
1715
1884
|
)
|
|
1716
1885
|
);
|
|
1717
|
-
|
|
1718
|
-
});
|
|
1886
|
+
}
|
|
1719
1887
|
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) {
|
|
1720
1888
|
console.log("dskcode run \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C07\u7AE0\uFF09");
|
|
1721
1889
|
});
|
|
@@ -1760,7 +1928,30 @@ compdef _dskcode_completion dskcode`);
|
|
|
1760
1928
|
}
|
|
1761
1929
|
});
|
|
1762
1930
|
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
|
|
1931
|
+
const ctx = this.dskcodeCtx;
|
|
1932
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
1933
|
+
const globalConfigPath = join2(home, ".dskcode", "settings.json");
|
|
1934
|
+
let globalConfigHasStock = false;
|
|
1935
|
+
try {
|
|
1936
|
+
const raw = await readFile2(globalConfigPath, "utf-8");
|
|
1937
|
+
const parsed = JSON.parse(raw);
|
|
1938
|
+
const stock = parsed.stock;
|
|
1939
|
+
globalConfigHasStock = Array.isArray(stock?.symbols) && stock.symbols.length > 0;
|
|
1940
|
+
} catch {
|
|
1941
|
+
}
|
|
1942
|
+
if (!globalConfigHasStock) {
|
|
1943
|
+
const defaultSymbols = [
|
|
1944
|
+
{ code: "sh000001" },
|
|
1945
|
+
{ code: "sz399300" },
|
|
1946
|
+
{ code: "sh601899" }
|
|
1947
|
+
];
|
|
1948
|
+
const savedPath = await saveStockConfig(defaultSymbols);
|
|
1949
|
+
console.log(`${chalk3.green("\u2714")} \u5DF2\u751F\u6210\u81EA\u9009\u80A1\u914D\u7F6E: ${chalk3.dim(savedPath)}`);
|
|
1950
|
+
console.log(`${chalk3.dim(" \u63D0\u793A: \u53EF\u7F16\u8F91\u4E0A\u8FF0\u6587\u4EF6\u81EA\u5B9A\u4E49\u81EA\u9009\u80A1\u5217\u8868")}
|
|
1951
|
+
`);
|
|
1952
|
+
}
|
|
1953
|
+
const freshResult = await loadAndValidate();
|
|
1954
|
+
const codeList = codes && codes.length > 0 ? codes : freshResult.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
1764
1955
|
const app = renderApp(
|
|
1765
1956
|
/* @__PURE__ */ jsx8(
|
|
1766
1957
|
StockList,
|
|
@@ -1804,7 +1995,8 @@ compdef _dskcode_completion dskcode`);
|
|
|
1804
1995
|
resolve(null);
|
|
1805
1996
|
}
|
|
1806
1997
|
}
|
|
1807
|
-
)
|
|
1998
|
+
),
|
|
1999
|
+
{ exitOnCtrlC: false }
|
|
1808
2000
|
);
|
|
1809
2001
|
});
|
|
1810
2002
|
if (selectedGame) {
|
|
@@ -1831,8 +2023,18 @@ var ExitCode = {
|
|
|
1831
2023
|
};
|
|
1832
2024
|
|
|
1833
2025
|
// src/index.ts
|
|
2026
|
+
var sigintCount = 0;
|
|
2027
|
+
var sigintTimer = null;
|
|
1834
2028
|
process.on("SIGINT", () => {
|
|
1835
|
-
|
|
2029
|
+
sigintCount++;
|
|
2030
|
+
if (sigintCount >= 2) {
|
|
2031
|
+
process.exit(ExitCode.SIGINT);
|
|
2032
|
+
}
|
|
2033
|
+
process.stdout.write("\n \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode\n");
|
|
2034
|
+
if (sigintTimer) clearTimeout(sigintTimer);
|
|
2035
|
+
sigintTimer = setTimeout(() => {
|
|
2036
|
+
sigintCount = 0;
|
|
2037
|
+
}, 1500);
|
|
1836
2038
|
});
|
|
1837
2039
|
var program = createCli();
|
|
1838
2040
|
try {
|