thatgfsj-code 3.0.1 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tui/app.d.ts.map +1 -1
- package/dist/tui/app.js +10 -43
- package/dist/tui/app.js.map +1 -1
- package/dist/tui/components/InitWizard.d.ts +18 -9
- package/dist/tui/components/InitWizard.d.ts.map +1 -1
- package/dist/tui/components/InitWizard.js +61 -33
- package/dist/tui/components/InitWizard.js.map +1 -1
- package/package.json +1 -1
- package/src/tui/app.tsx +8 -56
- package/src/tui/components/InitWizard.tsx +87 -40
package/dist/tui/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,KAA2C,MAAM,OAAO,CAAC;AAWhE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,KAA2C,MAAM,OAAO,CAAC;AAWhE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAM3C,UAAU,KAAK;IACb,GAAG,EAAE,GAAG,CAAC;CACV;AAkBD,wBAAgB,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,qBAiHpC"}
|
package/dist/tui/app.js
CHANGED
|
@@ -36,8 +36,6 @@ export function TuiApp({ app }) {
|
|
|
36
36
|
const { handleCommand } = useCommands(app);
|
|
37
37
|
const [systemMessages, setSystemMessages] = useState([]);
|
|
38
38
|
const [viewMode, setViewMode] = useState('chat');
|
|
39
|
-
const [initProvider, setInitProvider] = useState('siliconflow');
|
|
40
|
-
const [initUrl, setInitUrl] = useState('');
|
|
41
39
|
const { stdout } = useStdout();
|
|
42
40
|
const terminalWidth = stdout?.columns || 80;
|
|
43
41
|
// v3.0.0: cache stats snapshot for the Header. Re-read whenever messages
|
|
@@ -51,44 +49,6 @@ export function TuiApp({ app }) {
|
|
|
51
49
|
setSystemMessages(prev => [...prev, { role: 'assistant', content }]);
|
|
52
50
|
}, []);
|
|
53
51
|
const onSubmit = useCallback(async (input) => {
|
|
54
|
-
// Init wizard - API Key input
|
|
55
|
-
if (viewMode === 'init_key') {
|
|
56
|
-
const key = input.trim();
|
|
57
|
-
if (!key) {
|
|
58
|
-
setViewMode('chat');
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
// Save and complete
|
|
62
|
-
const { App: AppClass } = await import('../app/index.js');
|
|
63
|
-
const cfg = app.config.get();
|
|
64
|
-
app.config.save({ provider: initProvider, apiKey: key, model: cfg.model || 'default' });
|
|
65
|
-
saveModelToHistory(cfg.model || 'default');
|
|
66
|
-
setViewMode('chat');
|
|
67
|
-
addMsg(`配置已更新: ${initProvider}`);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
// Init wizard - Custom URL input
|
|
71
|
-
if (viewMode === 'init_url') {
|
|
72
|
-
const url = input.trim();
|
|
73
|
-
if (!url) {
|
|
74
|
-
setViewMode('chat');
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
setInitUrl(url);
|
|
78
|
-
setViewMode('init_key');
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
// Init wizard - Custom model name
|
|
82
|
-
if (viewMode === 'init_custom_model') {
|
|
83
|
-
const model = input.trim();
|
|
84
|
-
if (model) {
|
|
85
|
-
app.config.save({ model });
|
|
86
|
-
saveModelToHistory(model);
|
|
87
|
-
addMsg(`模型已切换: ${model}`);
|
|
88
|
-
}
|
|
89
|
-
setViewMode('chat');
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
52
|
// Model selector - ignore text input
|
|
93
53
|
if (viewMode === 'model_select')
|
|
94
54
|
return;
|
|
@@ -114,7 +74,7 @@ export function TuiApp({ app }) {
|
|
|
114
74
|
return;
|
|
115
75
|
}
|
|
116
76
|
sendMessage(input);
|
|
117
|
-
}, [handleCommand, sendMessage, app, viewMode,
|
|
77
|
+
}, [handleCommand, sendMessage, app, viewMode, addMsg]);
|
|
118
78
|
const allMessages = [...systemMessages, ...messages];
|
|
119
79
|
const activeSkills = app.skills.listActive().map(s => s.id);
|
|
120
80
|
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Header, { provider: app.config.get().provider, model: app.config.get().model, cacheHitRate: cacheSnapshot.hitRate > 0 ? cacheSnapshot.hitRate : null, cacheSavingsCNY: cacheSnapshot.estimatedSavingsCNY }), _jsx(ChatList, { messages: allMessages, streaming: streaming, streamingToolCalls: streamingToolCalls, width: terminalWidth - 4 }), _jsx(Thinking, { active: isThinking }), queuedMessage && (_jsxs(Box, { paddingLeft: 1, children: [_jsx(Text, { color: "#F59E0B", children: "\uD83D\uDCCE \u5DF2\u6392\u961F: " }), _jsx(Text, { color: "#94A3B8", children: queuedMessage })] })), viewMode === 'model_select' ? (_jsx(ModelSelector, { currentModel: app.config.get().model, currentProvider: app.config.get().provider, onSelect: (model) => {
|
|
@@ -122,11 +82,18 @@ export function TuiApp({ app }) {
|
|
|
122
82
|
saveModelToHistory(model);
|
|
123
83
|
setViewMode('chat');
|
|
124
84
|
addMsg(`模型已切换: ${model}`);
|
|
125
|
-
}, onAddNew: () => setViewMode('init_wizard') })) : viewMode === 'init_wizard' ? (
|
|
85
|
+
}, onAddNew: () => setViewMode('init_wizard') })) : viewMode === 'init_wizard' ? (
|
|
86
|
+
// v3.0.1: InitWizard is now fully self-contained — it owns its own
|
|
87
|
+
// input handling via useInput + ink-text-input. No external UserInput
|
|
88
|
+
// is rendered here, and app.tsx no longer has the text-entry
|
|
89
|
+
// viewModes (init_key / init_url / init_custom_model) because they
|
|
90
|
+
// were the broken input-handling path that left users stuck on a
|
|
91
|
+
// frozen "输入 API Key" screen with no way to type.
|
|
92
|
+
_jsx(InitWizard, { onComplete: (provider, model, apiKey, baseUrl) => {
|
|
126
93
|
app.config.save({ provider, model, apiKey, baseUrl });
|
|
127
94
|
saveModelToHistory(model);
|
|
128
95
|
setViewMode('chat');
|
|
129
96
|
addMsg(`配置完成: ${provider} / ${model}`);
|
|
130
|
-
}, onCancel: () => setViewMode('chat') })) : (
|
|
97
|
+
}, onCancel: () => setViewMode('chat') })) : (_jsx(Box, { flexDirection: "column", children: _jsx(UserInput, { onSubmit: onSubmit, onCancel: cancel, disabled: false }) })), _jsx(StatusBar, { messageCount: allMessages.length, skills: activeSkills })] }));
|
|
131
98
|
}
|
|
132
99
|
//# sourceMappingURL=app.js.map
|
package/dist/tui/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":";AAAA,6BAA6B;AAC7B,OAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/tui/app.tsx"],"names":[],"mappings":"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"}
|
|
@@ -6,17 +6,26 @@ interface Props {
|
|
|
6
6
|
onComplete: (provider: ProviderName, model: string, apiKey: string, baseUrl?: string) => void;
|
|
7
7
|
onCancel: () => void;
|
|
8
8
|
}
|
|
9
|
-
type InitStep = 'provider' | '
|
|
9
|
+
type InitStep = 'provider' | 'custom_url' | 'api_key' | 'model' | 'custom_model' | 'cache_strategy' | 'cache_ttl';
|
|
10
10
|
/**
|
|
11
|
-
* v3.0.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* v3.0.1: InitWizard is now fully self-contained — it manages its own
|
|
12
|
+
* step state AND its own keyboard input for text-entry steps (api_key,
|
|
13
|
+
* custom_url, custom_model). The previous version had a fatal bug:
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* The text-entry steps (`api_key`, `custom_url`, `custom_model`) only
|
|
16
|
+
* rendered a `<Text>` prompt with no actual input box. The host
|
|
17
|
+
* component (app.tsx) had its own UserInput BUT only rendered it when
|
|
18
|
+
* viewMode was NOT 'init_wizard' (see app.tsx ternary). So when the
|
|
19
|
+
* wizard's internal step advanced to 'api_key', the user could SEE the
|
|
20
|
+
* "输入 API Key: 服务商: deepseek" prompt but had NO way to type into
|
|
21
|
+
* it. Ink silently swallowed keystrokes (no useInput handler was
|
|
22
|
+
* active), and the user was stuck on a frozen screen — exactly the
|
|
23
|
+
* bug that prompted the user-reported "选择 deepseek 然后退出":
|
|
24
|
+
* pressing Ctrl+C felt like the only escape.
|
|
25
|
+
*
|
|
26
|
+
* Fix: this file now uses `useInput` and `ink-text-input` for the text
|
|
27
|
+
* steps. The SetViewMode-based input delegation in app.tsx is no longer
|
|
28
|
+
* required for the InitWizard flow.
|
|
20
29
|
*/
|
|
21
30
|
export declare function InitWizard({ onComplete, onCancel }: Props): React.JSX.Element | null;
|
|
22
31
|
export type { InitStep };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InitWizard.d.ts","sourceRoot":"","sources":["../../../src/tui/components/InitWizard.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,
|
|
1
|
+
{"version":3,"file":"InitWizard.d.ts","sourceRoot":"","sources":["../../../src/tui/components/InitWizard.tsx"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,KAAmB,MAAM,OAAO,CAAC;AAOxC,OAAO,EAAuC,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,UAAU,KAAK;IACb,UAAU,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9F,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,KAAK,QAAQ,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,cAAc,GAAG,gBAAgB,GAAG,WAAW,CAAC;AAOlH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,KAAK,4BAwNzD;AAGD,YAAY,EAAE,QAAQ,EAAE,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/** @jsxImportSource react */
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
-
import { Box, Text } from 'ink';
|
|
4
|
+
import { Box, Text, useInput } from 'ink';
|
|
5
5
|
import SelectInput from 'ink-select-input';
|
|
6
|
-
import
|
|
6
|
+
import TextInput from 'ink-text-input';
|
|
7
|
+
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
7
8
|
import { join } from 'path';
|
|
8
9
|
import { homedir } from 'os';
|
|
9
10
|
import { listProviders, getModelsForProvider, isCustomProvider } from '../../config/providers.js';
|
|
10
11
|
/**
|
|
11
|
-
* v3.0.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* v3.0.1: InitWizard is now fully self-contained — it manages its own
|
|
13
|
+
* step state AND its own keyboard input for text-entry steps (api_key,
|
|
14
|
+
* custom_url, custom_model). The previous version had a fatal bug:
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* The text-entry steps (`api_key`, `custom_url`, `custom_model`) only
|
|
17
|
+
* rendered a `<Text>` prompt with no actual input box. The host
|
|
18
|
+
* component (app.tsx) had its own UserInput BUT only rendered it when
|
|
19
|
+
* viewMode was NOT 'init_wizard' (see app.tsx ternary). So when the
|
|
20
|
+
* wizard's internal step advanced to 'api_key', the user could SEE the
|
|
21
|
+
* "输入 API Key: 服务商: deepseek" prompt but had NO way to type into
|
|
22
|
+
* it. Ink silently swallowed keystrokes (no useInput handler was
|
|
23
|
+
* active), and the user was stuck on a frozen screen — exactly the
|
|
24
|
+
* bug that prompted the user-reported "选择 deepseek 然后退出":
|
|
25
|
+
* pressing Ctrl+C felt like the only escape.
|
|
26
|
+
*
|
|
27
|
+
* Fix: this file now uses `useInput` and `ink-text-input` for the text
|
|
28
|
+
* steps. The SetViewMode-based input delegation in app.tsx is no longer
|
|
29
|
+
* required for the InitWizard flow.
|
|
20
30
|
*/
|
|
21
31
|
export function InitWizard({ onComplete, onCancel }) {
|
|
22
32
|
const [step, setStep] = useState('provider');
|
|
@@ -39,7 +49,6 @@ export function InitWizard({ onComplete, onCancel }) {
|
|
|
39
49
|
temperature: 0.7,
|
|
40
50
|
maxTokens: 4096,
|
|
41
51
|
contextLength: 50,
|
|
42
|
-
// v3.0.0: cache policy from wizard
|
|
43
52
|
cache: {
|
|
44
53
|
enabled: cacheChoice.enabled,
|
|
45
54
|
ttl: cacheChoice.ttl,
|
|
@@ -49,26 +58,23 @@ export function InitWizard({ onComplete, onCancel }) {
|
|
|
49
58
|
if (url)
|
|
50
59
|
config.baseUrl = url;
|
|
51
60
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
52
|
-
// Save to models history
|
|
53
|
-
const historyPath = join(dir, 'models.json');
|
|
54
|
-
let history = [];
|
|
55
|
-
if (existsSync(historyPath)) {
|
|
56
|
-
try {
|
|
57
|
-
history = JSON.parse(readFileSync(historyPath, 'utf-8'));
|
|
58
|
-
}
|
|
59
|
-
catch { }
|
|
60
|
-
}
|
|
61
|
-
if (!history.includes(model)) {
|
|
62
|
-
history.push(model);
|
|
63
|
-
writeFileSync(historyPath, JSON.stringify(history, null, 2));
|
|
64
|
-
}
|
|
65
61
|
};
|
|
62
|
+
// v3.0.1: ESC cancels the wizard from any text-entry step. This is the
|
|
63
|
+
// hook that the previous version was missing — when the user pressed
|
|
64
|
+
// ESC on the frozen "输入 API Key" screen, nothing happened because no
|
|
65
|
+
// useInput was active. Now it bails out cleanly.
|
|
66
|
+
useInput((_input, key) => {
|
|
67
|
+
if (key.escape && (step === 'api_key' || step === 'custom_url' || step === 'custom_model')) {
|
|
68
|
+
onCancel();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
66
71
|
// Step 1: Provider selection
|
|
67
72
|
if (step === 'provider') {
|
|
68
73
|
const items = providers.map(p => ({ label: p.name, value: p.key }));
|
|
69
74
|
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsx(Text, { color: "#06B6D4", bold: true, children: "\u9009\u62E9\u670D\u52A1\u5546 (\u2191\u2193 \u56DE\u8F66):" }), _jsx(SelectInput, { items: items, onSelect: (item) => {
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
const provider = item.value;
|
|
76
|
+
setSelectedProvider(provider);
|
|
77
|
+
if (isCustomProvider(provider)) {
|
|
72
78
|
setStep('custom_url');
|
|
73
79
|
}
|
|
74
80
|
else {
|
|
@@ -76,13 +82,31 @@ export function InitWizard({ onComplete, onCancel }) {
|
|
|
76
82
|
}
|
|
77
83
|
} })] }));
|
|
78
84
|
}
|
|
79
|
-
// Custom URL input
|
|
85
|
+
// Custom URL input — v3.0.1 NOW has a real TextInput
|
|
80
86
|
if (step === 'custom_url') {
|
|
81
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsx(Text, { color: "#06B6D4", bold: true, children: "\u8F93\u5165\u4E2D\u8F6C\u7AD9 URL:" }), _jsx(Text, { dimColor: true, children: "\u4F8B\u5982: https://api.example.com/v1" }), _jsx(Text, { color: "#F59E0B", children: "\uFF08\u8BF7\u76F4\u63A5\u8F93\u5165 URL \u5E76\u56DE\u8F66\uFF09" })
|
|
87
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsx(Text, { color: "#06B6D4", bold: true, children: "\u8F93\u5165\u4E2D\u8F6C\u7AD9 URL:" }), _jsx(Text, { dimColor: true, children: "\u4F8B\u5982: https://api.example.com/v1" }), _jsx(Text, { color: "#F59E0B", children: "\uFF08\u8BF7\u76F4\u63A5\u8F93\u5165 URL \u5E76\u56DE\u8F66\uFF0CESC \u53D6\u6D88\uFF09" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "#06B6D4", children: "\u276F " }), _jsx(TextInput, { value: customUrl, onChange: setCustomUrl, onSubmit: (value) => {
|
|
88
|
+
if (value.trim()) {
|
|
89
|
+
setCustomUrl(value.trim());
|
|
90
|
+
setStep('api_key');
|
|
91
|
+
}
|
|
92
|
+
} })] })] }));
|
|
82
93
|
}
|
|
83
|
-
// Step 2: API Key
|
|
94
|
+
// Step 2: API Key — v3.0.1 NOW has a real TextInput
|
|
84
95
|
if (step === 'api_key') {
|
|
85
|
-
|
|
96
|
+
const providerLabel = providers.find(p => p.key === selectedProvider)?.name || selectedProvider;
|
|
97
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsx(Text, { color: "#06B6D4", bold: true, children: "\u8F93\u5165 API Key:" }), _jsxs(Text, { dimColor: true, children: ["\u670D\u52A1\u5546: ", providerLabel] }), _jsx(Text, { color: "#F59E0B", children: "\uFF08\u8BF7\u76F4\u63A5\u8F93\u5165 Key \u5E76\u56DE\u8F66\uFF0CESC \u53D6\u6D88\uFF09" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "#06B6D4", children: "\u276F " }), _jsx(TextInput, { value: apiKey, onChange: setApiKey, onSubmit: (value) => {
|
|
98
|
+
if (value.trim()) {
|
|
99
|
+
setApiKey(value.trim());
|
|
100
|
+
if (isCustomProvider(selectedProvider)) {
|
|
101
|
+
// Custom OpenAI/Anthropic: after key, go to model
|
|
102
|
+
setStep('model');
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Standard providers: skip to model selection
|
|
106
|
+
setStep('model');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} })] })] }));
|
|
86
110
|
}
|
|
87
111
|
// Step 3: Model selection
|
|
88
112
|
if (step === 'model') {
|
|
@@ -96,14 +120,18 @@ export function InitWizard({ onComplete, onCancel }) {
|
|
|
96
120
|
}
|
|
97
121
|
else {
|
|
98
122
|
setSelectedModel(item.value);
|
|
99
|
-
// v3.0.0: prompt the user for cache strategy after model.
|
|
100
123
|
setStep('cache_strategy');
|
|
101
124
|
}
|
|
102
125
|
} })] }));
|
|
103
126
|
}
|
|
104
|
-
// Custom model name
|
|
127
|
+
// Custom model name input — v3.0.1 NOW has a real TextInput
|
|
105
128
|
if (step === 'custom_model') {
|
|
106
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsx(Text, { color: "#06B6D4", bold: true, children: "\u8F93\u5165\u6A21\u578B\u540D\u79F0:" }), _jsx(Text, { color: "#F59E0B", children: "\uFF08\u8BF7\u76F4\u63A5\u8F93\u5165\u6A21\u578B\u540D\u5E76\u56DE\u8F66\uFF09" })
|
|
129
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsx(Text, { color: "#06B6D4", bold: true, children: "\u8F93\u5165\u6A21\u578B\u540D\u79F0:" }), _jsx(Text, { color: "#F59E0B", children: "\uFF08\u8BF7\u76F4\u63A5\u8F93\u5165\u6A21\u578B\u540D\u5E76\u56DE\u8F66\uFF0CESC \u53D6\u6D88\uFF09" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "#06B6D4", children: "\u276F " }), _jsx(TextInput, { value: selectedModel, onChange: setSelectedModel, onSubmit: (value) => {
|
|
130
|
+
if (value.trim()) {
|
|
131
|
+
setSelectedModel(value.trim());
|
|
132
|
+
setStep('cache_strategy');
|
|
133
|
+
}
|
|
134
|
+
} })] })] }));
|
|
107
135
|
}
|
|
108
136
|
// v3.0.0: prompt-cache strategy step
|
|
109
137
|
if (step === 'cache_strategy') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InitWizard.js","sourceRoot":"","sources":["../../../src/tui/components/InitWizard.tsx"],"names":[],"mappings":";AAAA,6BAA6B;AAC7B,OAAc,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"InitWizard.js","sourceRoot":"","sources":["../../../src/tui/components/InitWizard.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/tui/app.tsx
CHANGED
|
@@ -12,7 +12,6 @@ import { useChat } from './hooks/useChat.js';
|
|
|
12
12
|
import { useCommands } from './hooks/useCommands.js';
|
|
13
13
|
import type { App } from '../app/index.js';
|
|
14
14
|
import type { MessageData } from './components/ChatMessage.js';
|
|
15
|
-
import type { ProviderName } from '../config/types.js';
|
|
16
15
|
import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs';
|
|
17
16
|
import { join } from 'path';
|
|
18
17
|
import { homedir } from 'os';
|
|
@@ -35,15 +34,13 @@ function saveModelToHistory(model: string) {
|
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
type ViewMode = 'chat' | 'model_select' | 'init_wizard'
|
|
37
|
+
type ViewMode = 'chat' | 'model_select' | 'init_wizard';
|
|
39
38
|
|
|
40
39
|
export function TuiApp({ app }: Props) {
|
|
41
40
|
const { messages, isThinking, streaming, streamingToolCalls, queuedMessage, sendMessage, cancel } = useChat(app);
|
|
42
41
|
const { handleCommand } = useCommands(app);
|
|
43
42
|
const [systemMessages, setSystemMessages] = useState<MessageData[]>([]);
|
|
44
43
|
const [viewMode, setViewMode] = useState<ViewMode>('chat');
|
|
45
|
-
const [initProvider, setInitProvider] = useState<ProviderName>('siliconflow');
|
|
46
|
-
const [initUrl, setInitUrl] = useState('');
|
|
47
44
|
const { stdout } = useStdout();
|
|
48
45
|
const terminalWidth = stdout?.columns || 80;
|
|
49
46
|
|
|
@@ -60,41 +57,6 @@ export function TuiApp({ app }: Props) {
|
|
|
60
57
|
}, []);
|
|
61
58
|
|
|
62
59
|
const onSubmit = useCallback(async (input: string) => {
|
|
63
|
-
// Init wizard - API Key input
|
|
64
|
-
if (viewMode === 'init_key') {
|
|
65
|
-
const key = input.trim();
|
|
66
|
-
if (!key) { setViewMode('chat'); return; }
|
|
67
|
-
// Save and complete
|
|
68
|
-
const { App: AppClass } = await import('../app/index.js');
|
|
69
|
-
const cfg = app.config.get();
|
|
70
|
-
app.config.save({ provider: initProvider, apiKey: key, model: cfg.model || 'default' });
|
|
71
|
-
saveModelToHistory(cfg.model || 'default');
|
|
72
|
-
setViewMode('chat');
|
|
73
|
-
addMsg(`配置已更新: ${initProvider}`);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Init wizard - Custom URL input
|
|
78
|
-
if (viewMode === 'init_url') {
|
|
79
|
-
const url = input.trim();
|
|
80
|
-
if (!url) { setViewMode('chat'); return; }
|
|
81
|
-
setInitUrl(url);
|
|
82
|
-
setViewMode('init_key');
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Init wizard - Custom model name
|
|
87
|
-
if (viewMode === 'init_custom_model') {
|
|
88
|
-
const model = input.trim();
|
|
89
|
-
if (model) {
|
|
90
|
-
app.config.save({ model });
|
|
91
|
-
saveModelToHistory(model);
|
|
92
|
-
addMsg(`模型已切换: ${model}`);
|
|
93
|
-
}
|
|
94
|
-
setViewMode('chat');
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
60
|
// Model selector - ignore text input
|
|
99
61
|
if (viewMode === 'model_select') return;
|
|
100
62
|
|
|
@@ -125,7 +87,7 @@ export function TuiApp({ app }: Props) {
|
|
|
125
87
|
}
|
|
126
88
|
|
|
127
89
|
sendMessage(input);
|
|
128
|
-
}, [handleCommand, sendMessage, app, viewMode,
|
|
90
|
+
}, [handleCommand, sendMessage, app, viewMode, addMsg]);
|
|
129
91
|
|
|
130
92
|
const allMessages = [...systemMessages, ...messages];
|
|
131
93
|
const activeSkills = app.skills.listActive().map(s => s.id);
|
|
@@ -164,6 +126,12 @@ export function TuiApp({ app }: Props) {
|
|
|
164
126
|
onAddNew={() => setViewMode('init_wizard')}
|
|
165
127
|
/>
|
|
166
128
|
) : viewMode === 'init_wizard' ? (
|
|
129
|
+
// v3.0.1: InitWizard is now fully self-contained — it owns its own
|
|
130
|
+
// input handling via useInput + ink-text-input. No external UserInput
|
|
131
|
+
// is rendered here, and app.tsx no longer has the text-entry
|
|
132
|
+
// viewModes (init_key / init_url / init_custom_model) because they
|
|
133
|
+
// were the broken input-handling path that left users stuck on a
|
|
134
|
+
// frozen "输入 API Key" screen with no way to type.
|
|
167
135
|
<InitWizard
|
|
168
136
|
onComplete={(provider, model, apiKey, baseUrl) => {
|
|
169
137
|
app.config.save({ provider, model, apiKey, baseUrl });
|
|
@@ -175,22 +143,6 @@ export function TuiApp({ app }: Props) {
|
|
|
175
143
|
/>
|
|
176
144
|
) : (
|
|
177
145
|
<Box flexDirection="column">
|
|
178
|
-
{viewMode === 'init_key' && (
|
|
179
|
-
<Box paddingLeft={1} marginBottom={0}>
|
|
180
|
-
<Text color="#06B6D4" bold>输入 API Key: </Text>
|
|
181
|
-
<Text dimColor>({initProvider})</Text>
|
|
182
|
-
</Box>
|
|
183
|
-
)}
|
|
184
|
-
{viewMode === 'init_url' && (
|
|
185
|
-
<Box paddingLeft={1} marginBottom={0}>
|
|
186
|
-
<Text color="#06B6D4" bold>输入中转站 URL: </Text>
|
|
187
|
-
</Box>
|
|
188
|
-
)}
|
|
189
|
-
{viewMode === 'init_custom_model' && (
|
|
190
|
-
<Box paddingLeft={1} marginBottom={0}>
|
|
191
|
-
<Text color="#06B6D4" bold>输入模型名称: </Text>
|
|
192
|
-
</Box>
|
|
193
|
-
)}
|
|
194
146
|
<UserInput onSubmit={onSubmit} onCancel={cancel} disabled={false} />
|
|
195
147
|
</Box>
|
|
196
148
|
)}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** @jsxImportSource react */
|
|
2
|
-
import React, { useState
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import SelectInput from 'ink-select-input';
|
|
5
|
-
import
|
|
5
|
+
import TextInput from 'ink-text-input';
|
|
6
|
+
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
6
7
|
import { join } from 'path';
|
|
7
8
|
import { homedir } from 'os';
|
|
8
9
|
import { listProviders, getModelsForProvider, isCustomProvider } from '../../config/providers.js';
|
|
@@ -13,14 +14,7 @@ interface Props {
|
|
|
13
14
|
onCancel: () => void;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
type InitStep =
|
|
17
|
-
| 'provider'
|
|
18
|
-
| 'api_key'
|
|
19
|
-
| 'model'
|
|
20
|
-
| 'custom_model'
|
|
21
|
-
| 'custom_url'
|
|
22
|
-
| 'cache_strategy'
|
|
23
|
-
| 'cache_ttl';
|
|
17
|
+
type InitStep = 'provider' | 'custom_url' | 'api_key' | 'model' | 'custom_model' | 'cache_strategy' | 'cache_ttl';
|
|
24
18
|
|
|
25
19
|
interface CacheChoice {
|
|
26
20
|
enabled: boolean;
|
|
@@ -28,15 +22,24 @@ interface CacheChoice {
|
|
|
28
22
|
}
|
|
29
23
|
|
|
30
24
|
/**
|
|
31
|
-
* v3.0.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
25
|
+
* v3.0.1: InitWizard is now fully self-contained — it manages its own
|
|
26
|
+
* step state AND its own keyboard input for text-entry steps (api_key,
|
|
27
|
+
* custom_url, custom_model). The previous version had a fatal bug:
|
|
34
28
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
29
|
+
* The text-entry steps (`api_key`, `custom_url`, `custom_model`) only
|
|
30
|
+
* rendered a `<Text>` prompt with no actual input box. The host
|
|
31
|
+
* component (app.tsx) had its own UserInput BUT only rendered it when
|
|
32
|
+
* viewMode was NOT 'init_wizard' (see app.tsx ternary). So when the
|
|
33
|
+
* wizard's internal step advanced to 'api_key', the user could SEE the
|
|
34
|
+
* "输入 API Key: 服务商: deepseek" prompt but had NO way to type into
|
|
35
|
+
* it. Ink silently swallowed keystrokes (no useInput handler was
|
|
36
|
+
* active), and the user was stuck on a frozen screen — exactly the
|
|
37
|
+
* bug that prompted the user-reported "选择 deepseek 然后退出":
|
|
38
|
+
* pressing Ctrl+C felt like the only escape.
|
|
39
|
+
*
|
|
40
|
+
* Fix: this file now uses `useInput` and `ink-text-input` for the text
|
|
41
|
+
* steps. The SetViewMode-based input delegation in app.tsx is no longer
|
|
42
|
+
* required for the InitWizard flow.
|
|
40
43
|
*/
|
|
41
44
|
export function InitWizard({ onComplete, onCancel }: Props) {
|
|
42
45
|
const [step, setStep] = useState<InitStep>('provider');
|
|
@@ -61,7 +64,6 @@ export function InitWizard({ onComplete, onCancel }: Props) {
|
|
|
61
64
|
temperature: 0.7,
|
|
62
65
|
maxTokens: 4096,
|
|
63
66
|
contextLength: 50,
|
|
64
|
-
// v3.0.0: cache policy from wizard
|
|
65
67
|
cache: {
|
|
66
68
|
enabled: cacheChoice.enabled,
|
|
67
69
|
ttl: cacheChoice.ttl,
|
|
@@ -70,18 +72,17 @@ export function InitWizard({ onComplete, onCancel }: Props) {
|
|
|
70
72
|
};
|
|
71
73
|
if (url) config.baseUrl = url;
|
|
72
74
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
75
|
+
};
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
// v3.0.1: ESC cancels the wizard from any text-entry step. This is the
|
|
78
|
+
// hook that the previous version was missing — when the user pressed
|
|
79
|
+
// ESC on the frozen "输入 API Key" screen, nothing happened because no
|
|
80
|
+
// useInput was active. Now it bails out cleanly.
|
|
81
|
+
useInput((_input, key) => {
|
|
82
|
+
if (key.escape && (step === 'api_key' || step === 'custom_url' || step === 'custom_model')) {
|
|
83
|
+
onCancel();
|
|
79
84
|
}
|
|
80
|
-
|
|
81
|
-
history.push(model);
|
|
82
|
-
writeFileSync(historyPath, JSON.stringify(history, null, 2));
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
+
});
|
|
85
86
|
|
|
86
87
|
// Step 1: Provider selection
|
|
87
88
|
if (step === 'provider') {
|
|
@@ -92,8 +93,9 @@ export function InitWizard({ onComplete, onCancel }: Props) {
|
|
|
92
93
|
<SelectInput
|
|
93
94
|
items={items}
|
|
94
95
|
onSelect={(item) => {
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
const provider = item.value as ProviderName;
|
|
97
|
+
setSelectedProvider(provider);
|
|
98
|
+
if (isCustomProvider(provider)) {
|
|
97
99
|
setStep('custom_url');
|
|
98
100
|
} else {
|
|
99
101
|
setStep('api_key');
|
|
@@ -104,24 +106,57 @@ export function InitWizard({ onComplete, onCancel }: Props) {
|
|
|
104
106
|
);
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
// Custom URL input
|
|
109
|
+
// Custom URL input — v3.0.1 NOW has a real TextInput
|
|
108
110
|
if (step === 'custom_url') {
|
|
109
111
|
return (
|
|
110
112
|
<Box flexDirection="column" paddingLeft={1}>
|
|
111
113
|
<Text color="#06B6D4" bold>输入中转站 URL:</Text>
|
|
112
114
|
<Text dimColor>例如: https://api.example.com/v1</Text>
|
|
113
|
-
<Text color="#F59E0B">(请直接输入 URL
|
|
115
|
+
<Text color="#F59E0B">(请直接输入 URL 并回车,ESC 取消)</Text>
|
|
116
|
+
<Box marginTop={1}>
|
|
117
|
+
<Text color="#06B6D4">❯ </Text>
|
|
118
|
+
<TextInput
|
|
119
|
+
value={customUrl}
|
|
120
|
+
onChange={setCustomUrl}
|
|
121
|
+
onSubmit={(value) => {
|
|
122
|
+
if (value.trim()) {
|
|
123
|
+
setCustomUrl(value.trim());
|
|
124
|
+
setStep('api_key');
|
|
125
|
+
}
|
|
126
|
+
}}
|
|
127
|
+
/>
|
|
128
|
+
</Box>
|
|
114
129
|
</Box>
|
|
115
130
|
);
|
|
116
131
|
}
|
|
117
132
|
|
|
118
|
-
// Step 2: API Key
|
|
133
|
+
// Step 2: API Key — v3.0.1 NOW has a real TextInput
|
|
119
134
|
if (step === 'api_key') {
|
|
135
|
+
const providerLabel = providers.find(p => p.key === selectedProvider)?.name || selectedProvider;
|
|
120
136
|
return (
|
|
121
137
|
<Box flexDirection="column" paddingLeft={1}>
|
|
122
138
|
<Text color="#06B6D4" bold>输入 API Key:</Text>
|
|
123
|
-
<Text dimColor>服务商: {
|
|
124
|
-
<Text color="#F59E0B">(请直接输入 Key
|
|
139
|
+
<Text dimColor>服务商: {providerLabel}</Text>
|
|
140
|
+
<Text color="#F59E0B">(请直接输入 Key 并回车,ESC 取消)</Text>
|
|
141
|
+
<Box marginTop={1}>
|
|
142
|
+
<Text color="#06B6D4">❯ </Text>
|
|
143
|
+
<TextInput
|
|
144
|
+
value={apiKey}
|
|
145
|
+
onChange={setApiKey}
|
|
146
|
+
onSubmit={(value) => {
|
|
147
|
+
if (value.trim()) {
|
|
148
|
+
setApiKey(value.trim());
|
|
149
|
+
if (isCustomProvider(selectedProvider)) {
|
|
150
|
+
// Custom OpenAI/Anthropic: after key, go to model
|
|
151
|
+
setStep('model');
|
|
152
|
+
} else {
|
|
153
|
+
// Standard providers: skip to model selection
|
|
154
|
+
setStep('model');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}}
|
|
158
|
+
/>
|
|
159
|
+
</Box>
|
|
125
160
|
</Box>
|
|
126
161
|
);
|
|
127
162
|
}
|
|
@@ -142,7 +177,6 @@ export function InitWizard({ onComplete, onCancel }: Props) {
|
|
|
142
177
|
setStep('custom_model');
|
|
143
178
|
} else {
|
|
144
179
|
setSelectedModel(item.value);
|
|
145
|
-
// v3.0.0: prompt the user for cache strategy after model.
|
|
146
180
|
setStep('cache_strategy');
|
|
147
181
|
}
|
|
148
182
|
}}
|
|
@@ -151,12 +185,25 @@ export function InitWizard({ onComplete, onCancel }: Props) {
|
|
|
151
185
|
);
|
|
152
186
|
}
|
|
153
187
|
|
|
154
|
-
// Custom model name
|
|
188
|
+
// Custom model name input — v3.0.1 NOW has a real TextInput
|
|
155
189
|
if (step === 'custom_model') {
|
|
156
190
|
return (
|
|
157
191
|
<Box flexDirection="column" paddingLeft={1}>
|
|
158
192
|
<Text color="#06B6D4" bold>输入模型名称:</Text>
|
|
159
|
-
<Text color="#F59E0B"
|
|
193
|
+
<Text color="#F59E0B">(请直接输入模型名并回车,ESC 取消)</Text>
|
|
194
|
+
<Box marginTop={1}>
|
|
195
|
+
<Text color="#06B6D4">❯ </Text>
|
|
196
|
+
<TextInput
|
|
197
|
+
value={selectedModel}
|
|
198
|
+
onChange={setSelectedModel}
|
|
199
|
+
onSubmit={(value) => {
|
|
200
|
+
if (value.trim()) {
|
|
201
|
+
setSelectedModel(value.trim());
|
|
202
|
+
setStep('cache_strategy');
|
|
203
|
+
}
|
|
204
|
+
}}
|
|
205
|
+
/>
|
|
206
|
+
</Box>
|
|
160
207
|
</Box>
|
|
161
208
|
);
|
|
162
209
|
}
|