xshat-lite 1.0.0 → 1.1.0
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 +6 -0
- package/package.json +1 -1
- package/src/index.mjs +119 -3
- package/src/utils/config.mjs +4 -0
package/README.md
CHANGED
package/package.json
CHANGED
package/src/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import readline from 'node:readline/promises';
|
|
2
|
+
import fs from 'node:fs';
|
|
2
3
|
import { stdin as input, stdout as output } from 'process';
|
|
3
4
|
import { resolve } from 'node:path';
|
|
4
5
|
import { confirm, input as promptInput, select } from '@inquirer/prompts';
|
|
@@ -50,8 +51,11 @@ import {
|
|
|
50
51
|
import { runRuntimeMaintenance } from './modules/runtime-maintenance.mjs';
|
|
51
52
|
import { waitForKeypress } from './utils/key-input.mjs';
|
|
52
53
|
import config, {
|
|
54
|
+
getRuntimeRootDir,
|
|
53
55
|
getProvider,
|
|
54
|
-
|
|
56
|
+
getUserConfigPath,
|
|
57
|
+
listProviders,
|
|
58
|
+
updateConfigValue
|
|
55
59
|
} from './utils/config.mjs';
|
|
56
60
|
|
|
57
61
|
let chatManager = null;
|
|
@@ -67,6 +71,117 @@ let lastSubmittedQuestion = '';
|
|
|
67
71
|
let lastAgentOrReplySummary = '';
|
|
68
72
|
const inputHistory = [];
|
|
69
73
|
|
|
74
|
+
function getRecommendedFirstRunProviderId() {
|
|
75
|
+
const candidates = ['duckai', 'qwen', 'deepai', 'huggingchat', 'you'];
|
|
76
|
+
const available = new Set(listProviders().map((provider) => provider.id));
|
|
77
|
+
return candidates.find((id) => available.has(id)) || getProvider().id;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isFirstRunGuideNeeded() {
|
|
81
|
+
if (config.meta?.firstRunCompleted) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return !fs.existsSync(getUserConfigPath());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function buildFirstRunGuideRows() {
|
|
89
|
+
const defaultProvider = getProvider();
|
|
90
|
+
const recommendedProvider = getProvider(getRecommendedFirstRunProviderId());
|
|
91
|
+
const userDataDirExists = fs.existsSync(config.browser?.userDataDir || '');
|
|
92
|
+
|
|
93
|
+
return [
|
|
94
|
+
{ label: '运行目录', value: getRuntimeRootDir() },
|
|
95
|
+
{ label: '默认 Provider', value: defaultProvider.name },
|
|
96
|
+
{ label: '推荐首选', value: recommendedProvider.name },
|
|
97
|
+
{ label: '浏览器模式', value: config.browser?.headless ? 'Headless' : 'Visible' },
|
|
98
|
+
{ label: '允许可见浏览器', value: String(Boolean(config.browser?.allowVisibleBrowser)) },
|
|
99
|
+
{ label: '资料目录', value: config.browser?.userDataDir || '(未设置)' },
|
|
100
|
+
{ label: '资料目录状态', value: userDataDirExists ? '已存在' : '首次启动时创建' }
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function warmupBrowserEnvironment() {
|
|
105
|
+
ui.printInfo('正在预热浏览器环境,首次启动可能需要下载浏览器内核...');
|
|
106
|
+
try {
|
|
107
|
+
await browserManager.init({
|
|
108
|
+
headless: true
|
|
109
|
+
});
|
|
110
|
+
await browserManager.close();
|
|
111
|
+
ui.printSuccess('浏览器环境已准备好');
|
|
112
|
+
return true;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
ui.printWarning(`浏览器预热未完成: ${error.message}`);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function applyRecommendedFirstRunProfile() {
|
|
120
|
+
const recommendedProviderId = getRecommendedFirstRunProviderId();
|
|
121
|
+
await Promise.all([
|
|
122
|
+
config.defaultProvider !== recommendedProviderId
|
|
123
|
+
? updateConfigValue('defaultProvider', recommendedProviderId)
|
|
124
|
+
: Promise.resolve(),
|
|
125
|
+
config.browser?.headless
|
|
126
|
+
? updateConfigValue('browser.headless', false)
|
|
127
|
+
: Promise.resolve(),
|
|
128
|
+
config.browser?.allowVisibleBrowser !== true
|
|
129
|
+
? updateConfigValue('browser.allowVisibleBrowser', true)
|
|
130
|
+
: Promise.resolve(),
|
|
131
|
+
config.browser?.autoRecover !== true
|
|
132
|
+
? updateConfigValue('browser.autoRecover', true)
|
|
133
|
+
: Promise.resolve()
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function markFirstRunGuideCompleted() {
|
|
138
|
+
await updateConfigValue('meta.firstRunCompleted', true);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function maybeRunFirstLaunchGuide() {
|
|
142
|
+
if (!isFirstRunGuideNeeded()) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
ui.clear();
|
|
147
|
+
ui.printTitle('首次启动向导');
|
|
148
|
+
ui.printNewline();
|
|
149
|
+
ui.printInfo('检测到这是一个全新的 XShat 运行环境。先做一轮轻量初始化,后面进聊天会顺很多。');
|
|
150
|
+
ui.printNewline();
|
|
151
|
+
ui.printKeyValueTable('环境概览', buildFirstRunGuideRows());
|
|
152
|
+
ui.printNewline();
|
|
153
|
+
ui.printInfo('建议第一次先使用免登录或更稳定的站点,再按需要切换到 Gemini / ChatGPT / Copilot。');
|
|
154
|
+
ui.printNewline();
|
|
155
|
+
|
|
156
|
+
const choice = await select({
|
|
157
|
+
message: '选择首次启动动作',
|
|
158
|
+
choices: [
|
|
159
|
+
{ name: '一键推荐设置(Duck.ai/Qwen + 可见浏览器)', value: 'recommended' },
|
|
160
|
+
{ name: '只预热浏览器环境', value: 'warmup' },
|
|
161
|
+
{ name: '打开系统设置自己调整', value: 'settings' },
|
|
162
|
+
{ name: '暂时跳过', value: 'skip' }
|
|
163
|
+
],
|
|
164
|
+
default: 'recommended'
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
if (choice === 'recommended') {
|
|
168
|
+
await applyRecommendedFirstRunProfile();
|
|
169
|
+
ui.printSuccess(`已应用首次推荐设置,默认提供方切到 ${getProvider().name}`);
|
|
170
|
+
await warmupBrowserEnvironment();
|
|
171
|
+
} else if (choice === 'warmup') {
|
|
172
|
+
await warmupBrowserEnvironment();
|
|
173
|
+
} else if (choice === 'settings') {
|
|
174
|
+
await showSettingsMenu({ ui, config });
|
|
175
|
+
} else {
|
|
176
|
+
ui.printMuted('已跳过首次向导,后续仍可在 /config 里调整。');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await markFirstRunGuideCompleted();
|
|
180
|
+
ui.printNewline();
|
|
181
|
+
ui.printInfo('首次向导完成。按回车进入主菜单...');
|
|
182
|
+
await askInput('', true);
|
|
183
|
+
}
|
|
184
|
+
|
|
70
185
|
function getSessionCompressionOptions() {
|
|
71
186
|
return {
|
|
72
187
|
compressThreshold: Number(config.session?.compressThreshold) || 12,
|
|
@@ -2743,11 +2858,12 @@ async function main() {
|
|
|
2743
2858
|
await logger.init();
|
|
2744
2859
|
await sessionManager.init();
|
|
2745
2860
|
await taskStore.init();
|
|
2861
|
+
setupGracefulShutdown();
|
|
2862
|
+
await maybeRunFirstLaunchGuide();
|
|
2863
|
+
|
|
2746
2864
|
const maintenance = await runRuntimeMaintenance();
|
|
2747
2865
|
ensureTaskRunner().startLoop();
|
|
2748
2866
|
|
|
2749
|
-
setupGracefulShutdown();
|
|
2750
|
-
|
|
2751
2867
|
if (maintenance.tempRemoved > 0) {
|
|
2752
2868
|
ui.printMuted(`启动清理: 已移除 ${maintenance.tempRemoved} 个临时文件`);
|
|
2753
2869
|
}
|
package/src/utils/config.mjs
CHANGED
|
@@ -219,6 +219,10 @@ export function getUserConfigPath() {
|
|
|
219
219
|
return userConfigPath;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
export function getRuntimeRootDir() {
|
|
223
|
+
return runtimeRootDir;
|
|
224
|
+
}
|
|
225
|
+
|
|
222
226
|
export function getUserConfigOverrides() {
|
|
223
227
|
return JSON.parse(JSON.stringify(userConfigOverrides));
|
|
224
228
|
}
|