travel-agent-cli 0.3.6 → 0.4.1
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/bin/cli.js +62 -55
- package/interactive.jsx +216 -0
- package/package.json +18 -4
- package/python/interactive.py +188 -28
- package/scripts/postinstall.js +7 -3
package/bin/cli.js
CHANGED
|
@@ -5,22 +5,42 @@
|
|
|
5
5
|
* 该脚本作为 npm 包的入口,调用 Python 实现的 main.py
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
11
15
|
|
|
12
16
|
// 获取包的安装路径
|
|
13
17
|
const packagePath = path.join(__dirname, '..');
|
|
14
18
|
const pythonDir = path.join(packagePath, 'python');
|
|
15
19
|
const mainPy = path.join(pythonDir, 'main.py');
|
|
16
20
|
|
|
21
|
+
// 检查虚拟环境是否存在
|
|
22
|
+
function getVenvPython() {
|
|
23
|
+
const venvPaths = [
|
|
24
|
+
path.join(pythonDir, 'venv', 'bin', 'python'),
|
|
25
|
+
path.join(pythonDir, '.venv', 'bin', 'python'),
|
|
26
|
+
path.join(pythonDir, 'venv', 'Scripts', 'python.exe'),
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
for (const venvPath of venvPaths) {
|
|
30
|
+
if (fs.existsSync(venvPath)) {
|
|
31
|
+
return venvPath;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
17
38
|
// 检查 Python 是否可用
|
|
18
39
|
function findPython() {
|
|
19
40
|
const candidates = ['python3', 'python'];
|
|
20
41
|
|
|
21
42
|
for (const cmd of candidates) {
|
|
22
43
|
try {
|
|
23
|
-
const { execSync } = require('child_process');
|
|
24
44
|
execSync(`${cmd} --version`, { stdio: 'ignore' });
|
|
25
45
|
return cmd;
|
|
26
46
|
} catch (e) {
|
|
@@ -31,21 +51,29 @@ function findPython() {
|
|
|
31
51
|
return null;
|
|
32
52
|
}
|
|
33
53
|
|
|
34
|
-
//
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
path.join(pythonDir, 'venv', 'bin', 'python'),
|
|
38
|
-
path.join(pythonDir, '.venv', 'bin', 'python'),
|
|
39
|
-
path.join(pythonDir, 'venv', 'Scripts', 'python.exe'),
|
|
40
|
-
];
|
|
54
|
+
// 启动 Ink 交互式 CLI
|
|
55
|
+
function runInteractive() {
|
|
56
|
+
const interactiveJs = path.join(packagePath, 'interactive.jsx');
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
58
|
+
if (!fs.existsSync(interactiveJs)) {
|
|
59
|
+
console.error(`错误:未找到 interactive.jsx 文件:${interactiveJs}`);
|
|
60
|
+
process.exit(1);
|
|
46
61
|
}
|
|
47
62
|
|
|
48
|
-
|
|
63
|
+
// 使用 tsx 运行 JSX
|
|
64
|
+
const child = spawn('node', ['--import', 'tsx', interactiveJs], {
|
|
65
|
+
stdio: 'inherit',
|
|
66
|
+
cwd: packagePath
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
child.on('error', (err) => {
|
|
70
|
+
console.error('执行失败:', err.message);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
child.on('exit', (code) => {
|
|
75
|
+
process.exit(code || 0);
|
|
76
|
+
});
|
|
49
77
|
}
|
|
50
78
|
|
|
51
79
|
// 主函数
|
|
@@ -64,24 +92,21 @@ function run() {
|
|
|
64
92
|
travel-agent <command> [options]
|
|
65
93
|
|
|
66
94
|
可用命令:
|
|
67
|
-
run
|
|
68
|
-
analyze
|
|
95
|
+
run 运行完整工作流
|
|
96
|
+
analyze 产品可行性分析
|
|
69
97
|
agents 查看 Agent 团队信息
|
|
70
|
-
status
|
|
71
|
-
model 管理 LLM
|
|
72
|
-
config
|
|
73
|
-
flyai FlyAI
|
|
98
|
+
status 显示系统状态
|
|
99
|
+
model 管理 LLM 模型配置
|
|
100
|
+
config 管理配置
|
|
101
|
+
flyai FlyAI 旅行搜索
|
|
74
102
|
interactive 启动交互式 CLI(推荐)
|
|
75
103
|
help 显示帮助信息
|
|
76
104
|
|
|
77
105
|
示例:
|
|
78
|
-
travel-agent interactive # 启动交互式 CLI
|
|
79
|
-
travel-agent run -k "海岛游"
|
|
80
|
-
travel-agent analyze "北欧极光"
|
|
81
|
-
travel-agent flyai "5 天日本行程规划"
|
|
82
|
-
travel-agent agents # 查看 Agent 团队
|
|
83
|
-
travel-agent model list # 查看支持的模型
|
|
84
|
-
travel-agent --help # 显示帮助
|
|
106
|
+
travel-agent interactive # 启动交互式 CLI
|
|
107
|
+
travel-agent run -k "海岛游"
|
|
108
|
+
travel-agent analyze "北欧极光"
|
|
109
|
+
travel-agent flyai "5 天日本行程规划"
|
|
85
110
|
|
|
86
111
|
选项:
|
|
87
112
|
-h, --help 显示帮助信息
|
|
@@ -93,17 +118,23 @@ function run() {
|
|
|
93
118
|
|
|
94
119
|
// 版本信息
|
|
95
120
|
if (args.includes('-v') || args.includes('--version')) {
|
|
96
|
-
const pkg =
|
|
121
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
97
122
|
console.log(`travel-agent-cli v${pkg.version}`);
|
|
98
123
|
process.exit(0);
|
|
99
124
|
}
|
|
100
125
|
|
|
126
|
+
// 检查 interactive 命令
|
|
127
|
+
if (args[0] === 'interactive') {
|
|
128
|
+
runInteractive();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
101
132
|
// 优先使用虚拟环境的 Python
|
|
102
133
|
let pythonCmd = getVenvPython();
|
|
103
134
|
|
|
104
135
|
// 如果没有虚拟环境,使用系统 Python
|
|
105
136
|
if (!pythonCmd) {
|
|
106
|
-
pythonCmd = findPython();
|
|
137
|
+
pythonCmd = await findPython();
|
|
107
138
|
}
|
|
108
139
|
|
|
109
140
|
if (!pythonCmd) {
|
|
@@ -112,30 +143,6 @@ function run() {
|
|
|
112
143
|
process.exit(1);
|
|
113
144
|
}
|
|
114
145
|
|
|
115
|
-
// 检查 interactive 命令
|
|
116
|
-
const interactivePy = path.join(pythonDir, 'interactive.py');
|
|
117
|
-
if (args[0] === 'interactive') {
|
|
118
|
-
if (!fs.existsSync(interactivePy)) {
|
|
119
|
-
console.error(`错误:未找到 interactive.py 文件:${interactivePy}`);
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
122
|
-
const pyArgs = [interactivePy, ...args.slice(1)];
|
|
123
|
-
const child = spawn(pythonCmd, pyArgs, {
|
|
124
|
-
stdio: 'inherit',
|
|
125
|
-
cwd: pythonDir
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
child.on('error', (err) => {
|
|
129
|
-
console.error('执行失败:', err.message);
|
|
130
|
-
process.exit(1);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
child.on('exit', (code) => {
|
|
134
|
-
process.exit(code || 0);
|
|
135
|
-
});
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
146
|
// 检查主文件是否存在
|
|
140
147
|
if (!fs.existsSync(mainPy)) {
|
|
141
148
|
console.error(`错误:未找到 main.py 文件:${mainPy}`);
|
package/interactive.jsx
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* travel-agent 交互式 CLI - Ink 版本
|
|
4
|
+
* 基于 React/Ink 实现,类似 Claude Code 的交互方式
|
|
5
|
+
*/
|
|
6
|
+
import React, { useState, useEffect } from 'react';
|
|
7
|
+
import { render, Box, Text, useApp, useInput } from 'ink';
|
|
8
|
+
import TextInput from 'ink-text-input';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
|
|
11
|
+
// 命令选项
|
|
12
|
+
const commands = [
|
|
13
|
+
{ label: 'run - 运行完整工作流', value: 'run' },
|
|
14
|
+
{ label: 'flyai - FlyAI 旅行搜索', value: 'flyai' },
|
|
15
|
+
{ label: 'analyze - 产品可行性分析', value: 'analyze' },
|
|
16
|
+
{ label: 'model - 模型管理', value: 'model' },
|
|
17
|
+
{ label: 'config - 配置管理', value: 'config' },
|
|
18
|
+
{ label: 'status - 系统状态', value: 'status' },
|
|
19
|
+
{ label: 'agents - 查看 Agent 团队', value: 'agents' },
|
|
20
|
+
{ label: 'help - 帮助信息', value: 'help' },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
// 主应用组件
|
|
24
|
+
function App() {
|
|
25
|
+
const { exit } = useApp();
|
|
26
|
+
const [input, setInput] = useState('');
|
|
27
|
+
const [messages, setMessages] = useState([
|
|
28
|
+
{ type: 'system', content: '欢迎使用 travel-agent 交互式 CLI' },
|
|
29
|
+
{ type: 'system', content: '' },
|
|
30
|
+
{ type: 'system', content: '可用命令:' },
|
|
31
|
+
{ type: 'info', content: ' /run - 运行完整工作流' },
|
|
32
|
+
{ type: 'info', content: ' /flyai - FlyAI 旅行搜索' },
|
|
33
|
+
{ type: 'info', content: ' /analyze - 产品可行性分析' },
|
|
34
|
+
{ type: 'info', content: ' /model - 模型管理' },
|
|
35
|
+
{ type: 'info', content: ' /config - 配置管理' },
|
|
36
|
+
{ type: 'info', content: ' /status - 系统状态' },
|
|
37
|
+
{ type: 'info', content: ' /agents - 查看 Agent 团队' },
|
|
38
|
+
{ type: 'system', content: '' },
|
|
39
|
+
{ type: 'system', content: '快捷键:Ctrl+C 退出,Ctrl+L 清屏,/ 打开命令面板' },
|
|
40
|
+
{ type: 'system', content: '' },
|
|
41
|
+
]);
|
|
42
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
43
|
+
const [showCommandPalette, setShowCommandPalette] = useState(false);
|
|
44
|
+
const [commandInput, setCommandInput] = useState('');
|
|
45
|
+
const [filteredCommands, setFilteredCommands] = useState(commands);
|
|
46
|
+
const [selectedCommandIndex, setSelectedCommandIndex] = useState(0);
|
|
47
|
+
|
|
48
|
+
// 处理键盘输入
|
|
49
|
+
useInput((inputChar, key) => {
|
|
50
|
+
// Ctrl+C 退出
|
|
51
|
+
if (key.ctrl && inputChar === 'c') {
|
|
52
|
+
exit();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Ctrl+L 清屏
|
|
56
|
+
if (key.ctrl && inputChar === 'l') {
|
|
57
|
+
setMessages([
|
|
58
|
+
{ type: 'system', content: '欢迎使用 travel-agent 交互式 CLI' },
|
|
59
|
+
{ type: 'system', content: '' },
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// / 打开命令面板
|
|
64
|
+
if (inputChar === '/' && !showCommandPalette) {
|
|
65
|
+
setShowCommandPalette(true);
|
|
66
|
+
setCommandInput('');
|
|
67
|
+
setFilteredCommands(commands);
|
|
68
|
+
setSelectedCommandIndex(0);
|
|
69
|
+
return; // 阻止字符输入到 TextInput
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Esc 关闭命令面板
|
|
73
|
+
if (key.escape && showCommandPalette) {
|
|
74
|
+
setShowCommandPalette(false);
|
|
75
|
+
setCommandInput('');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 命令面板导航
|
|
80
|
+
if (showCommandPalette) {
|
|
81
|
+
if (key.upArrow) {
|
|
82
|
+
setSelectedCommandIndex(prev => Math.max(0, prev - 1));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (key.downArrow) {
|
|
86
|
+
setSelectedCommandIndex(prev => Math.min(filteredCommands.length - 1, prev + 1));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Enter 提交
|
|
90
|
+
if (key.return && filteredCommands.length > 0) {
|
|
91
|
+
const selected = filteredCommands[selectedCommandIndex];
|
|
92
|
+
setInput('/' + selected.value);
|
|
93
|
+
setShowCommandPalette(false);
|
|
94
|
+
setCommandInput('');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// 处理命令提交
|
|
101
|
+
const handleSubmit = async (value) => {
|
|
102
|
+
if (!value.trim()) return;
|
|
103
|
+
|
|
104
|
+
const userMsg = { type: 'user', content: value };
|
|
105
|
+
setMessages(prev => [...prev, userMsg]);
|
|
106
|
+
setInput('');
|
|
107
|
+
setIsProcessing(true);
|
|
108
|
+
|
|
109
|
+
// 模拟处理延迟
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
setMessages(prev => [...prev, {
|
|
112
|
+
type: 'result',
|
|
113
|
+
content: `命令 "${value}" 执行完成(演示模式)`
|
|
114
|
+
}]);
|
|
115
|
+
setIsProcessing(false);
|
|
116
|
+
}, 1000);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// 命令面板过滤
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (commandInput) {
|
|
122
|
+
setFilteredCommands(
|
|
123
|
+
commands.filter(cmd =>
|
|
124
|
+
cmd.value.toLowerCase().includes(commandInput.toLowerCase()) ||
|
|
125
|
+
cmd.label.toLowerCase().includes(commandInput.toLowerCase())
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
setSelectedCommandIndex(0);
|
|
129
|
+
} else {
|
|
130
|
+
setFilteredCommands(commands);
|
|
131
|
+
}
|
|
132
|
+
}, [commandInput]);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<Box flexDirection="column">
|
|
136
|
+
{/* 头部 */}
|
|
137
|
+
<Box marginBottom={1}>
|
|
138
|
+
<Text bold color="cyan">travel-agent</Text>
|
|
139
|
+
<Text> - AI 驱动的旅行目的地推荐</Text>
|
|
140
|
+
</Box>
|
|
141
|
+
|
|
142
|
+
{/* 消息区域 */}
|
|
143
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
144
|
+
{messages.map((msg, i) => (
|
|
145
|
+
<Box key={i}>
|
|
146
|
+
{msg.type === 'user' && (
|
|
147
|
+
<Text color="blue">{'> '}{msg.content}</Text>
|
|
148
|
+
)}
|
|
149
|
+
{msg.type === 'system' && (
|
|
150
|
+
<Text dimColor>{msg.content}</Text>
|
|
151
|
+
)}
|
|
152
|
+
{msg.type === 'info' && (
|
|
153
|
+
<Text color="gray">{msg.content}</Text>
|
|
154
|
+
)}
|
|
155
|
+
{msg.type === 'result' && (
|
|
156
|
+
<Box borderStyle="round" borderColor="green" paddingX={1}>
|
|
157
|
+
<Text color="green">{msg.content}</Text>
|
|
158
|
+
</Box>
|
|
159
|
+
)}
|
|
160
|
+
{msg.type === 'error' && (
|
|
161
|
+
<Text color="red">错误:{msg.content}</Text>
|
|
162
|
+
)}
|
|
163
|
+
</Box>
|
|
164
|
+
))}
|
|
165
|
+
{isProcessing && (
|
|
166
|
+
<Text dimColor>正在处理...</Text>
|
|
167
|
+
)}
|
|
168
|
+
</Box>
|
|
169
|
+
|
|
170
|
+
{/* 命令面板 */}
|
|
171
|
+
{showCommandPalette && (
|
|
172
|
+
<Box flexDirection="column" marginBottom={1} borderStyle="single" borderColor="cyan" paddingX={1}>
|
|
173
|
+
<Box>
|
|
174
|
+
<Text>命令:/</Text>
|
|
175
|
+
<Text color="cyan">{commandInput}</Text>
|
|
176
|
+
</Box>
|
|
177
|
+
{filteredCommands.map((cmd, i) => (
|
|
178
|
+
<Box key={cmd.value}>
|
|
179
|
+
{i === selectedCommandIndex && <Text color="cyan">{'>'} </Text>}
|
|
180
|
+
{i !== selectedCommandIndex && <Text> </Text>}
|
|
181
|
+
<Text color={i === selectedCommandIndex ? 'cyan' : 'gray'}>{cmd.label}</Text>
|
|
182
|
+
</Box>
|
|
183
|
+
))}
|
|
184
|
+
</Box>
|
|
185
|
+
)}
|
|
186
|
+
|
|
187
|
+
{/* 输入区域 */}
|
|
188
|
+
<Box>
|
|
189
|
+
<Text color="blue">{'> '}</Text>
|
|
190
|
+
<TextInput
|
|
191
|
+
value={input}
|
|
192
|
+
onChange={setInput}
|
|
193
|
+
onSubmit={handleSubmit}
|
|
194
|
+
placeholder="输入消息或按 / 打开命令面板..."
|
|
195
|
+
focus={!showCommandPalette}
|
|
196
|
+
/>
|
|
197
|
+
</Box>
|
|
198
|
+
</Box>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 渲染应用
|
|
203
|
+
const instance = render(<App />, {
|
|
204
|
+
exitOnCtrlC: false,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// 优雅退出
|
|
208
|
+
process.on('SIGINT', () => {
|
|
209
|
+
instance.unmount();
|
|
210
|
+
process.exit(0);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
process.on('SIGTERM', () => {
|
|
214
|
+
instance.unmount();
|
|
215
|
+
process.exit(0);
|
|
216
|
+
});
|
package/package.json
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "travel-agent-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "AI 驱动的旅行目的地推荐 Agent - 命令行工具(集成 FlyAI 旅行搜索)",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"bin": {
|
|
6
7
|
"travel-agent": "bin/cli.js",
|
|
7
8
|
"travel-agent-cli": "bin/cli.js"
|
|
8
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"python",
|
|
13
|
+
"scripts",
|
|
14
|
+
"interactive.jsx"
|
|
15
|
+
],
|
|
9
16
|
"scripts": {
|
|
10
17
|
"postinstall": "node scripts/postinstall.js",
|
|
11
18
|
"preuninstall": "node scripts/preuninstall.js"
|
|
12
19
|
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"ink": "^5.2.0",
|
|
22
|
+
"react": "^18.3.1",
|
|
23
|
+
"ink-text-input": "^6.0.0",
|
|
24
|
+
"ink-select-input": "^6.0.0",
|
|
25
|
+
"chalk": "^5.4.1",
|
|
26
|
+
"tsx": "^4.19.2"
|
|
27
|
+
},
|
|
13
28
|
"keywords": [
|
|
14
29
|
"travel",
|
|
15
30
|
"agent",
|
|
@@ -32,12 +47,11 @@
|
|
|
32
47
|
"directory": "npm-package"
|
|
33
48
|
},
|
|
34
49
|
"engines": {
|
|
35
|
-
"node": ">=
|
|
50
|
+
"node": ">=18.0.0"
|
|
36
51
|
},
|
|
37
52
|
"os": [
|
|
38
53
|
"darwin",
|
|
39
|
-
"linux"
|
|
40
|
-
"win32"
|
|
54
|
+
"linux"
|
|
41
55
|
],
|
|
42
56
|
"preferGlobal": true
|
|
43
57
|
}
|
package/python/interactive.py
CHANGED
|
@@ -20,6 +20,57 @@ from main import run_command
|
|
|
20
20
|
from config.settings import get_settings
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
class WhiteInput(Input):
|
|
24
|
+
"""强制白底黑字的 Input 组件"""
|
|
25
|
+
|
|
26
|
+
DEFAULT_CSS = """
|
|
27
|
+
WhiteInput {
|
|
28
|
+
background: #ffffff !important;
|
|
29
|
+
color: #000000 !important;
|
|
30
|
+
height: 3;
|
|
31
|
+
margin: 1 1 0 1;
|
|
32
|
+
padding: 1;
|
|
33
|
+
border: solid #000000;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
WhiteInput:focus {
|
|
37
|
+
border: solid #e94560;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
WhiteInput > .input--cursor {
|
|
41
|
+
background: #000000 !important;
|
|
42
|
+
color: #ffffff !important;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
WhiteInput > .input--selection {
|
|
46
|
+
background: #cccccc !important;
|
|
47
|
+
color: #000000 !important;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
WhiteInput > .input--placeholder, WhiteInput > .input--suggestion {
|
|
51
|
+
color: #666666 !important;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
WhiteInput:ansi {
|
|
55
|
+
background: ansi_white !important;
|
|
56
|
+
color: ansi_black !important;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
WhiteInput:ansi > .input--cursor {
|
|
60
|
+
background: ansi_black !important;
|
|
61
|
+
color: ansi_white !important;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
WhiteInput:ansi > .input--selection {
|
|
65
|
+
background: ansi_default !important;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
WhiteInput:ansi > .input--placeholder, WhiteInput:ansi > .input--suggestion {
|
|
69
|
+
text-style: dim !important;
|
|
70
|
+
}
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
|
|
23
74
|
class CommandProvider(Provider):
|
|
24
75
|
"""命令提供者 - 用于命令面板"""
|
|
25
76
|
|
|
@@ -68,8 +119,8 @@ class ChatScreen(Screen):
|
|
|
68
119
|
with Vertical():
|
|
69
120
|
# 聊天输出区域 - 使用 RichLog 支持 Markdown
|
|
70
121
|
yield RichLog(id="chat-output", highlight=True, markup=True)
|
|
71
|
-
# 输入区域
|
|
72
|
-
yield
|
|
122
|
+
# 输入区域 - 使用自定义 WhiteInput 强制白底黑字
|
|
123
|
+
yield WhiteInput(
|
|
73
124
|
placeholder="输入消息或按 / 打开命令面板...",
|
|
74
125
|
id="chat-input"
|
|
75
126
|
)
|
|
@@ -197,8 +248,15 @@ class TravelAgentApp(App):
|
|
|
197
248
|
SUB_TITLE = "AI 驱动的旅行目的地推荐"
|
|
198
249
|
|
|
199
250
|
CSS = """
|
|
251
|
+
/* 强制使用亮色主题 - 白底黑字 */
|
|
252
|
+
App {
|
|
253
|
+
background: #ffffff;
|
|
254
|
+
color: #000000;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* 主屏幕背景 - 白色 */
|
|
200
258
|
Screen {
|
|
201
|
-
background: #
|
|
259
|
+
background: #ffffff;
|
|
202
260
|
}
|
|
203
261
|
|
|
204
262
|
Vertical {
|
|
@@ -207,7 +265,8 @@ class TravelAgentApp(App):
|
|
|
207
265
|
|
|
208
266
|
#chat-output {
|
|
209
267
|
height: 1fr;
|
|
210
|
-
background: #
|
|
268
|
+
background: #ffffff;
|
|
269
|
+
color: #000000;
|
|
211
270
|
padding: 1;
|
|
212
271
|
}
|
|
213
272
|
|
|
@@ -216,56 +275,157 @@ class TravelAgentApp(App):
|
|
|
216
275
|
margin: 1 1 0 1;
|
|
217
276
|
padding: 1;
|
|
218
277
|
dock: bottom;
|
|
219
|
-
background: #
|
|
220
|
-
|
|
278
|
+
background: #ffffff;
|
|
279
|
+
color: #000000;
|
|
280
|
+
border: solid #000000;
|
|
221
281
|
}
|
|
222
282
|
|
|
223
283
|
Input#chat-input:focus {
|
|
224
|
-
background: #
|
|
284
|
+
background: #ffffff;
|
|
285
|
+
color: #000000;
|
|
225
286
|
border: solid #e94560;
|
|
226
287
|
}
|
|
227
288
|
|
|
228
|
-
/* Command Palette
|
|
289
|
+
/* Command Palette - 强制白底黑字 */
|
|
229
290
|
CommandPalette {
|
|
230
|
-
background:
|
|
291
|
+
background: #ffffff !important;
|
|
292
|
+
color: #000000 !important;
|
|
231
293
|
}
|
|
232
294
|
|
|
233
|
-
CommandPalette >
|
|
234
|
-
|
|
235
|
-
|
|
295
|
+
CommandPalette > #--container {
|
|
296
|
+
align: center middle;
|
|
297
|
+
background: #ffffff !important;
|
|
236
298
|
}
|
|
237
299
|
|
|
238
|
-
CommandPalette
|
|
239
|
-
background: #
|
|
240
|
-
|
|
241
|
-
|
|
300
|
+
CommandPalette > #--container > Vertical {
|
|
301
|
+
background: #ffffff !important;
|
|
302
|
+
border: solid #000000 !important;
|
|
303
|
+
width: 80;
|
|
304
|
+
height: auto;
|
|
305
|
+
max-height: 20;
|
|
242
306
|
}
|
|
243
307
|
|
|
244
|
-
CommandPalette
|
|
245
|
-
background: #
|
|
246
|
-
|
|
308
|
+
CommandPalette > #--container > Vertical > #--input {
|
|
309
|
+
background: #ffffff !important;
|
|
310
|
+
color: #000000 !important;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Input 主体 */
|
|
314
|
+
CommandPalette #--input Input {
|
|
315
|
+
background: #ffffff !important;
|
|
316
|
+
color: #000000 !important;
|
|
317
|
+
border: solid #000000 !important;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
CommandPalette #--input Input:focus {
|
|
321
|
+
background: #ffffff !important;
|
|
322
|
+
color: #000000 !important;
|
|
323
|
+
border: solid #e94560 !important;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* Input 内部子组件 - 使用直接子选择器 */
|
|
327
|
+
CommandPalette #--input > .input--cursor {
|
|
328
|
+
background: #000000 !important;
|
|
329
|
+
color: #ffffff !important;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
CommandPalette #--input > .input--selection {
|
|
333
|
+
background: #cccccc !important;
|
|
334
|
+
color: #000000 !important;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
CommandPalette #--input > .input--placeholder {
|
|
338
|
+
color: #666666 !important;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
CommandPalette #--input > .input--suggestion {
|
|
342
|
+
color: #666666 !important;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* 输入内容的颜色 - 强制覆盖 */
|
|
346
|
+
CommandPalette #--input Input * {
|
|
347
|
+
color: #000000 !important;
|
|
348
|
+
background: #ffffff !important;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
CommandPalette #--input SearchIcon {
|
|
352
|
+
color: #000000 !important;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
CommandPalette #--input Button {
|
|
356
|
+
background: #e94560 !important;
|
|
357
|
+
color: #ffffff !important;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
CommandPalette #--results {
|
|
361
|
+
background: #ffffff !important;
|
|
362
|
+
color: #000000 !important;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
CommandPalette #--results .option-list {
|
|
366
|
+
background: #ffffff !important;
|
|
367
|
+
color: #000000 !important;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
CommandPalette .option-list__option,
|
|
371
|
+
CommandPalette .option-list--option {
|
|
372
|
+
color: #000000 !important;
|
|
373
|
+
background: #ffffff !important;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
CommandPalette .option-list__option--highlighted,
|
|
377
|
+
CommandPalette .option-list--option--highlighted {
|
|
378
|
+
background: #e94560 !important;
|
|
379
|
+
color: #ffffff !important;
|
|
247
380
|
}
|
|
248
381
|
|
|
249
382
|
CommandPalette .command-palette--help-text {
|
|
250
|
-
color: #
|
|
383
|
+
color: #666666 !important;
|
|
251
384
|
}
|
|
252
385
|
|
|
253
386
|
CommandPalette .command-palette--highlight {
|
|
254
|
-
text-style: bold;
|
|
387
|
+
text-style: bold !important;
|
|
388
|
+
color: #e94560 !important;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
CommandPalette Static,
|
|
392
|
+
CommandPalette Label {
|
|
393
|
+
color: #000000 !important;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
CommandPalette LoadingIndicator {
|
|
397
|
+
background: #ffffff;
|
|
255
398
|
color: #e94560;
|
|
256
399
|
}
|
|
257
400
|
|
|
258
|
-
|
|
259
|
-
|
|
401
|
+
/* ANSI 模式覆盖 */
|
|
402
|
+
CommandPalette:ansi {
|
|
403
|
+
background: ansi_white !important;
|
|
404
|
+
color: ansi_black !important;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
CommandPalette:ansi #--input Input {
|
|
408
|
+
background: ansi_white !important;
|
|
409
|
+
color: ansi_black !important;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
CommandPalette:ansi #--input > .input--cursor {
|
|
413
|
+
background: ansi_black !important;
|
|
414
|
+
color: ansi_white !important;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
CommandPalette:ansi #--input > .input--selection {
|
|
418
|
+
background: ansi_default !important;
|
|
260
419
|
}
|
|
261
420
|
|
|
262
|
-
CommandPalette .
|
|
263
|
-
|
|
421
|
+
CommandPalette:ansi #--input > .input--placeholder,
|
|
422
|
+
CommandPalette:ansi #--input > .input--suggestion {
|
|
423
|
+
text-style: dim !important;
|
|
264
424
|
}
|
|
265
425
|
|
|
266
|
-
CommandPalette .option-list__option
|
|
267
|
-
|
|
268
|
-
|
|
426
|
+
CommandPalette:ansi .option-list__option {
|
|
427
|
+
color: ansi_black !important;
|
|
428
|
+
background: ansi_white !important;
|
|
269
429
|
}
|
|
270
430
|
"""
|
|
271
431
|
|
package/scripts/postinstall.js
CHANGED
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
* 创建虚拟环境并安装 Python 依赖
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
10
14
|
|
|
11
15
|
const packagePath = path.join(__dirname, '..');
|
|
12
16
|
const pythonDir = path.join(packagePath, 'python');
|