@yivan-lab/pretty-please 1.0.0 → 1.2.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 +381 -28
- package/bin/pls.tsx +1138 -109
- package/dist/bin/pls.d.ts +1 -1
- package/dist/bin/pls.js +994 -91
- package/dist/package.json +80 -0
- package/dist/src/ai.d.ts +1 -41
- package/dist/src/ai.js +9 -190
- package/dist/src/alias.d.ts +41 -0
- package/dist/src/alias.js +240 -0
- package/dist/src/builtin-detector.d.ts +14 -8
- package/dist/src/builtin-detector.js +36 -16
- package/dist/src/chat-history.d.ts +16 -11
- package/dist/src/chat-history.js +35 -4
- package/dist/src/components/Chat.js +5 -4
- package/dist/src/components/CodeColorizer.js +26 -20
- package/dist/src/components/CommandBox.js +3 -17
- package/dist/src/components/ConfirmationPrompt.d.ts +2 -1
- package/dist/src/components/ConfirmationPrompt.js +9 -4
- package/dist/src/components/Duration.js +2 -1
- package/dist/src/components/InlineRenderer.js +2 -1
- package/dist/src/components/MarkdownDisplay.js +2 -1
- package/dist/src/components/MultiStepCommandGenerator.d.ts +5 -1
- package/dist/src/components/MultiStepCommandGenerator.js +127 -14
- package/dist/src/components/TableRenderer.js +2 -1
- package/dist/src/config.d.ts +59 -9
- package/dist/src/config.js +147 -48
- package/dist/src/history.d.ts +19 -5
- package/dist/src/history.js +26 -11
- package/dist/src/mastra-agent.d.ts +0 -1
- package/dist/src/mastra-agent.js +3 -4
- package/dist/src/mastra-chat.d.ts +28 -0
- package/dist/src/mastra-chat.js +93 -0
- package/dist/src/multi-step.d.ts +23 -7
- package/dist/src/multi-step.js +29 -6
- package/dist/src/prompts.d.ts +11 -0
- package/dist/src/prompts.js +140 -0
- package/dist/src/remote-history.d.ts +63 -0
- package/dist/src/remote-history.js +315 -0
- package/dist/src/remote.d.ts +113 -0
- package/dist/src/remote.js +634 -0
- package/dist/src/shell-hook.d.ts +87 -12
- package/dist/src/shell-hook.js +315 -17
- package/dist/src/sysinfo.d.ts +9 -5
- package/dist/src/sysinfo.js +2 -2
- package/dist/src/ui/theme.d.ts +27 -24
- package/dist/src/ui/theme.js +71 -21
- package/dist/src/upgrade.d.ts +41 -0
- package/dist/src/upgrade.js +348 -0
- package/dist/src/utils/console.d.ts +11 -11
- package/dist/src/utils/console.js +26 -17
- package/package.json +11 -9
- package/src/alias.ts +301 -0
- package/src/builtin-detector.ts +126 -0
- package/src/chat-history.ts +140 -0
- package/src/components/Chat.tsx +6 -5
- package/src/components/CodeColorizer.tsx +27 -19
- package/src/components/CommandBox.tsx +3 -17
- package/src/components/ConfirmationPrompt.tsx +11 -3
- package/src/components/Duration.tsx +2 -1
- package/src/components/InlineRenderer.tsx +2 -1
- package/src/components/MarkdownDisplay.tsx +2 -1
- package/src/components/MultiStepCommandGenerator.tsx +167 -16
- package/src/components/TableRenderer.tsx +2 -1
- package/src/config.ts +394 -0
- package/src/history.ts +160 -0
- package/src/mastra-agent.ts +3 -4
- package/src/mastra-chat.ts +124 -0
- package/src/multi-step.ts +45 -8
- package/src/prompts.ts +154 -0
- package/src/remote-history.ts +390 -0
- package/src/remote.ts +800 -0
- package/src/shell-hook.ts +754 -0
- package/src/{sysinfo.js → sysinfo.ts} +28 -16
- package/src/ui/theme.ts +101 -24
- package/src/upgrade.ts +397 -0
- package/src/utils/{console.js → console.ts} +36 -27
- package/bin/pls.js +0 -681
- package/src/ai.js +0 -324
- package/src/builtin-detector.js +0 -98
- package/src/chat-history.js +0 -94
- package/src/components/ChatStatus.tsx +0 -53
- package/src/components/CommandGenerator.tsx +0 -184
- package/src/components/ConfigDisplay.tsx +0 -64
- package/src/components/ConfigWizard.tsx +0 -101
- package/src/components/HistoryDisplay.tsx +0 -69
- package/src/components/HookManager.tsx +0 -150
- package/src/config.js +0 -221
- package/src/history.js +0 -131
- package/src/shell-hook.js +0 -393
package/dist/src/ui/theme.js
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
// 深色主题(原默认主题)
|
|
5
|
+
const darkTheme = {
|
|
6
|
+
primary: '#00D9FF',
|
|
7
|
+
secondary: '#A78BFA',
|
|
8
|
+
accent: '#F472B6',
|
|
9
|
+
success: '#10B981',
|
|
10
|
+
error: '#EF4444',
|
|
11
|
+
warning: '#F59E0B',
|
|
12
|
+
info: '#3B82F6',
|
|
13
13
|
text: {
|
|
14
|
-
primary: '#E5E7EB',
|
|
15
|
-
secondary: '#9CA3AF',
|
|
16
|
-
muted: '#6B7280',
|
|
17
|
-
dim: '#4B5563',
|
|
14
|
+
primary: '#E5E7EB',
|
|
15
|
+
secondary: '#9CA3AF',
|
|
16
|
+
muted: '#6B7280',
|
|
17
|
+
dim: '#4B5563',
|
|
18
18
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
divider: '#1F2937', // 分隔线
|
|
22
|
-
// 代码相关
|
|
19
|
+
border: '#374151',
|
|
20
|
+
divider: '#1F2937',
|
|
23
21
|
code: {
|
|
24
22
|
background: '#1F2937',
|
|
25
23
|
text: '#E5E7EB',
|
|
@@ -27,5 +25,57 @@ export const theme = {
|
|
|
27
25
|
string: '#98C379',
|
|
28
26
|
function: '#61AFEF',
|
|
29
27
|
comment: '#5C6370',
|
|
30
|
-
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
// 浅色主题(白色/浅色终端背景)
|
|
31
|
+
// 所有颜色都要在白色背景上清晰可见
|
|
32
|
+
const lightTheme = {
|
|
33
|
+
primary: '#0369A1', // 深天蓝,在白底上醒目
|
|
34
|
+
secondary: '#6D28D9', // 深紫色
|
|
35
|
+
accent: '#BE185D', // 深粉色
|
|
36
|
+
success: '#047857', // 深绿色
|
|
37
|
+
error: '#B91C1C', // 深红色
|
|
38
|
+
warning: '#B45309', // 深橙色
|
|
39
|
+
info: '#1D4ED8', // 深蓝色
|
|
40
|
+
text: {
|
|
41
|
+
primary: '#111827', // 近黑色,主要文字
|
|
42
|
+
secondary: '#374151', // 深灰色
|
|
43
|
+
muted: '#4B5563', // 中灰色
|
|
44
|
+
dim: '#6B7280', // 浅灰色
|
|
45
|
+
},
|
|
46
|
+
border: '#6B7280', // 边框要明显
|
|
47
|
+
divider: '#9CA3AF',
|
|
48
|
+
code: {
|
|
49
|
+
background: '#F3F4F6',
|
|
50
|
+
text: '#111827',
|
|
51
|
+
keyword: '#6D28D9',
|
|
52
|
+
string: '#047857',
|
|
53
|
+
function: '#0369A1',
|
|
54
|
+
comment: '#4B5563',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
// 所有主题
|
|
58
|
+
export const themes = {
|
|
59
|
+
dark: darkTheme,
|
|
60
|
+
light: lightTheme,
|
|
31
61
|
};
|
|
62
|
+
// 获取当前主题
|
|
63
|
+
export function getCurrentTheme() {
|
|
64
|
+
// 直接读取配置文件,避免循环依赖
|
|
65
|
+
try {
|
|
66
|
+
const configPath = path.join(os.homedir(), '.please', 'config.json');
|
|
67
|
+
if (fs.existsSync(configPath)) {
|
|
68
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
69
|
+
const config = JSON.parse(content);
|
|
70
|
+
if (config.theme && themes[config.theme]) {
|
|
71
|
+
return themes[config.theme];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// 忽略错误,返回默认主题
|
|
77
|
+
}
|
|
78
|
+
return themes.dark;
|
|
79
|
+
}
|
|
80
|
+
// 向后兼容:导出默认主题
|
|
81
|
+
export const theme = darkTheme;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取最新版本(通过重定向,避免 API 限制)
|
|
3
|
+
* 优先使用 curl(支持代理),fallback 到 https 模块
|
|
4
|
+
*/
|
|
5
|
+
export declare function getLatestVersion(): Promise<string | null>;
|
|
6
|
+
/**
|
|
7
|
+
* 比较版本号
|
|
8
|
+
* @returns 1 if v1 > v2, -1 if v1 < v2, 0 if equal
|
|
9
|
+
*/
|
|
10
|
+
export declare function compareVersions(v1: string, v2: string): number;
|
|
11
|
+
/**
|
|
12
|
+
* 检测当前平台
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectPlatform(): {
|
|
15
|
+
os: string;
|
|
16
|
+
arch: string;
|
|
17
|
+
artifact: string;
|
|
18
|
+
} | null;
|
|
19
|
+
/**
|
|
20
|
+
* 获取当前可执行文件路径
|
|
21
|
+
*/
|
|
22
|
+
export declare function getCurrentExecutablePath(): string;
|
|
23
|
+
/**
|
|
24
|
+
* 检查是否有新版本(带缓存)
|
|
25
|
+
*/
|
|
26
|
+
export declare function checkForUpdates(currentVersion: string, force?: boolean): Promise<{
|
|
27
|
+
hasUpdate: boolean;
|
|
28
|
+
latestVersion: string | null;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* 显示更新提示
|
|
32
|
+
*/
|
|
33
|
+
export declare function showUpdateNotice(currentVersion: string, latestVersion: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* 检测是否是 Bun 编译的二进制
|
|
36
|
+
*/
|
|
37
|
+
export declare function isBunBinary(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* 执行升级
|
|
40
|
+
*/
|
|
41
|
+
export declare function performUpgrade(currentVersion: string): Promise<boolean>;
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 版本升级模块
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import https from 'https';
|
|
8
|
+
import { execSync, spawn } from 'child_process';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import * as console2 from './utils/console.js';
|
|
11
|
+
import { getCurrentTheme } from './ui/theme.js';
|
|
12
|
+
// 获取主题颜色
|
|
13
|
+
function getColors() {
|
|
14
|
+
const theme = getCurrentTheme();
|
|
15
|
+
return {
|
|
16
|
+
primary: theme.primary,
|
|
17
|
+
success: theme.success,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const REPO = 'IvanLark/pretty-please';
|
|
21
|
+
const UPDATE_CHECK_FILE = path.join(os.homedir(), '.please', 'update-check.json');
|
|
22
|
+
const CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 小时
|
|
23
|
+
/**
|
|
24
|
+
* 获取最新版本(通过重定向,避免 API 限制)
|
|
25
|
+
* 优先使用 curl(支持代理),fallback 到 https 模块
|
|
26
|
+
*/
|
|
27
|
+
export async function getLatestVersion() {
|
|
28
|
+
// 先尝试用 curl(支持环境变量代理)
|
|
29
|
+
try {
|
|
30
|
+
const result = execSync(`curl -fsSI "https://github.com/${REPO}/releases/latest" 2>/dev/null | grep -i "^location:" | head -1`, { timeout: 10000, encoding: 'utf-8' });
|
|
31
|
+
const match = result.match(/\/tag\/([^\s\r\n]+)/);
|
|
32
|
+
if (match) {
|
|
33
|
+
return match[1].trim();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// curl 失败,尝试 https 模块
|
|
38
|
+
}
|
|
39
|
+
// fallback: 使用 https 模块
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
const req = https.request(`https://github.com/${REPO}/releases/latest`, { method: 'HEAD' }, (res) => {
|
|
42
|
+
const location = res.headers.location;
|
|
43
|
+
if (location) {
|
|
44
|
+
const match = location.match(/\/tag\/([^/]+)$/);
|
|
45
|
+
if (match) {
|
|
46
|
+
resolve(match[1]);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
resolve(null);
|
|
51
|
+
});
|
|
52
|
+
req.on('error', () => resolve(null));
|
|
53
|
+
req.setTimeout(5000, () => {
|
|
54
|
+
req.destroy();
|
|
55
|
+
resolve(null);
|
|
56
|
+
});
|
|
57
|
+
req.end();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 比较版本号
|
|
62
|
+
* @returns 1 if v1 > v2, -1 if v1 < v2, 0 if equal
|
|
63
|
+
*/
|
|
64
|
+
export function compareVersions(v1, v2) {
|
|
65
|
+
const normalize = (v) => v.replace(/^v/, '').split('.').map(Number);
|
|
66
|
+
const parts1 = normalize(v1);
|
|
67
|
+
const parts2 = normalize(v2);
|
|
68
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
69
|
+
const p1 = parts1[i] || 0;
|
|
70
|
+
const p2 = parts2[i] || 0;
|
|
71
|
+
if (p1 > p2)
|
|
72
|
+
return 1;
|
|
73
|
+
if (p1 < p2)
|
|
74
|
+
return -1;
|
|
75
|
+
}
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 检测当前平台
|
|
80
|
+
*/
|
|
81
|
+
export function detectPlatform() {
|
|
82
|
+
const platform = os.platform();
|
|
83
|
+
const arch = os.arch();
|
|
84
|
+
if (platform === 'darwin') {
|
|
85
|
+
if (arch === 'arm64') {
|
|
86
|
+
return { os: 'darwin', arch: 'arm64', artifact: 'pls-darwin-arm64' };
|
|
87
|
+
}
|
|
88
|
+
else if (arch === 'x64') {
|
|
89
|
+
return { os: 'darwin', arch: 'x64', artifact: 'pls-darwin-x64' };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (platform === 'linux') {
|
|
93
|
+
if (arch === 'arm64') {
|
|
94
|
+
return { os: 'linux', arch: 'arm64', artifact: 'pls-linux-arm64' };
|
|
95
|
+
}
|
|
96
|
+
else if (arch === 'x64') {
|
|
97
|
+
return { os: 'linux', arch: 'x64', artifact: 'pls-linux-x64' };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else if (platform === 'win32') {
|
|
101
|
+
if (arch === 'x64') {
|
|
102
|
+
return { os: 'windows', arch: 'x64', artifact: 'pls-windows-x64.exe' };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 获取当前可执行文件路径
|
|
109
|
+
*/
|
|
110
|
+
export function getCurrentExecutablePath() {
|
|
111
|
+
return process.execPath;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 读取更新检查缓存
|
|
115
|
+
*/
|
|
116
|
+
function readUpdateCache() {
|
|
117
|
+
try {
|
|
118
|
+
if (fs.existsSync(UPDATE_CHECK_FILE)) {
|
|
119
|
+
const data = fs.readFileSync(UPDATE_CHECK_FILE, 'utf-8');
|
|
120
|
+
return JSON.parse(data);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// 忽略错误
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 写入更新检查缓存
|
|
130
|
+
*/
|
|
131
|
+
function writeUpdateCache(cache) {
|
|
132
|
+
try {
|
|
133
|
+
const dir = path.dirname(UPDATE_CHECK_FILE);
|
|
134
|
+
if (!fs.existsSync(dir)) {
|
|
135
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
fs.writeFileSync(UPDATE_CHECK_FILE, JSON.stringify(cache, null, 2));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// 忽略错误
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 检查是否有新版本(带缓存)
|
|
145
|
+
*/
|
|
146
|
+
export async function checkForUpdates(currentVersion, force = false) {
|
|
147
|
+
const cache = readUpdateCache();
|
|
148
|
+
const now = Date.now();
|
|
149
|
+
// 如果不是强制检查,且缓存有效,使用缓存
|
|
150
|
+
if (!force && cache && now - cache.lastCheck < CHECK_INTERVAL) {
|
|
151
|
+
if (cache.latestVersion) {
|
|
152
|
+
const hasUpdate = compareVersions(cache.latestVersion, currentVersion) > 0;
|
|
153
|
+
return { hasUpdate, latestVersion: cache.latestVersion };
|
|
154
|
+
}
|
|
155
|
+
return { hasUpdate: false, latestVersion: null };
|
|
156
|
+
}
|
|
157
|
+
// 获取最新版本
|
|
158
|
+
const latestVersion = await getLatestVersion();
|
|
159
|
+
// 更新缓存
|
|
160
|
+
writeUpdateCache({ lastCheck: now, latestVersion });
|
|
161
|
+
if (latestVersion) {
|
|
162
|
+
const hasUpdate = compareVersions(latestVersion, currentVersion) > 0;
|
|
163
|
+
return { hasUpdate, latestVersion };
|
|
164
|
+
}
|
|
165
|
+
return { hasUpdate: false, latestVersion: null };
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 显示更新提示
|
|
169
|
+
*/
|
|
170
|
+
export function showUpdateNotice(currentVersion, latestVersion) {
|
|
171
|
+
const colors = getColors();
|
|
172
|
+
// 使用简洁的单行提示,避免复杂的对齐问题
|
|
173
|
+
console.log('');
|
|
174
|
+
console2.warning(`发现新版本: ${currentVersion} → ${chalk.hex(colors.success)(latestVersion)},运行 ${chalk.hex(colors.primary)('pls upgrade')} 更新`);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 下载文件(使用 curl,支持代理)
|
|
178
|
+
*/
|
|
179
|
+
function downloadFile(url, dest, onProgress) {
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
// 使用 curl 下载,支持代理和进度显示
|
|
182
|
+
const args = ['-fSL', '--progress-bar', '-o', dest, url];
|
|
183
|
+
const curl = spawn('curl', args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
184
|
+
let lastPercent = 0;
|
|
185
|
+
// curl 进度输出在 stderr
|
|
186
|
+
curl.stderr?.on('data', (data) => {
|
|
187
|
+
const str = data.toString();
|
|
188
|
+
// 解析 curl 进度条输出,格式如: "### 6.2%"
|
|
189
|
+
const match = str.match(/(\d+\.?\d*)%/);
|
|
190
|
+
if (match && onProgress) {
|
|
191
|
+
const percent = Math.round(parseFloat(match[1]));
|
|
192
|
+
if (percent > lastPercent) {
|
|
193
|
+
lastPercent = percent;
|
|
194
|
+
onProgress(percent);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
curl.on('close', (code) => {
|
|
199
|
+
if (code === 0) {
|
|
200
|
+
resolve();
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
reject(new Error(`curl 退出码: ${code}`));
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
curl.on('error', (err) => {
|
|
207
|
+
reject(err);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 检测是否是 Bun 编译的二进制
|
|
213
|
+
*/
|
|
214
|
+
export function isBunBinary() {
|
|
215
|
+
const execPath = process.execPath.toLowerCase();
|
|
216
|
+
// npm/node 运行时,execPath 会包含 node
|
|
217
|
+
// tsx 开发时,execPath 会包含 node 或 tsx
|
|
218
|
+
// Bun 编译的二进制,execPath 就是程序自己的路径
|
|
219
|
+
return !execPath.includes('node') && !execPath.includes('bun');
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* 执行升级
|
|
223
|
+
*/
|
|
224
|
+
export async function performUpgrade(currentVersion) {
|
|
225
|
+
console.log('');
|
|
226
|
+
console2.title('🚀 Pretty-Please 升级');
|
|
227
|
+
console2.muted('━'.repeat(40));
|
|
228
|
+
// 检测平台
|
|
229
|
+
console2.info('检测系统平台...');
|
|
230
|
+
const platform = detectPlatform();
|
|
231
|
+
if (!platform) {
|
|
232
|
+
console2.error('不支持的平台');
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
console2.success(`平台: ${platform.os} ${platform.arch}`);
|
|
236
|
+
// 获取最新版本
|
|
237
|
+
console2.info('获取最新版本...');
|
|
238
|
+
const latestVersion = await getLatestVersion();
|
|
239
|
+
if (!latestVersion) {
|
|
240
|
+
console2.error('无法获取最新版本');
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
// 比较版本
|
|
244
|
+
if (compareVersions(latestVersion, currentVersion) <= 0) {
|
|
245
|
+
console2.success(`当前已是最新版本 (${currentVersion})`);
|
|
246
|
+
console.log('');
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
console2.success(`发现新版本: ${currentVersion} → ${latestVersion}`);
|
|
250
|
+
// 检查安装方式
|
|
251
|
+
if (!isBunBinary()) {
|
|
252
|
+
// 如果是通过 npm/node 运行的,提示使用 npm 更新
|
|
253
|
+
console.log('');
|
|
254
|
+
console2.warning('检测到你是通过 npm 安装的,请使用以下命令更新:');
|
|
255
|
+
console.log('');
|
|
256
|
+
console.log(chalk.hex(getColors().primary)(' npm update -g @yivan-lab/pretty-please'));
|
|
257
|
+
console.log('');
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
// 获取当前可执行文件路径
|
|
261
|
+
const execPath = getCurrentExecutablePath();
|
|
262
|
+
console2.info(`当前程序: ${execPath}`);
|
|
263
|
+
// 下载新版本
|
|
264
|
+
const downloadUrl = `https://github.com/${REPO}/releases/download/${latestVersion}/${platform.artifact}`;
|
|
265
|
+
const tempFile = path.join(os.tmpdir(), `pls-upgrade-${Date.now()}`);
|
|
266
|
+
console2.info('下载中...');
|
|
267
|
+
try {
|
|
268
|
+
let lastPercent = 0;
|
|
269
|
+
await downloadFile(downloadUrl, tempFile, (percent) => {
|
|
270
|
+
if (percent - lastPercent >= 10 || percent === 100) {
|
|
271
|
+
process.stdout.write(`\r${chalk.hex(getCurrentTheme().primary)('[INFO]')} 下载中... ${percent}%`);
|
|
272
|
+
lastPercent = percent;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
console.log(''); // 换行
|
|
276
|
+
console2.success('下载完成');
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
console2.error(`下载失败: ${err.message}`);
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
// 替换当前程序
|
|
283
|
+
console2.info('安装新版本...');
|
|
284
|
+
try {
|
|
285
|
+
// 设置可执行权限
|
|
286
|
+
fs.chmodSync(tempFile, 0o755);
|
|
287
|
+
// 备份旧版本
|
|
288
|
+
const backupPath = `${execPath}.backup`;
|
|
289
|
+
if (fs.existsSync(backupPath)) {
|
|
290
|
+
fs.unlinkSync(backupPath);
|
|
291
|
+
}
|
|
292
|
+
// Windows 需要特殊处理
|
|
293
|
+
if (platform.os === 'windows') {
|
|
294
|
+
// Windows 上无法替换正在运行的程序,创建一个批处理脚本
|
|
295
|
+
const batchScript = `@echo off
|
|
296
|
+
timeout /t 1 /nobreak >nul
|
|
297
|
+
move /y "${execPath}" "${backupPath}" >nul
|
|
298
|
+
move /y "${tempFile}" "${execPath}" >nul
|
|
299
|
+
del "${backupPath}" >nul 2>&1
|
|
300
|
+
echo.
|
|
301
|
+
echo 升级完成! ${currentVersion} → ${latestVersion}
|
|
302
|
+
echo.
|
|
303
|
+
pause
|
|
304
|
+
`;
|
|
305
|
+
const batchPath = path.join(os.tmpdir(), 'pls-upgrade.bat');
|
|
306
|
+
fs.writeFileSync(batchPath, batchScript);
|
|
307
|
+
console.log('');
|
|
308
|
+
console2.warning('Windows 上需要额外步骤完成升级:');
|
|
309
|
+
console.log('');
|
|
310
|
+
console.log(chalk.hex(getColors().primary)(` 请运行: ${batchPath}`));
|
|
311
|
+
console.log('');
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
// Unix 系统:直接替换
|
|
315
|
+
fs.renameSync(execPath, backupPath);
|
|
316
|
+
fs.renameSync(tempFile, execPath);
|
|
317
|
+
// 删除备份
|
|
318
|
+
try {
|
|
319
|
+
fs.unlinkSync(backupPath);
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
// 忽略删除备份失败
|
|
323
|
+
}
|
|
324
|
+
console2.muted('━'.repeat(40));
|
|
325
|
+
console2.success(`升级成功: ${currentVersion} → ${latestVersion}`);
|
|
326
|
+
console.log('');
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
catch (err) {
|
|
330
|
+
console2.error(`安装失败: ${err.message}`);
|
|
331
|
+
// 尝试清理
|
|
332
|
+
try {
|
|
333
|
+
if (fs.existsSync(tempFile)) {
|
|
334
|
+
fs.unlinkSync(tempFile);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch { }
|
|
338
|
+
// 如果是权限问题,提示使用 sudo
|
|
339
|
+
if (err.code === 'EACCES' || err.code === 'EPERM') {
|
|
340
|
+
console.log('');
|
|
341
|
+
console2.warning('权限不足,请尝试使用 sudo:');
|
|
342
|
+
console.log('');
|
|
343
|
+
console.log(chalk.hex(getColors().primary)(' sudo pls upgrade'));
|
|
344
|
+
console.log('');
|
|
345
|
+
}
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
@@ -1,44 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 计算字符串的显示宽度(中文占2个宽度)
|
|
3
3
|
*/
|
|
4
|
-
export function getDisplayWidth(str:
|
|
4
|
+
export declare function getDisplayWidth(str: string): number;
|
|
5
5
|
/**
|
|
6
6
|
* 绘制命令框(原生版本)
|
|
7
7
|
*/
|
|
8
|
-
export function drawCommandBox(command:
|
|
8
|
+
export declare function drawCommandBox(command: string, title?: string): void;
|
|
9
9
|
/**
|
|
10
10
|
* 格式化耗时
|
|
11
11
|
*/
|
|
12
|
-
export function formatDuration(ms:
|
|
12
|
+
export declare function formatDuration(ms: number): string;
|
|
13
13
|
/**
|
|
14
14
|
* 输出分隔线
|
|
15
15
|
*/
|
|
16
|
-
export function printSeparator(text?: string, length?: number): void;
|
|
16
|
+
export declare function printSeparator(text?: string, length?: number): void;
|
|
17
17
|
/**
|
|
18
18
|
* 输出成功消息
|
|
19
19
|
*/
|
|
20
|
-
export function success(message:
|
|
20
|
+
export declare function success(message: string): void;
|
|
21
21
|
/**
|
|
22
22
|
* 输出错误消息
|
|
23
23
|
*/
|
|
24
|
-
export function error(message:
|
|
24
|
+
export declare function error(message: string): void;
|
|
25
25
|
/**
|
|
26
26
|
* 输出警告消息
|
|
27
27
|
*/
|
|
28
|
-
export function warning(message:
|
|
28
|
+
export declare function warning(message: string): void;
|
|
29
29
|
/**
|
|
30
30
|
* 输出信息消息
|
|
31
31
|
*/
|
|
32
|
-
export function info(message:
|
|
32
|
+
export declare function info(message: string): void;
|
|
33
33
|
/**
|
|
34
34
|
* 输出灰色文本
|
|
35
35
|
*/
|
|
36
|
-
export function muted(message:
|
|
36
|
+
export declare function muted(message: string): void;
|
|
37
37
|
/**
|
|
38
38
|
* 输出标题
|
|
39
39
|
*/
|
|
40
|
-
export function title(message:
|
|
40
|
+
export declare function title(message: string): void;
|
|
41
41
|
/**
|
|
42
42
|
* 输出主色文本
|
|
43
43
|
*/
|
|
44
|
-
export function primary(message:
|
|
44
|
+
export declare function primary(message: string): void;
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
2
3
|
/**
|
|
3
4
|
* 原生控制台输出工具函数
|
|
4
5
|
* 用于不需要 Ink 的场景,避免清屏和性能问题
|
|
5
6
|
*/
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
// 获取当前主题颜色
|
|
8
|
+
function getColors() {
|
|
9
|
+
const theme = getCurrentTheme();
|
|
10
|
+
return {
|
|
11
|
+
primary: theme.primary,
|
|
12
|
+
secondary: theme.secondary,
|
|
13
|
+
accent: theme.accent,
|
|
14
|
+
success: theme.success,
|
|
15
|
+
error: theme.error,
|
|
16
|
+
warning: theme.warning,
|
|
17
|
+
info: theme.info,
|
|
18
|
+
muted: theme.text.muted,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
17
21
|
/**
|
|
18
22
|
* 计算字符串的显示宽度(中文占2个宽度)
|
|
19
23
|
*/
|
|
@@ -33,9 +37,10 @@ export function getDisplayWidth(str) {
|
|
|
33
37
|
* 绘制命令框(原生版本)
|
|
34
38
|
*/
|
|
35
39
|
export function drawCommandBox(command, title = '生成命令') {
|
|
40
|
+
const colors = getColors();
|
|
36
41
|
const lines = command.split('\n');
|
|
37
42
|
const titleWidth = getDisplayWidth(title);
|
|
38
|
-
const maxContentWidth = Math.max(...lines.map(l => getDisplayWidth(l)));
|
|
43
|
+
const maxContentWidth = Math.max(...lines.map((l) => getDisplayWidth(l)));
|
|
39
44
|
const boxWidth = Math.max(maxContentWidth + 4, titleWidth + 6, 20);
|
|
40
45
|
const topPadding = boxWidth - titleWidth - 5;
|
|
41
46
|
const topBorder = '┌─ ' + title + ' ' + '─'.repeat(topPadding) + '┐';
|
|
@@ -44,10 +49,7 @@ export function drawCommandBox(command, title = '生成命令') {
|
|
|
44
49
|
for (const line of lines) {
|
|
45
50
|
const lineWidth = getDisplayWidth(line);
|
|
46
51
|
const padding = ' '.repeat(boxWidth - lineWidth - 4);
|
|
47
|
-
console.log(chalk.hex(colors.warning)('│ ') +
|
|
48
|
-
chalk.hex(colors.primary)(line) +
|
|
49
|
-
padding +
|
|
50
|
-
chalk.hex(colors.warning)(' │'));
|
|
52
|
+
console.log(chalk.hex(colors.warning)('│ ') + chalk.hex(colors.primary)(line) + padding + chalk.hex(colors.warning)(' │'));
|
|
51
53
|
}
|
|
52
54
|
console.log(chalk.hex(colors.warning)(bottomBorder));
|
|
53
55
|
}
|
|
@@ -65,7 +67,8 @@ export function formatDuration(ms) {
|
|
|
65
67
|
*/
|
|
66
68
|
export function printSeparator(text = '输出', length = 38) {
|
|
67
69
|
const textPart = text ? ` ${text} ` : '';
|
|
68
|
-
const
|
|
70
|
+
const textWidth = getDisplayWidth(textPart); // 使用显示宽度而不是字符数
|
|
71
|
+
const lineLength = Math.max(0, length - textWidth);
|
|
69
72
|
const leftDashes = '─'.repeat(Math.floor(lineLength / 2));
|
|
70
73
|
const rightDashes = '─'.repeat(Math.ceil(lineLength / 2));
|
|
71
74
|
console.log(chalk.gray(`${leftDashes}${textPart}${rightDashes}`));
|
|
@@ -74,30 +77,35 @@ export function printSeparator(text = '输出', length = 38) {
|
|
|
74
77
|
* 输出成功消息
|
|
75
78
|
*/
|
|
76
79
|
export function success(message) {
|
|
80
|
+
const colors = getColors();
|
|
77
81
|
console.log(chalk.hex(colors.success)('✓ ' + message));
|
|
78
82
|
}
|
|
79
83
|
/**
|
|
80
84
|
* 输出错误消息
|
|
81
85
|
*/
|
|
82
86
|
export function error(message) {
|
|
87
|
+
const colors = getColors();
|
|
83
88
|
console.log(chalk.hex(colors.error)('✗ ' + message));
|
|
84
89
|
}
|
|
85
90
|
/**
|
|
86
91
|
* 输出警告消息
|
|
87
92
|
*/
|
|
88
93
|
export function warning(message) {
|
|
94
|
+
const colors = getColors();
|
|
89
95
|
console.log(chalk.hex(colors.warning)('⚠️ ' + message));
|
|
90
96
|
}
|
|
91
97
|
/**
|
|
92
98
|
* 输出信息消息
|
|
93
99
|
*/
|
|
94
100
|
export function info(message) {
|
|
101
|
+
const colors = getColors();
|
|
95
102
|
console.log(chalk.hex(colors.info)(message));
|
|
96
103
|
}
|
|
97
104
|
/**
|
|
98
105
|
* 输出灰色文本
|
|
99
106
|
*/
|
|
100
107
|
export function muted(message) {
|
|
108
|
+
const colors = getColors();
|
|
101
109
|
console.log(chalk.hex(colors.muted)(message));
|
|
102
110
|
}
|
|
103
111
|
/**
|
|
@@ -110,5 +118,6 @@ export function title(message) {
|
|
|
110
118
|
* 输出主色文本
|
|
111
119
|
*/
|
|
112
120
|
export function primary(message) {
|
|
121
|
+
const colors = getColors();
|
|
113
122
|
console.log(chalk.hex(colors.primary)(message));
|
|
114
123
|
}
|