@yivan-lab/pretty-please 1.1.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 +283 -1
- package/bin/pls.tsx +1022 -104
- package/dist/bin/pls.js +894 -84
- package/dist/package.json +4 -4
- package/dist/src/alias.d.ts +41 -0
- package/dist/src/alias.js +240 -0
- package/dist/src/chat-history.js +10 -1
- package/dist/src/components/Chat.js +2 -1
- package/dist/src/components/CodeColorizer.js +26 -20
- package/dist/src/components/CommandBox.js +2 -1
- package/dist/src/components/ConfirmationPrompt.js +2 -1
- 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 +3 -1
- package/dist/src/components/MultiStepCommandGenerator.js +20 -10
- package/dist/src/components/TableRenderer.js +2 -1
- package/dist/src/config.d.ts +34 -3
- package/dist/src/config.js +71 -31
- package/dist/src/multi-step.d.ts +22 -6
- package/dist/src/multi-step.js +27 -4
- 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 +53 -0
- package/dist/src/shell-hook.js +242 -19
- 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.js +22 -11
- package/package.json +4 -4
- package/src/alias.ts +301 -0
- package/src/chat-history.ts +11 -1
- package/src/components/Chat.tsx +2 -1
- package/src/components/CodeColorizer.tsx +27 -19
- package/src/components/CommandBox.tsx +2 -1
- package/src/components/ConfirmationPrompt.tsx +2 -1
- 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 +25 -11
- package/src/components/TableRenderer.tsx +2 -1
- package/src/config.ts +117 -32
- package/src/multi-step.ts +43 -6
- package/src/remote-history.ts +390 -0
- package/src/remote.ts +800 -0
- package/src/shell-hook.ts +271 -19
- package/src/ui/theme.ts +101 -24
- package/src/upgrade.ts +397 -0
- package/src/utils/console.ts +22 -11
package/dist/src/shell-hook.js
CHANGED
|
@@ -4,6 +4,18 @@ import os from 'os';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { CONFIG_DIR, getConfig, setConfigValue } from './config.js';
|
|
6
6
|
import { getHistory } from './history.js';
|
|
7
|
+
import { getCurrentTheme } from './ui/theme.js';
|
|
8
|
+
// 获取主题颜色
|
|
9
|
+
function getColors() {
|
|
10
|
+
const theme = getCurrentTheme();
|
|
11
|
+
return {
|
|
12
|
+
primary: theme.primary,
|
|
13
|
+
success: theme.success,
|
|
14
|
+
error: theme.error,
|
|
15
|
+
warning: theme.warning,
|
|
16
|
+
secondary: theme.secondary,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
7
19
|
const SHELL_HISTORY_FILE = path.join(CONFIG_DIR, 'shell_history.jsonl');
|
|
8
20
|
const MAX_SHELL_HISTORY = 20;
|
|
9
21
|
// Hook 标记,用于识别我们添加的内容
|
|
@@ -157,20 +169,21 @@ function generateHookScript(shellType) {
|
|
|
157
169
|
export async function installShellHook() {
|
|
158
170
|
const shellType = detectShell();
|
|
159
171
|
const configPath = getShellConfigPath(shellType);
|
|
172
|
+
const colors = getColors();
|
|
160
173
|
if (!configPath) {
|
|
161
|
-
console.log(chalk.
|
|
174
|
+
console.log(chalk.hex(colors.error)(`❌ 不支持的 shell 类型: ${shellType}`));
|
|
162
175
|
return false;
|
|
163
176
|
}
|
|
164
177
|
const hookScript = generateHookScript(shellType);
|
|
165
178
|
if (!hookScript) {
|
|
166
|
-
console.log(chalk.
|
|
179
|
+
console.log(chalk.hex(colors.error)(`❌ 无法为 ${shellType} 生成 hook 脚本`));
|
|
167
180
|
return false;
|
|
168
181
|
}
|
|
169
182
|
// 检查是否已安装
|
|
170
183
|
if (fs.existsSync(configPath)) {
|
|
171
184
|
const content = fs.readFileSync(configPath, 'utf-8');
|
|
172
185
|
if (content.includes(HOOK_START_MARKER)) {
|
|
173
|
-
console.log(chalk.
|
|
186
|
+
console.log(chalk.hex(colors.warning)('⚠️ Shell hook 已安装,跳过'));
|
|
174
187
|
setConfigValue('shellHook', true);
|
|
175
188
|
return true;
|
|
176
189
|
}
|
|
@@ -189,9 +202,9 @@ export async function installShellHook() {
|
|
|
189
202
|
fs.appendFileSync(configPath, hookScript);
|
|
190
203
|
// 更新配置
|
|
191
204
|
setConfigValue('shellHook', true);
|
|
192
|
-
console.log(chalk.
|
|
193
|
-
console.log(chalk.
|
|
194
|
-
console.log(chalk.
|
|
205
|
+
console.log(chalk.hex(colors.success)(`✅ Shell hook 已安装到: ${configPath}`));
|
|
206
|
+
console.log(chalk.hex(colors.warning)('⚠️ 请重启终端或执行以下命令使其生效:'));
|
|
207
|
+
console.log(chalk.hex(colors.primary)(` source ${configPath}`));
|
|
195
208
|
return true;
|
|
196
209
|
}
|
|
197
210
|
/**
|
|
@@ -200,8 +213,9 @@ export async function installShellHook() {
|
|
|
200
213
|
export function uninstallShellHook() {
|
|
201
214
|
const shellType = detectShell();
|
|
202
215
|
const configPath = getShellConfigPath(shellType);
|
|
216
|
+
const colors = getColors();
|
|
203
217
|
if (!configPath || !fs.existsSync(configPath)) {
|
|
204
|
-
console.log(chalk.
|
|
218
|
+
console.log(chalk.hex(colors.warning)('⚠️ 未找到 shell 配置文件'));
|
|
205
219
|
setConfigValue('shellHook', false);
|
|
206
220
|
return true;
|
|
207
221
|
}
|
|
@@ -210,7 +224,7 @@ export function uninstallShellHook() {
|
|
|
210
224
|
const startIndex = content.indexOf(HOOK_START_MARKER);
|
|
211
225
|
const endIndex = content.indexOf(HOOK_END_MARKER);
|
|
212
226
|
if (startIndex === -1 || endIndex === -1) {
|
|
213
|
-
console.log(chalk.
|
|
227
|
+
console.log(chalk.hex(colors.warning)('⚠️ 未找到已安装的 hook'));
|
|
214
228
|
setConfigValue('shellHook', false);
|
|
215
229
|
return true;
|
|
216
230
|
}
|
|
@@ -224,8 +238,8 @@ export function uninstallShellHook() {
|
|
|
224
238
|
if (fs.existsSync(SHELL_HISTORY_FILE)) {
|
|
225
239
|
fs.unlinkSync(SHELL_HISTORY_FILE);
|
|
226
240
|
}
|
|
227
|
-
console.log(chalk.
|
|
228
|
-
console.log(chalk.
|
|
241
|
+
console.log(chalk.hex(colors.success)('✅ Shell hook 已卸载'));
|
|
242
|
+
console.log(chalk.hex(colors.warning)('⚠️ 请重启终端使其生效'));
|
|
229
243
|
return true;
|
|
230
244
|
}
|
|
231
245
|
/**
|
|
@@ -361,10 +375,11 @@ export function getHookStatus() {
|
|
|
361
375
|
export function displayShellHistory() {
|
|
362
376
|
const config = getConfig();
|
|
363
377
|
const history = getShellHistory();
|
|
378
|
+
const colors = getColors();
|
|
364
379
|
if (!config.shellHook) {
|
|
365
380
|
console.log('');
|
|
366
|
-
console.log(chalk.
|
|
367
|
-
console.log(chalk.gray('运行 ') + chalk.
|
|
381
|
+
console.log(chalk.hex(colors.warning)('⚠️ Shell Hook 未启用'));
|
|
382
|
+
console.log(chalk.gray('运行 ') + chalk.hex(colors.primary)('pls hook install') + chalk.gray(' 启用 Shell Hook'));
|
|
368
383
|
console.log('');
|
|
369
384
|
return;
|
|
370
385
|
}
|
|
@@ -379,7 +394,7 @@ export function displayShellHistory() {
|
|
|
379
394
|
console.log(chalk.gray('━'.repeat(50)));
|
|
380
395
|
history.forEach((item, index) => {
|
|
381
396
|
const num = index + 1;
|
|
382
|
-
const status = item.exit === 0 ? chalk.
|
|
397
|
+
const status = item.exit === 0 ? chalk.hex(colors.success)('✓') : chalk.hex(colors.error)(`✗ (${item.exit})`);
|
|
383
398
|
// 检查是否是 pls 命令
|
|
384
399
|
const isPls = item.cmd.startsWith('pls ') || item.cmd.startsWith('please ');
|
|
385
400
|
if (isPls) {
|
|
@@ -389,20 +404,20 @@ export function displayShellHistory() {
|
|
|
389
404
|
if (plsRecord && plsRecord.executed) {
|
|
390
405
|
// 检查用户是否修改了命令
|
|
391
406
|
if (plsRecord.userModified && plsRecord.aiGeneratedCommand) {
|
|
392
|
-
console.log(` ${chalk.
|
|
407
|
+
console.log(` ${chalk.hex(colors.primary)(num.toString().padStart(2, ' '))}. ${chalk.hex(colors.secondary)('[pls]')} "${args}"`);
|
|
393
408
|
console.log(` ${chalk.dim('AI 生成:')} ${chalk.gray(plsRecord.aiGeneratedCommand)}`);
|
|
394
|
-
console.log(` ${chalk.dim('用户修改为:')} ${plsRecord.command} ${status} ${chalk.
|
|
409
|
+
console.log(` ${chalk.dim('用户修改为:')} ${plsRecord.command} ${status} ${chalk.hex(colors.warning)('(已修改)')}`);
|
|
395
410
|
}
|
|
396
411
|
else {
|
|
397
|
-
console.log(` ${chalk.
|
|
412
|
+
console.log(` ${chalk.hex(colors.primary)(num.toString().padStart(2, ' '))}. ${chalk.hex(colors.secondary)('[pls]')} "${args}" → ${plsRecord.command} ${status}`);
|
|
398
413
|
}
|
|
399
414
|
}
|
|
400
415
|
else {
|
|
401
|
-
console.log(` ${chalk.
|
|
416
|
+
console.log(` ${chalk.hex(colors.primary)(num.toString().padStart(2, ' '))}. ${chalk.hex(colors.secondary)('[pls]')} ${args} ${status}`);
|
|
402
417
|
}
|
|
403
418
|
}
|
|
404
419
|
else {
|
|
405
|
-
console.log(` ${chalk.
|
|
420
|
+
console.log(` ${chalk.hex(colors.primary)(num.toString().padStart(2, ' '))}. ${item.cmd} ${status}`);
|
|
406
421
|
}
|
|
407
422
|
});
|
|
408
423
|
console.log(chalk.gray('━'.repeat(50)));
|
|
@@ -417,7 +432,215 @@ export function clearShellHistory() {
|
|
|
417
432
|
if (fs.existsSync(SHELL_HISTORY_FILE)) {
|
|
418
433
|
fs.unlinkSync(SHELL_HISTORY_FILE);
|
|
419
434
|
}
|
|
435
|
+
const colors = getColors();
|
|
420
436
|
console.log('');
|
|
421
|
-
console.log(chalk.
|
|
437
|
+
console.log(chalk.hex(colors.success)('✓ Shell 历史已清空'));
|
|
422
438
|
console.log('');
|
|
423
439
|
}
|
|
440
|
+
// ================== 远程 Shell Hook ==================
|
|
441
|
+
/**
|
|
442
|
+
* 生成远程 zsh hook 脚本
|
|
443
|
+
*/
|
|
444
|
+
function generateRemoteZshHook() {
|
|
445
|
+
return `
|
|
446
|
+
${HOOK_START_MARKER}
|
|
447
|
+
# 记录命令到 pretty-please 历史
|
|
448
|
+
__pls_preexec() {
|
|
449
|
+
__PLS_LAST_CMD="$1"
|
|
450
|
+
__PLS_CMD_START=$(date +%s)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
__pls_precmd() {
|
|
454
|
+
local exit_code=$?
|
|
455
|
+
if [[ -n "$__PLS_LAST_CMD" ]]; then
|
|
456
|
+
local end_time=$(date +%s)
|
|
457
|
+
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
458
|
+
# 确保目录存在
|
|
459
|
+
mkdir -p ~/.please
|
|
460
|
+
# 转义命令中的特殊字符
|
|
461
|
+
local escaped_cmd=$(echo "$__PLS_LAST_CMD" | sed 's/\\\\/\\\\\\\\/g; s/"/\\\\"/g')
|
|
462
|
+
echo "{\\"cmd\\":\\"$escaped_cmd\\",\\"exit\\":$exit_code,\\"time\\":\\"$timestamp\\"}" >> ~/.please/shell_history.jsonl
|
|
463
|
+
# 保持文件不超过 50 行
|
|
464
|
+
tail -n 50 ~/.please/shell_history.jsonl > ~/.please/shell_history.jsonl.tmp && mv ~/.please/shell_history.jsonl.tmp ~/.please/shell_history.jsonl
|
|
465
|
+
unset __PLS_LAST_CMD
|
|
466
|
+
fi
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
autoload -Uz add-zsh-hook
|
|
470
|
+
add-zsh-hook preexec __pls_preexec
|
|
471
|
+
add-zsh-hook precmd __pls_precmd
|
|
472
|
+
${HOOK_END_MARKER}
|
|
473
|
+
`;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* 生成远程 bash hook 脚本
|
|
477
|
+
*/
|
|
478
|
+
function generateRemoteBashHook() {
|
|
479
|
+
return `
|
|
480
|
+
${HOOK_START_MARKER}
|
|
481
|
+
# 记录命令到 pretty-please 历史
|
|
482
|
+
__pls_prompt_command() {
|
|
483
|
+
local exit_code=$?
|
|
484
|
+
local last_cmd=$(history 1 | sed 's/^ *[0-9]* *//')
|
|
485
|
+
if [[ -n "$last_cmd" && "$last_cmd" != "$__PLS_LAST_CMD" ]]; then
|
|
486
|
+
__PLS_LAST_CMD="$last_cmd"
|
|
487
|
+
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
488
|
+
# 确保目录存在
|
|
489
|
+
mkdir -p ~/.please
|
|
490
|
+
local escaped_cmd=$(echo "$last_cmd" | sed 's/\\\\/\\\\\\\\/g; s/"/\\\\"/g')
|
|
491
|
+
echo "{\\"cmd\\":\\"$escaped_cmd\\",\\"exit\\":$exit_code,\\"time\\":\\"$timestamp\\"}" >> ~/.please/shell_history.jsonl
|
|
492
|
+
tail -n 50 ~/.please/shell_history.jsonl > ~/.please/shell_history.jsonl.tmp && mv ~/.please/shell_history.jsonl.tmp ~/.please/shell_history.jsonl
|
|
493
|
+
fi
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if [[ ! "$PROMPT_COMMAND" =~ __pls_prompt_command ]]; then
|
|
497
|
+
PROMPT_COMMAND="__pls_prompt_command;\${PROMPT_COMMAND}"
|
|
498
|
+
fi
|
|
499
|
+
${HOOK_END_MARKER}
|
|
500
|
+
`;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* 检测远程服务器的 shell 类型
|
|
504
|
+
*/
|
|
505
|
+
export async function detectRemoteShell(sshExecFn) {
|
|
506
|
+
try {
|
|
507
|
+
const result = await sshExecFn('basename "$SHELL"');
|
|
508
|
+
if (result.exitCode === 0) {
|
|
509
|
+
const shell = result.stdout.trim();
|
|
510
|
+
if (shell === 'zsh')
|
|
511
|
+
return 'zsh';
|
|
512
|
+
if (shell === 'bash')
|
|
513
|
+
return 'bash';
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
catch {
|
|
517
|
+
// 忽略错误
|
|
518
|
+
}
|
|
519
|
+
return 'bash'; // 默认 bash
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* 获取远程 shell 配置文件路径
|
|
523
|
+
*/
|
|
524
|
+
export function getRemoteShellConfigPath(shellType) {
|
|
525
|
+
switch (shellType) {
|
|
526
|
+
case 'zsh':
|
|
527
|
+
return '~/.zshrc';
|
|
528
|
+
case 'bash':
|
|
529
|
+
return '~/.bashrc';
|
|
530
|
+
default:
|
|
531
|
+
return '~/.bashrc';
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* 生成远程 hook 脚本
|
|
536
|
+
*/
|
|
537
|
+
export function generateRemoteHookScript(shellType) {
|
|
538
|
+
switch (shellType) {
|
|
539
|
+
case 'zsh':
|
|
540
|
+
return generateRemoteZshHook();
|
|
541
|
+
case 'bash':
|
|
542
|
+
return generateRemoteBashHook();
|
|
543
|
+
default:
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* 检查远程 hook 是否已安装
|
|
549
|
+
*/
|
|
550
|
+
export async function checkRemoteHookInstalled(sshExecFn, configPath) {
|
|
551
|
+
try {
|
|
552
|
+
const result = await sshExecFn(`grep -q "${HOOK_START_MARKER}" ${configPath} 2>/dev/null && echo "installed" || echo "not_installed"`);
|
|
553
|
+
return result.stdout.trim() === 'installed';
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* 在远程服务器安装 shell hook
|
|
561
|
+
*/
|
|
562
|
+
export async function installRemoteShellHook(sshExecFn, shellType) {
|
|
563
|
+
const colors = getColors();
|
|
564
|
+
const configPath = getRemoteShellConfigPath(shellType);
|
|
565
|
+
const hookScript = generateRemoteHookScript(shellType);
|
|
566
|
+
if (!hookScript) {
|
|
567
|
+
return { success: false, message: chalk.hex(colors.error)(`不支持的 shell 类型: ${shellType}`) };
|
|
568
|
+
}
|
|
569
|
+
// 检查是否已安装
|
|
570
|
+
const installed = await checkRemoteHookInstalled(sshExecFn, configPath);
|
|
571
|
+
if (installed) {
|
|
572
|
+
return { success: true, message: chalk.hex(colors.warning)('Shell hook 已安装,跳过') };
|
|
573
|
+
}
|
|
574
|
+
// 备份原配置文件
|
|
575
|
+
try {
|
|
576
|
+
await sshExecFn(`cp ${configPath} ${configPath}.pls-backup 2>/dev/null || true`);
|
|
577
|
+
}
|
|
578
|
+
catch {
|
|
579
|
+
// 忽略备份错误
|
|
580
|
+
}
|
|
581
|
+
// 安装 hook
|
|
582
|
+
// 使用 cat 和 heredoc 来追加内容
|
|
583
|
+
const escapedScript = hookScript.replace(/'/g, "'\"'\"'");
|
|
584
|
+
const installCmd = `echo '${escapedScript}' >> ${configPath}`;
|
|
585
|
+
try {
|
|
586
|
+
const result = await sshExecFn(installCmd);
|
|
587
|
+
if (result.exitCode !== 0) {
|
|
588
|
+
return { success: false, message: chalk.hex(colors.error)(`安装失败: ${result.stdout}`) };
|
|
589
|
+
}
|
|
590
|
+
// 确保 ~/.please 目录存在
|
|
591
|
+
await sshExecFn('mkdir -p ~/.please');
|
|
592
|
+
return {
|
|
593
|
+
success: true,
|
|
594
|
+
message: chalk.hex(colors.success)(`Shell hook 已安装到 ${configPath}`),
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
catch (error) {
|
|
598
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
599
|
+
return { success: false, message: chalk.hex(colors.error)(`安装失败: ${message}`) };
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* 从远程服务器卸载 shell hook
|
|
604
|
+
*/
|
|
605
|
+
export async function uninstallRemoteShellHook(sshExecFn, shellType) {
|
|
606
|
+
const colors = getColors();
|
|
607
|
+
const configPath = getRemoteShellConfigPath(shellType);
|
|
608
|
+
// 检查是否已安装
|
|
609
|
+
const installed = await checkRemoteHookInstalled(sshExecFn, configPath);
|
|
610
|
+
if (!installed) {
|
|
611
|
+
return { success: true, message: chalk.hex(colors.warning)('Shell hook 未安装,跳过') };
|
|
612
|
+
}
|
|
613
|
+
// 使用 sed 删除 hook 代码块
|
|
614
|
+
// 注意:需要处理特殊字符
|
|
615
|
+
const startMarkerEscaped = HOOK_START_MARKER.replace(/[[\]]/g, '\\$&');
|
|
616
|
+
const endMarkerEscaped = HOOK_END_MARKER.replace(/[[\]]/g, '\\$&');
|
|
617
|
+
// 在 macOS 和 Linux 上 sed -i 行为不同,使用 sed + 临时文件
|
|
618
|
+
const uninstallCmd = `
|
|
619
|
+
sed '/${startMarkerEscaped}/,/${endMarkerEscaped}/d' ${configPath} > ${configPath}.tmp && mv ${configPath}.tmp ${configPath}
|
|
620
|
+
`;
|
|
621
|
+
try {
|
|
622
|
+
const result = await sshExecFn(uninstallCmd);
|
|
623
|
+
if (result.exitCode !== 0) {
|
|
624
|
+
return { success: false, message: chalk.hex(colors.error)(`卸载失败: ${result.stdout}`) };
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
success: true,
|
|
628
|
+
message: chalk.hex(colors.success)('Shell hook 已卸载'),
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
catch (error) {
|
|
632
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
633
|
+
return { success: false, message: chalk.hex(colors.error)(`卸载失败: ${message}`) };
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* 获取远程 hook 状态
|
|
638
|
+
*/
|
|
639
|
+
export async function getRemoteHookStatus(sshExecFn) {
|
|
640
|
+
// 检测 shell 类型
|
|
641
|
+
const shellType = await detectRemoteShell(sshExecFn);
|
|
642
|
+
const configPath = getRemoteShellConfigPath(shellType);
|
|
643
|
+
// 检查是否已安装
|
|
644
|
+
const installed = await checkRemoteHookInstalled(sshExecFn, configPath);
|
|
645
|
+
return { installed, shellType, configPath };
|
|
646
|
+
}
|
package/dist/src/ui/theme.d.ts
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
export type ThemeName = 'dark' | 'light';
|
|
2
|
+
export interface Theme {
|
|
3
|
+
primary: string;
|
|
4
|
+
secondary: string;
|
|
5
|
+
accent: string;
|
|
6
|
+
success: string;
|
|
7
|
+
error: string;
|
|
8
|
+
warning: string;
|
|
9
|
+
info: string;
|
|
10
|
+
text: {
|
|
11
|
+
primary: string;
|
|
12
|
+
secondary: string;
|
|
13
|
+
muted: string;
|
|
14
|
+
dim: string;
|
|
14
15
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
border: string;
|
|
17
|
+
divider: string;
|
|
18
|
+
code: {
|
|
19
|
+
background: string;
|
|
20
|
+
text: string;
|
|
21
|
+
keyword: string;
|
|
22
|
+
string: string;
|
|
23
|
+
function: string;
|
|
24
|
+
comment: string;
|
|
24
25
|
};
|
|
25
|
-
}
|
|
26
|
-
export
|
|
26
|
+
}
|
|
27
|
+
export declare const themes: Record<ThemeName, Theme>;
|
|
28
|
+
export declare function getCurrentTheme(): Theme;
|
|
29
|
+
export declare const theme: Theme;
|
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>;
|