dskcode 0.1.6 → 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 +1 -1
- package/dist/index.js +289 -152
- 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",
|
|
@@ -339,36 +351,32 @@ function hasApiKey(providers) {
|
|
|
339
351
|
if (process.env.DEEPSEEK_API_KEY) return true;
|
|
340
352
|
return false;
|
|
341
353
|
}
|
|
354
|
+
var MIN_API_KEY_LENGTH = 10;
|
|
342
355
|
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
|
-
);
|
|
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"));
|
|
358
361
|
const rl = createInterface({
|
|
359
362
|
input: process.stdin,
|
|
360
363
|
output: process.stdout
|
|
361
364
|
});
|
|
362
365
|
return new Promise((resolve) => {
|
|
366
|
+
let resolved = false;
|
|
363
367
|
const cleanup = () => {
|
|
368
|
+
if (resolved) return;
|
|
369
|
+
resolved = true;
|
|
370
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
364
371
|
rl.close();
|
|
365
372
|
};
|
|
366
|
-
|
|
373
|
+
const onKeypress = (_, key) => {
|
|
367
374
|
if (key.ctrl && key.name === "c") {
|
|
368
375
|
cleanup();
|
|
369
376
|
resolve(null);
|
|
370
377
|
}
|
|
371
|
-
}
|
|
378
|
+
};
|
|
379
|
+
process.stdin.on("keypress", onKeypress);
|
|
372
380
|
rl.question(
|
|
373
381
|
` ${chalk2.cyan("\u{1F511}")} ${chalk2.bold("\u8BF7\u8F93\u5165\u4F60\u7684 DeepSeek API Key:")} `,
|
|
374
382
|
(answer) => {
|
|
@@ -379,7 +387,7 @@ async function promptForApiKey() {
|
|
|
379
387
|
resolve(null);
|
|
380
388
|
return;
|
|
381
389
|
}
|
|
382
|
-
if (trimmed.length <
|
|
390
|
+
if (trimmed.length < MIN_API_KEY_LENGTH) {
|
|
383
391
|
console.log(chalk2.red(" \u2716 API Key \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u957F\u5EA6\u81F3\u5C11 10 \u4F4D"));
|
|
384
392
|
resolve(null);
|
|
385
393
|
return;
|
|
@@ -393,7 +401,7 @@ async function promptForApiKey() {
|
|
|
393
401
|
// src/ui/RenderScope.tsx
|
|
394
402
|
import { render } from "ink";
|
|
395
403
|
function renderApp(node) {
|
|
396
|
-
const { waitUntilExit, clear, unmount } = render(node);
|
|
404
|
+
const { waitUntilExit, clear, unmount } = render(node, { exitOnCtrlC: false });
|
|
397
405
|
return { waitUntilExit: waitUntilExit(), clear, unmount };
|
|
398
406
|
}
|
|
399
407
|
|
|
@@ -410,12 +418,6 @@ import { jsxs as jsxs2 } from "react/jsx-runtime";
|
|
|
410
418
|
import { Box as Box2, Text as Text3 } from "ink";
|
|
411
419
|
import { useEffect, useState } from "react";
|
|
412
420
|
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
421
|
var CYBER_PALETTE = ["#00ffff", "#ff00ff", "#00ff41", "#ff1493", "#8b00ff"];
|
|
420
422
|
var LOGO_LINES = [
|
|
421
423
|
" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
@@ -425,57 +427,119 @@ var LOGO_LINES = [
|
|
|
425
427
|
" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557",
|
|
426
428
|
" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"
|
|
427
429
|
];
|
|
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("");
|
|
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;
|
|
448
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(() => {
|
|
449
506
|
const timer = setInterval(() => {
|
|
450
507
|
setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);
|
|
451
508
|
}, 500);
|
|
452
509
|
return () => clearInterval(timer);
|
|
453
510
|
}, []);
|
|
454
|
-
const handleSubmit =
|
|
511
|
+
const handleSubmit = useCallback2((value) => {
|
|
455
512
|
const trimmed = value.trim();
|
|
456
513
|
if (!trimmed) return;
|
|
457
514
|
if (trimmed.startsWith("/")) {
|
|
458
|
-
const cmd =
|
|
515
|
+
const cmd = commandRegistry.get(trimmed.toLowerCase());
|
|
459
516
|
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
517
|
const result = cmd.handler();
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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;
|
|
476
542
|
}
|
|
477
|
-
setInput("");
|
|
478
|
-
return;
|
|
479
543
|
}
|
|
480
544
|
setMessages((prev) => [
|
|
481
545
|
...prev,
|
|
@@ -491,7 +555,7 @@ function ChatSession({ providerCount, toolCount, verbose }) {
|
|
|
491
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" }
|
|
492
556
|
]);
|
|
493
557
|
setInput("");
|
|
494
|
-
}, []);
|
|
558
|
+
}, [onLaunchGame, onLaunchStock]);
|
|
495
559
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
|
|
496
560
|
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", marginBottom: 1, children: [
|
|
497
561
|
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
|
|
@@ -530,19 +594,26 @@ function ChatSession({ providerCount, toolCount, verbose }) {
|
|
|
530
594
|
}
|
|
531
595
|
) })
|
|
532
596
|
] }),
|
|
533
|
-
/* @__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" }) })
|
|
534
599
|
] });
|
|
535
600
|
}
|
|
536
601
|
|
|
537
602
|
// src/ui/GamePicker.tsx
|
|
538
|
-
import { Box as Box4, Text as Text5, useInput } from "ink";
|
|
539
|
-
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";
|
|
540
605
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
541
|
-
function GamePicker({ games, onSelect, onExit }) {
|
|
542
|
-
const [selectedIndex, setSelectedIndex] =
|
|
543
|
-
|
|
544
|
-
|
|
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(
|
|
545
612
|
(input, key) => {
|
|
613
|
+
if (input === "c" && key.ctrl) {
|
|
614
|
+
handleCtrlC();
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
546
617
|
if (games.length === 0) return;
|
|
547
618
|
if (key.upArrow || input === "k") {
|
|
548
619
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : games.length - 1);
|
|
@@ -552,10 +623,11 @@ function GamePicker({ games, onSelect, onExit }) {
|
|
|
552
623
|
const game = games[selectedIndex];
|
|
553
624
|
if (game) onSelect(game);
|
|
554
625
|
} else if (key.escape || input === "q") {
|
|
555
|
-
|
|
626
|
+
if (onBackToChat) onBackToChat();
|
|
627
|
+
else onExit?.();
|
|
556
628
|
}
|
|
557
629
|
},
|
|
558
|
-
[games, selectedIndex, onSelect, onExit]
|
|
630
|
+
[games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC]
|
|
559
631
|
)
|
|
560
632
|
);
|
|
561
633
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
@@ -568,7 +640,8 @@ function GamePicker({ games, onSelect, onExit }) {
|
|
|
568
640
|
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text5, { color: "#888888", children: game.description }) })
|
|
569
641
|
] }, game.id);
|
|
570
642
|
}) }),
|
|
571
|
-
/* @__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" }) })
|
|
572
645
|
] });
|
|
573
646
|
}
|
|
574
647
|
|
|
@@ -585,8 +658,8 @@ function listGames() {
|
|
|
585
658
|
}
|
|
586
659
|
|
|
587
660
|
// src/game/brick-breaker/index.tsx
|
|
588
|
-
import { Box as Box5, Text as Text6, useInput as
|
|
589
|
-
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";
|
|
590
663
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
591
664
|
var GAME_WIDTH = 40;
|
|
592
665
|
var GAME_HEIGHT = 18;
|
|
@@ -718,13 +791,13 @@ function buildBoard(state) {
|
|
|
718
791
|
return lines.map((l) => `\u2502${l}\u2502`).join("\n");
|
|
719
792
|
}
|
|
720
793
|
function BrickBreakerGame({ onExit: _onExit }) {
|
|
721
|
-
const [initialLevel, setInitialLevel] =
|
|
722
|
-
const [selectingLevel, setSelectingLevel] =
|
|
723
|
-
const stateRef =
|
|
724
|
-
const [tick, setTick] =
|
|
725
|
-
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);
|
|
726
799
|
onExitRef.current = _onExit;
|
|
727
|
-
|
|
800
|
+
useEffect4(() => {
|
|
728
801
|
if (selectingLevel) return;
|
|
729
802
|
const interval = setInterval(() => {
|
|
730
803
|
update(stateRef.current);
|
|
@@ -732,18 +805,18 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
732
805
|
}, 80);
|
|
733
806
|
return () => clearInterval(interval);
|
|
734
807
|
}, [selectingLevel]);
|
|
735
|
-
const restart =
|
|
808
|
+
const restart = useCallback4((level) => {
|
|
736
809
|
const lv = level ?? stateRef.current.level;
|
|
737
810
|
stateRef.current = createInitialState(lv);
|
|
738
811
|
setInitialLevel(lv);
|
|
739
812
|
setSelectingLevel(false);
|
|
740
813
|
setTick(0);
|
|
741
814
|
}, []);
|
|
742
|
-
const startLevelSelect =
|
|
815
|
+
const startLevelSelect = useCallback4(() => {
|
|
743
816
|
setSelectingLevel(true);
|
|
744
817
|
}, []);
|
|
745
|
-
|
|
746
|
-
|
|
818
|
+
useInput3(
|
|
819
|
+
useCallback4((input, key) => {
|
|
747
820
|
const s2 = stateRef.current;
|
|
748
821
|
if (selectingLevel) {
|
|
749
822
|
if (input >= "1" && input <= "9") {
|
|
@@ -857,8 +930,8 @@ var brick_breaker_default = {
|
|
|
857
930
|
};
|
|
858
931
|
|
|
859
932
|
// src/game/coder-check/index.tsx
|
|
860
|
-
import { Box as Box6, Text as Text7, useInput as
|
|
861
|
-
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";
|
|
862
935
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
863
936
|
var GAME_W = 66;
|
|
864
937
|
var GAME_H = 20;
|
|
@@ -1250,26 +1323,26 @@ function buildGameView(s, scoreLines, scoreColor, message) {
|
|
|
1250
1323
|
return rows;
|
|
1251
1324
|
}
|
|
1252
1325
|
function CoderCheck({ onExit: _onExit }) {
|
|
1253
|
-
const stateRef =
|
|
1254
|
-
const [tick, setTick] =
|
|
1255
|
-
const [colorOffset, setColorOffset] =
|
|
1256
|
-
const onExitRef =
|
|
1326
|
+
const stateRef = useRef3(createInitialState2());
|
|
1327
|
+
const [tick, setTick] = useState6(0);
|
|
1328
|
+
const [colorOffset, setColorOffset] = useState6(0);
|
|
1329
|
+
const onExitRef = useRef3(_onExit);
|
|
1257
1330
|
onExitRef.current = _onExit;
|
|
1258
|
-
|
|
1331
|
+
useEffect5(() => {
|
|
1259
1332
|
const timer = setInterval(() => {
|
|
1260
1333
|
setColorOffset((prev) => (prev + 1) % CYBER_PALETTE2.length);
|
|
1261
1334
|
}, 400);
|
|
1262
1335
|
return () => clearInterval(timer);
|
|
1263
1336
|
}, []);
|
|
1264
|
-
|
|
1337
|
+
useEffect5(() => {
|
|
1265
1338
|
const interval = setInterval(() => {
|
|
1266
1339
|
update2(stateRef.current);
|
|
1267
1340
|
setTick((t) => t + 1);
|
|
1268
1341
|
}, 60);
|
|
1269
1342
|
return () => clearInterval(interval);
|
|
1270
1343
|
}, []);
|
|
1271
|
-
|
|
1272
|
-
|
|
1344
|
+
useInput4(
|
|
1345
|
+
useCallback5((input, key) => {
|
|
1273
1346
|
const s2 = stateRef.current;
|
|
1274
1347
|
if (s2.gameOver) {
|
|
1275
1348
|
if (input === "r") {
|
|
@@ -1398,25 +1471,24 @@ import { render as render4 } from "ink";
|
|
|
1398
1471
|
import chalk3 from "chalk";
|
|
1399
1472
|
|
|
1400
1473
|
// src/stock/StockList.tsx
|
|
1401
|
-
import { Box as Box7, Text as Text8, useInput as
|
|
1402
|
-
import { useState as
|
|
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";
|
|
1403
1476
|
import asciichart from "asciichart";
|
|
1404
1477
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1405
1478
|
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;
|
|
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;
|
|
1411
1483
|
return "sh" + code;
|
|
1412
1484
|
}
|
|
1413
1485
|
async function fetchStockMinute(code) {
|
|
1414
|
-
const url = MINUTE_API.replace("{code}",
|
|
1486
|
+
const url = MINUTE_API.replace("{code}", normalizeApiCode(code));
|
|
1415
1487
|
try {
|
|
1416
1488
|
const resp = await fetch(url);
|
|
1417
1489
|
const json = await resp.json();
|
|
1418
1490
|
if (json.code !== 0) return null;
|
|
1419
|
-
const stockKey =
|
|
1491
|
+
const stockKey = normalizeApiCode(code);
|
|
1420
1492
|
const stockData = json.data?.[stockKey];
|
|
1421
1493
|
if (!stockData) return null;
|
|
1422
1494
|
const rawMinutes = stockData.data?.data ?? [];
|
|
@@ -1487,17 +1559,18 @@ function latestPoints(data, maxPoints = 60) {
|
|
|
1487
1559
|
if (data.length <= maxPoints) return data;
|
|
1488
1560
|
return data.slice(data.length - maxPoints);
|
|
1489
1561
|
}
|
|
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
|
|
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 () => {
|
|
1501
1574
|
setLoading(true);
|
|
1502
1575
|
try {
|
|
1503
1576
|
const data = await fetchStocks(codes ?? []);
|
|
@@ -1507,10 +1580,10 @@ function StockList({ codes, onExit }) {
|
|
|
1507
1580
|
}
|
|
1508
1581
|
setLoading(false);
|
|
1509
1582
|
}, [codes]);
|
|
1510
|
-
|
|
1583
|
+
useEffect6(() => {
|
|
1511
1584
|
loadData();
|
|
1512
1585
|
}, [loadData]);
|
|
1513
|
-
|
|
1586
|
+
useEffect6(() => {
|
|
1514
1587
|
const interval = setInterval(() => {
|
|
1515
1588
|
setCountdown((prev) => {
|
|
1516
1589
|
if (prev <= 1) {
|
|
@@ -1522,7 +1595,7 @@ function StockList({ codes, onExit }) {
|
|
|
1522
1595
|
}, 1e3);
|
|
1523
1596
|
return () => clearInterval(interval);
|
|
1524
1597
|
}, [loadData]);
|
|
1525
|
-
|
|
1598
|
+
useEffect6(() => {
|
|
1526
1599
|
if (!detailView) {
|
|
1527
1600
|
setDetailPrices(null);
|
|
1528
1601
|
setDetailLoading(false);
|
|
@@ -1542,16 +1615,20 @@ function StockList({ codes, onExit }) {
|
|
|
1542
1615
|
const timer = setInterval(loadDetail, 1e4);
|
|
1543
1616
|
return () => clearInterval(timer);
|
|
1544
1617
|
}, [detailView]);
|
|
1545
|
-
|
|
1618
|
+
useEffect6(() => {
|
|
1546
1619
|
if (!detailView) return;
|
|
1547
1620
|
const timer = setInterval(() => {
|
|
1548
1621
|
setDetailCountdown((prev) => prev > 0 ? prev - 1 : 10);
|
|
1549
1622
|
}, 1e3);
|
|
1550
1623
|
return () => clearInterval(timer);
|
|
1551
1624
|
}, [detailView]);
|
|
1552
|
-
|
|
1553
|
-
|
|
1625
|
+
useInput5(
|
|
1626
|
+
useCallback6(
|
|
1554
1627
|
(input, key) => {
|
|
1628
|
+
if (input === "c" && key.ctrl) {
|
|
1629
|
+
handleCtrlC();
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1555
1632
|
if (detailView) {
|
|
1556
1633
|
if (key.escape || input === "q" || input === " ") {
|
|
1557
1634
|
setDetailView(null);
|
|
@@ -1567,13 +1644,14 @@ function StockList({ codes, onExit }) {
|
|
|
1567
1644
|
const stock = stocks[selectedIndex];
|
|
1568
1645
|
if (stock) setDetailView(stock);
|
|
1569
1646
|
} else if (key.escape || input === "q") {
|
|
1570
|
-
|
|
1647
|
+
if (onBackToChat) onBackToChat();
|
|
1648
|
+
else onExit();
|
|
1571
1649
|
} else if (input === "r") {
|
|
1572
1650
|
setCountdown(5);
|
|
1573
1651
|
loadData();
|
|
1574
1652
|
}
|
|
1575
1653
|
},
|
|
1576
|
-
[stocks, selectedIndex, detailView, onExit, loadData]
|
|
1654
|
+
[stocks, selectedIndex, detailView, onExit, onBackToChat, loadData, handleCtrlC]
|
|
1577
1655
|
)
|
|
1578
1656
|
);
|
|
1579
1657
|
if (detailView) {
|
|
@@ -1623,7 +1701,8 @@ function StockList({ codes, onExit }) {
|
|
|
1623
1701
|
] }, stock.code);
|
|
1624
1702
|
}) }),
|
|
1625
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` }) }),
|
|
1626
|
-
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate}` }) })
|
|
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" }) })
|
|
1627
1706
|
] });
|
|
1628
1707
|
}
|
|
1629
1708
|
function renderDetail(stock, _onBack, prices, countdown = 10) {
|
|
@@ -1704,18 +1783,64 @@ function createCli() {
|
|
|
1704
1783
|
const result = await loadAndValidate();
|
|
1705
1784
|
ctx = { ...ctx, config: result.config };
|
|
1706
1785
|
}
|
|
1707
|
-
|
|
1786
|
+
startChat(ctx);
|
|
1787
|
+
});
|
|
1788
|
+
function startChat(ctx) {
|
|
1789
|
+
const chatApp = renderApp(
|
|
1708
1790
|
/* @__PURE__ */ jsx8(
|
|
1709
1791
|
ChatSession,
|
|
1710
1792
|
{
|
|
1711
1793
|
providerCount: ctx?.config.providers.length ?? 1,
|
|
1712
1794
|
toolCount: ctx?.config.tools.length ?? 0,
|
|
1713
|
-
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
|
+
}
|
|
1714
1840
|
}
|
|
1715
1841
|
)
|
|
1716
1842
|
);
|
|
1717
|
-
|
|
1718
|
-
});
|
|
1843
|
+
}
|
|
1719
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) {
|
|
1720
1845
|
console.log("dskcode run \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C07\u7AE0\uFF09");
|
|
1721
1846
|
});
|
|
@@ -1760,7 +1885,8 @@ compdef _dskcode_completion dskcode`);
|
|
|
1760
1885
|
}
|
|
1761
1886
|
});
|
|
1762
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) {
|
|
1763
|
-
const
|
|
1888
|
+
const ctx = this.dskcodeCtx;
|
|
1889
|
+
const codeList = codes && codes.length > 0 ? codes : ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
|
|
1764
1890
|
const app = renderApp(
|
|
1765
1891
|
/* @__PURE__ */ jsx8(
|
|
1766
1892
|
StockList,
|
|
@@ -1804,7 +1930,8 @@ compdef _dskcode_completion dskcode`);
|
|
|
1804
1930
|
resolve(null);
|
|
1805
1931
|
}
|
|
1806
1932
|
}
|
|
1807
|
-
)
|
|
1933
|
+
),
|
|
1934
|
+
{ exitOnCtrlC: false }
|
|
1808
1935
|
);
|
|
1809
1936
|
});
|
|
1810
1937
|
if (selectedGame) {
|
|
@@ -1831,8 +1958,18 @@ var ExitCode = {
|
|
|
1831
1958
|
};
|
|
1832
1959
|
|
|
1833
1960
|
// src/index.ts
|
|
1961
|
+
var sigintCount = 0;
|
|
1962
|
+
var sigintTimer = null;
|
|
1834
1963
|
process.on("SIGINT", () => {
|
|
1835
|
-
|
|
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);
|
|
1836
1973
|
});
|
|
1837
1974
|
var program = createCli();
|
|
1838
1975
|
try {
|