@cloud411716/fancy-webnovel 0.1.13 → 0.1.15
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/package.json +1 -1
- package/plugins/fancy-bootstrap/index.js +65 -34
package/package.json
CHANGED
|
@@ -11,58 +11,89 @@ const __dirname = dirname(__filename);
|
|
|
11
11
|
export const name = 'fancy-bootstrap';
|
|
12
12
|
export const inject = ['commands', 'userQuestions'];
|
|
13
13
|
|
|
14
|
+
function notify(session, text) {
|
|
15
|
+
queueMicrotask(() => {
|
|
16
|
+
session.append(
|
|
17
|
+
'user/message',
|
|
18
|
+
{ content: [{ type: 'text', text }], source: { kind: 'user' } },
|
|
19
|
+
{ surfaceOp: 'append' }
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
export async function apply(ctx) {
|
|
15
25
|
ctx.commands.register({
|
|
16
26
|
name: 'fancy-bootstrap',
|
|
17
|
-
description: '初始化项目根 (
|
|
27
|
+
description: '初始化项目根 ( usage: /fancy-bootstrap [项目根] )',
|
|
18
28
|
handler: async (invocation) => {
|
|
19
|
-
const
|
|
29
|
+
const session = invocation.agent.session;
|
|
30
|
+
const rawInput = invocation.rawInput ? invocation.rawInput.trim() : '';
|
|
31
|
+
|
|
32
|
+
let projectRoot = rawInput;
|
|
20
33
|
if (!projectRoot) {
|
|
21
|
-
|
|
34
|
+
const result = await ctx.userQuestions.ask({
|
|
35
|
+
questions: [{ id: 'path', question: '请输入项目根路径 ( 或者输入 . 代表当前路径 )' }],
|
|
36
|
+
});
|
|
37
|
+
projectRoot = result.answers[0]?.custom ? result.answers[0].custom.trim() : '';
|
|
22
38
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} else {
|
|
28
|
-
const err = result.error;
|
|
29
|
-
const msg = (typeof err === 'object' && err !== null)
|
|
30
|
-
? (err.message || JSON.stringify(err))
|
|
31
|
-
: String(err || 'unknown');
|
|
32
|
-
return { kind: 'error', text: 'Error: ' + msg };
|
|
33
|
-
}
|
|
34
|
-
} catch (e) {
|
|
35
|
-
return { kind: 'error', text: 'Exception: ' + (e instanceof Error ? e.message : String(e)) };
|
|
39
|
+
|
|
40
|
+
if (!projectRoot) {
|
|
41
|
+
notify(session, '未提供路径,已取消');
|
|
42
|
+
return { kind: 'success', text: '' };
|
|
36
43
|
}
|
|
44
|
+
|
|
45
|
+
if (projectRoot.includes('..') || /[<>:"'|?*[\x00-\x1f]/.test(projectRoot) || /\s/.test(projectRoot)) {
|
|
46
|
+
notify(session, '路径不合法(不能含空格、.. 或特殊字符)');
|
|
47
|
+
return { kind: 'success', text: '' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const confirm = await ctx.userQuestions.ask({
|
|
51
|
+
questions: [{
|
|
52
|
+
id: 'confirm',
|
|
53
|
+
question: '确认将以下路径作为项目根?',
|
|
54
|
+
detail: projectRoot,
|
|
55
|
+
hideCustomInput: true,
|
|
56
|
+
options: [
|
|
57
|
+
{ label: '确认创建', description: '开始在此目录初始化项目' },
|
|
58
|
+
{ label: '取消', description: '取消本次操作' },
|
|
59
|
+
],
|
|
60
|
+
}],
|
|
61
|
+
});
|
|
62
|
+
const chosen = confirm.answers[0]?.selected ? confirm.answers[0].selected[0] : confirm.answers[0]?.custom;
|
|
63
|
+
if (chosen === '取消') {
|
|
64
|
+
notify(session, '已取消');
|
|
65
|
+
return { kind: 'success', text: '' };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const scriptPath = join(__dirname, 'scripts', 'init.js');
|
|
69
|
+
const { ok, output, error } = await runScript('node', [scriptPath, projectRoot]);
|
|
70
|
+
|
|
71
|
+
if (!ok) {
|
|
72
|
+
let detail = error || output || '未知错误';
|
|
73
|
+
try { detail = JSON.parse(output).error.message || detail; } catch (_) {}
|
|
74
|
+
notify(session, '初始化失败:' + detail);
|
|
75
|
+
} else {
|
|
76
|
+
notify(session, '项目初始化完成:' + projectRoot + '\n\n下一步:/fancy-scan 扫榜分析');
|
|
77
|
+
}
|
|
78
|
+
return { kind: 'success', text: '' };
|
|
37
79
|
}
|
|
38
80
|
});
|
|
39
81
|
}
|
|
40
82
|
|
|
41
|
-
function
|
|
83
|
+
function runScript(cmd, args) {
|
|
42
84
|
return new Promise((resolve) => {
|
|
43
|
-
const
|
|
44
|
-
const proc = spawn('node', [scriptPath, projectRoot], { timeout: 30000, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
85
|
+
const proc = spawn(cmd, args, { timeout: 30000, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
45
86
|
let stdout = '', stderr = '';
|
|
46
|
-
proc.stdout.on('data', (
|
|
47
|
-
proc.stderr.on('data', (
|
|
87
|
+
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
88
|
+
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
48
89
|
proc.on('close', (code) => {
|
|
49
90
|
try {
|
|
50
91
|
const parsed = JSON.parse(stdout);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} else {
|
|
54
|
-
const err = parsed.error;
|
|
55
|
-
const msg = (typeof err === 'object' && err !== null)
|
|
56
|
-
? (err.message || JSON.stringify(err))
|
|
57
|
-
: (err || stderr || ('exit ' + code));
|
|
58
|
-
resolve({ ok: false, output: stdout, error: msg });
|
|
59
|
-
}
|
|
60
|
-
} catch {
|
|
92
|
+
resolve({ ok: parsed.ok === true, output: stdout, error: parsed.error ? (parsed.error.message || JSON.stringify(parsed.error)) : stderr || undefined });
|
|
93
|
+
} catch (_) {
|
|
61
94
|
resolve({ ok: false, output: stdout, error: stderr || ('exit ' + code) });
|
|
62
95
|
}
|
|
63
96
|
});
|
|
64
|
-
proc.on('error', (err) => {
|
|
65
|
-
resolve({ ok: false, output: '', error: err.message });
|
|
66
|
-
});
|
|
97
|
+
proc.on('error', (err) => resolve({ ok: false, output: '', error: err.message }));
|
|
67
98
|
});
|
|
68
99
|
}
|