@panguard-ai/panguard 0.3.8 → 0.4.2
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/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +28 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/serve-admin.d.ts +11 -0
- package/dist/cli/commands/serve-admin.d.ts.map +1 -0
- package/dist/cli/commands/serve-admin.js +302 -0
- package/dist/cli/commands/serve-admin.js.map +1 -0
- package/dist/cli/commands/serve-auth.d.ts +11 -0
- package/dist/cli/commands/serve-auth.d.ts.map +1 -0
- package/dist/cli/commands/serve-auth.js +119 -0
- package/dist/cli/commands/serve-auth.js.map +1 -0
- package/dist/cli/commands/serve-core.d.ts +25 -0
- package/dist/cli/commands/serve-core.d.ts.map +1 -0
- package/dist/cli/commands/serve-core.js +258 -0
- package/dist/cli/commands/serve-core.js.map +1 -0
- package/dist/cli/commands/serve-tc.d.ts +12 -0
- package/dist/cli/commands/serve-tc.d.ts.map +1 -0
- package/dist/cli/commands/serve-tc.js +296 -0
- package/dist/cli/commands/serve-tc.js.map +1 -0
- package/dist/cli/commands/serve-types.d.ts +38 -0
- package/dist/cli/commands/serve-types.d.ts.map +1 -0
- package/dist/cli/commands/serve-types.js +108 -0
- package/dist/cli/commands/serve-types.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +6 -0
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +41 -1144
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/setup.js +2 -2
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/interactive/actions/audit.d.ts +7 -0
- package/dist/cli/interactive/actions/audit.d.ts.map +1 -0
- package/dist/cli/interactive/actions/audit.js +198 -0
- package/dist/cli/interactive/actions/audit.js.map +1 -0
- package/dist/cli/interactive/actions/demo.d.ts +7 -0
- package/dist/cli/interactive/actions/demo.d.ts.map +1 -0
- package/dist/cli/interactive/actions/demo.js +113 -0
- package/dist/cli/interactive/actions/demo.js.map +1 -0
- package/dist/cli/interactive/actions/guard.d.ts +7 -0
- package/dist/cli/interactive/actions/guard.d.ts.map +1 -0
- package/dist/cli/interactive/actions/guard.js +164 -0
- package/dist/cli/interactive/actions/guard.js.map +1 -0
- package/dist/cli/interactive/actions/misc.d.ts +13 -0
- package/dist/cli/interactive/actions/misc.d.ts.map +1 -0
- package/dist/cli/interactive/actions/misc.js +209 -0
- package/dist/cli/interactive/actions/misc.js.map +1 -0
- package/dist/cli/interactive/actions/scan.d.ts +7 -0
- package/dist/cli/interactive/actions/scan.d.ts.map +1 -0
- package/dist/cli/interactive/actions/scan.js +143 -0
- package/dist/cli/interactive/actions/scan.js.map +1 -0
- package/dist/cli/interactive/actions/setup.d.ts +8 -0
- package/dist/cli/interactive/actions/setup.d.ts.map +1 -0
- package/dist/cli/interactive/actions/setup.js +130 -0
- package/dist/cli/interactive/actions/setup.js.map +1 -0
- package/dist/cli/interactive/lang.d.ts +11 -0
- package/dist/cli/interactive/lang.d.ts.map +1 -0
- package/dist/cli/interactive/lang.js +51 -0
- package/dist/cli/interactive/lang.js.map +1 -0
- package/dist/cli/interactive/menu-defs.d.ts +17 -0
- package/dist/cli/interactive/menu-defs.d.ts.map +1 -0
- package/dist/cli/interactive/menu-defs.js +106 -0
- package/dist/cli/interactive/menu-defs.js.map +1 -0
- package/dist/cli/interactive/render.d.ts +16 -0
- package/dist/cli/interactive/render.d.ts.map +1 -0
- package/dist/cli/interactive/render.js +145 -0
- package/dist/cli/interactive/render.js.map +1 -0
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +64 -1322
- package/dist/cli/interactive.js.map +1 -1
- package/package.json +7 -6
package/dist/cli/interactive.js
CHANGED
|
@@ -6,321 +6,50 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @panguard-ai/panguard/cli/interactive
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
function detectLang() {
|
|
24
|
-
if (existsSync(CONFIG_PATH)) {
|
|
25
|
-
try {
|
|
26
|
-
const data = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
27
|
-
if (data.lang === 'zh-TW' || data.lang === 'en')
|
|
28
|
-
return data.lang;
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
/* ignore */
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
const sysLang = process.env['LANG'] ?? process.env['LC_ALL'] ?? '';
|
|
35
|
-
if (sysLang.includes('zh'))
|
|
36
|
-
return 'zh-TW';
|
|
37
|
-
return 'en';
|
|
38
|
-
}
|
|
39
|
-
function saveLang(lang) {
|
|
40
|
-
try {
|
|
41
|
-
let config = {};
|
|
42
|
-
if (existsSync(CONFIG_PATH)) {
|
|
43
|
-
config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
44
|
-
}
|
|
45
|
-
config['lang'] = lang;
|
|
46
|
-
if (!existsSync(CONFIG_DIR))
|
|
47
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
48
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
/* best effort */
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
let currentLang = 'en';
|
|
55
|
-
const MENU_DEFS = [
|
|
56
|
-
{
|
|
57
|
-
key: 'setup',
|
|
58
|
-
icon: '>',
|
|
59
|
-
number: 0,
|
|
60
|
-
en: 'Setup Wizard',
|
|
61
|
-
zh: '\u521D\u59CB\u8A2D\u5B9A',
|
|
62
|
-
enDesc: 'Configure all modules with guided setup',
|
|
63
|
-
zhDesc: '\u958B\u555F\u6B64\u5F15\u64CE\uFF0C\u81EA\u52D5\u914D\u7F6E\u6240\u6709\u6A21\u7D44',
|
|
64
|
-
tierBadge: '',
|
|
65
|
-
featureKey: 'setup',
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
key: 'scan',
|
|
69
|
-
icon: '\u26A1',
|
|
70
|
-
number: 1,
|
|
71
|
-
en: 'Security Scan',
|
|
72
|
-
zh: '\u5B89\u5168\u6383\u63CF',
|
|
73
|
-
enDesc: 'Scan system security and analyze risks',
|
|
74
|
-
zhDesc: '\u6383\u63CF\u7CFB\u7D71\u5B89\u5168\u72C0\u614B\uFF0C\u5206\u6790\u6240\u6709\u98A8\u96AA',
|
|
75
|
-
tierBadge: '',
|
|
76
|
-
featureKey: 'scan',
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
key: 'report',
|
|
80
|
-
icon: '\u25A0',
|
|
81
|
-
number: 2,
|
|
82
|
-
en: 'Compliance Report [Coming Soon]',
|
|
83
|
-
zh: '\u5408\u898F\u5831\u544A [\u5373\u5C07\u63A8\u51FA]',
|
|
84
|
-
enDesc: 'ISO 27001, SOC 2, TW Cyber Security Act — under development',
|
|
85
|
-
zhDesc: 'ISO 27001\u3001SOC 2\u3001\u8CC7\u5B89\u7BA1\u7406\u6CD5 \u2014 \u958B\u767C\u4E2D',
|
|
86
|
-
tierBadge: '',
|
|
87
|
-
featureKey: 'report',
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
key: 'guard',
|
|
91
|
-
icon: '\u2713',
|
|
92
|
-
number: 3,
|
|
93
|
-
en: 'Guard Engine',
|
|
94
|
-
zh: '\u5B88\u8B77\u5F15\u64CE',
|
|
95
|
-
enDesc: 'Real-time monitoring and continuous protection',
|
|
96
|
-
zhDesc: '\u5373\u6642\u76E3\u63A7\u9023\u7E8C\u9632\u8B77\u7CFB\u7D71',
|
|
97
|
-
tierBadge: '',
|
|
98
|
-
featureKey: 'guard',
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
key: 'trap',
|
|
102
|
-
icon: '\u00B7',
|
|
103
|
-
number: 4,
|
|
104
|
-
en: 'Honeypot System [Coming Soon]',
|
|
105
|
-
zh: '\u871C\u7F50\u7CFB\u7D71 [\u5373\u5C07\u63A8\u51FA]',
|
|
106
|
-
enDesc: 'Decoy services to detect attackers — under development',
|
|
107
|
-
zhDesc: '\u8A98\u990C\u670D\u52D9\u5075\u6E2C\u653B\u64CA\u8005 \u2014 \u958B\u767C\u4E2D',
|
|
108
|
-
tierBadge: '',
|
|
109
|
-
featureKey: 'trap',
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
key: 'notify',
|
|
113
|
-
icon: '\u00B7',
|
|
114
|
-
number: 5,
|
|
115
|
-
en: 'Notifications',
|
|
116
|
-
zh: '\u901A\u77E5\u7CFB\u7D71',
|
|
117
|
-
enDesc: 'LINE, Telegram, Slack, Email, Webhook channels',
|
|
118
|
-
zhDesc: 'LINE\u3001Telegram\u3001Slack\u3001Email\u3001Webhook \u901A\u77E5\u7BA1\u9053',
|
|
119
|
-
tierBadge: '',
|
|
120
|
-
featureKey: 'notify',
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
key: 'threat-cloud',
|
|
124
|
-
icon: '\u00B7',
|
|
125
|
-
number: 6,
|
|
126
|
-
en: 'Threat Cloud',
|
|
127
|
-
zh: '\u5A01\u8105\u60C5\u5831',
|
|
128
|
-
enDesc: 'Threat intelligence REST API server',
|
|
129
|
-
zhDesc: '\u5A01\u8105\u60C5\u5831 REST API \u4F3A\u670D\u52D9',
|
|
130
|
-
tierBadge: '',
|
|
131
|
-
featureKey: 'threat-cloud',
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
key: 'demo',
|
|
135
|
-
icon: '\u00B7',
|
|
136
|
-
number: 7,
|
|
137
|
-
en: 'Auto Demo',
|
|
138
|
-
zh: '\u81EA\u52D5\u5C55\u793A',
|
|
139
|
-
enDesc: 'Run through all security modules',
|
|
140
|
-
zhDesc: '\u81EA\u52D5\u57F7\u884C\u7D9C\u5408\u529F\u80FD\u5C55\u793A',
|
|
141
|
-
tierBadge: '',
|
|
142
|
-
featureKey: 'demo',
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
key: 'audit',
|
|
146
|
-
icon: '\u25A0',
|
|
147
|
-
number: 8,
|
|
148
|
-
en: 'Skill Auditor',
|
|
149
|
-
zh: '\u6280\u80FD\u5BE9\u8A08',
|
|
150
|
-
enDesc: 'Audit AI agent skills for security issues',
|
|
151
|
-
zhDesc: '\u5BE9\u8A08 AI \u4EE3\u7406\u6280\u80FD\u7684\u5B89\u5168\u554F\u984C',
|
|
152
|
-
tierBadge: '',
|
|
153
|
-
featureKey: 'audit',
|
|
154
|
-
},
|
|
155
|
-
];
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
|
-
// MCP detection helper
|
|
158
|
-
// ---------------------------------------------------------------------------
|
|
159
|
-
async function isMCPConfigured() {
|
|
160
|
-
try {
|
|
161
|
-
const mcpConfig = await import('@panguard-ai/panguard-mcp/config');
|
|
162
|
-
const platforms = await mcpConfig.detectPlatforms();
|
|
163
|
-
return platforms.some((p) => p.detected && p.alreadyConfigured);
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
// ---------------------------------------------------------------------------
|
|
170
|
-
// Guard process helpers
|
|
171
|
-
// ---------------------------------------------------------------------------
|
|
172
|
-
function isGuardRunning() {
|
|
173
|
-
const pidPath = join(homedir(), '.panguard-guard', 'panguard-guard.pid');
|
|
174
|
-
if (!existsSync(pidPath))
|
|
175
|
-
return { running: false };
|
|
176
|
-
try {
|
|
177
|
-
const pid = parseInt(readFileSync(pidPath, 'utf-8').trim(), 10);
|
|
178
|
-
process.kill(pid, 0);
|
|
179
|
-
return { running: true, pid };
|
|
180
|
-
}
|
|
181
|
-
catch {
|
|
182
|
-
return { running: false };
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
// ---------------------------------------------------------------------------
|
|
186
|
-
// Status panel with box border
|
|
187
|
-
// ---------------------------------------------------------------------------
|
|
188
|
-
function renderStatusPanel() {
|
|
189
|
-
const guardStatus = isGuardRunning();
|
|
190
|
-
const modulesValue = moduleCountDisplay(currentLang);
|
|
191
|
-
const guardLine = guardStatus.running
|
|
192
|
-
? `${c.safe('PROTECTED')} ${c.dim(`(PID: ${guardStatus.pid})`)}`
|
|
193
|
-
: c.dim('Inactive');
|
|
194
|
-
const lines = currentLang === 'zh-TW'
|
|
195
|
-
? [
|
|
196
|
-
`\u9632\u8B77\u72C0\u614B\uFF1A${guardLine}`,
|
|
197
|
-
`\u53EF\u7528\u6A21\u7D44\uFF1A${modulesValue}`,
|
|
198
|
-
`\u7248\u672C\uFF1A v${PANGUARD_VERSION}`,
|
|
199
|
-
]
|
|
200
|
-
: [
|
|
201
|
-
`Protection: ${guardLine}`,
|
|
202
|
-
`Modules: ${modulesValue}`,
|
|
203
|
-
`Version: v${PANGUARD_VERSION}`,
|
|
204
|
-
];
|
|
205
|
-
const title = currentLang === 'zh-TW' ? '\u7CFB\u7D71\u72C0\u614B / System Status' : 'System Status';
|
|
206
|
-
console.log(box(lines.join('\n'), { borderColor: c.sage, title }));
|
|
207
|
-
}
|
|
208
|
-
// ---------------------------------------------------------------------------
|
|
209
|
-
// Menu rendering
|
|
210
|
-
// ---------------------------------------------------------------------------
|
|
211
|
-
function renderMenu() {
|
|
212
|
-
const titleText = currentLang === 'zh-TW' ? '\u4E3B\u9078\u55AE / Main Menu' : 'Main Menu';
|
|
213
|
-
console.log(` ${theme.title(titleText)}`);
|
|
214
|
-
console.log('');
|
|
215
|
-
for (const def of MENU_DEFS) {
|
|
216
|
-
const name = currentLang === 'zh-TW' ? def.zh : def.en;
|
|
217
|
-
const desc = currentLang === 'zh-TW' ? def.zhDesc : def.enDesc;
|
|
218
|
-
const badge = def.tierBadge ? ` ${c.dim(def.tierBadge)}` : '';
|
|
219
|
-
const iconStr = def.icon === '\u2713' ? c.safe(def.icon) : c.dim(def.icon);
|
|
220
|
-
console.log(` ${iconStr} ${c.sage(`[${def.number}]`)} ${name}${badge}`);
|
|
221
|
-
console.log(` ${c.dim(desc)}`);
|
|
222
|
-
console.log('');
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// ---------------------------------------------------------------------------
|
|
226
|
-
// Footer shortcuts
|
|
227
|
-
// ---------------------------------------------------------------------------
|
|
228
|
-
function renderFooter() {
|
|
229
|
-
const quit = currentLang === 'zh-TW' ? '\u9000\u51FA' : 'Quit';
|
|
230
|
-
const help = currentLang === 'zh-TW' ? '\u8AAA\u660E' : 'Help';
|
|
231
|
-
const langToggle = currentLang === 'zh-TW' ? '\u4E2D/EN' : '\u4E2D/EN';
|
|
232
|
-
console.log(`${c.dim(`[q] ${quit} [h] ${help} [b] ${langToggle}`)}`);
|
|
233
|
-
console.log('');
|
|
234
|
-
}
|
|
235
|
-
// ---------------------------------------------------------------------------
|
|
236
|
-
// Help display
|
|
237
|
-
// ---------------------------------------------------------------------------
|
|
238
|
-
function showHelp() {
|
|
239
|
-
console.log('');
|
|
240
|
-
const title = currentLang === 'zh-TW' ? '\u6307\u4EE4\u8AAA\u660E' : 'Available Commands';
|
|
241
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
242
|
-
console.log('');
|
|
243
|
-
const menuTitle = currentLang === 'zh-TW' ? '\u4E3B\u9078\u55AE' : 'Main Menu';
|
|
244
|
-
console.log(` ${c.sage(menuTitle)}`);
|
|
245
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
246
|
-
? ' [0]-[8] \u6309\u6578\u5B57\u9375\u5373\u523B\u9078\u64C7\uFF08\u4E0D\u9700\u6309 Enter\uFF09'
|
|
247
|
-
: ' [0]-[8] Press number key to select instantly (no Enter needed)'));
|
|
248
|
-
console.log('');
|
|
249
|
-
const promptTitle = currentLang === 'zh-TW' ? '\u6587\u5B57\u6307\u4EE4' : 'Text Commands';
|
|
250
|
-
console.log(` ${c.sage(promptTitle)}`);
|
|
251
|
-
const cmds = [
|
|
252
|
-
{
|
|
253
|
-
cmd: 'setup',
|
|
254
|
-
en: 'Connect AI agents via MCP',
|
|
255
|
-
zh: '\u900F\u904E MCP \u9023\u63A5 AI \u4EE3\u7406',
|
|
256
|
-
},
|
|
257
|
-
{ cmd: 'audit', en: 'Audit AI agent skills', zh: '\u5BE9\u8A08 AI \u4EE3\u7406\u6280\u80FD' },
|
|
258
|
-
{ cmd: 'status', en: 'System status overview', zh: '\u7CFB\u7D71\u72C0\u614B\u7E3D\u89BD' },
|
|
259
|
-
{ cmd: 'config', en: 'Settings management', zh: '\u8A2D\u5B9A\u7BA1\u7406' },
|
|
260
|
-
{ cmd: 'hardening', en: 'Security hardening', zh: '\u5B89\u5168\u52A0\u56FA' },
|
|
261
|
-
{ cmd: 'doctor', en: 'Health diagnostics', zh: '\u5065\u5EB7\u8A3A\u65B7' },
|
|
262
|
-
{ cmd: 'help', en: 'Show this help', zh: '\u986F\u793A\u6B64\u8AAA\u660E' },
|
|
263
|
-
];
|
|
264
|
-
for (const { cmd, en, zh } of cmds) {
|
|
265
|
-
const desc = currentLang === 'zh-TW' ? zh : en;
|
|
266
|
-
console.log(` ${c.sage(cmd.padEnd(14))} ${c.dim(desc)}`);
|
|
267
|
-
}
|
|
268
|
-
const shortcutTitle = currentLang === 'zh-TW' ? '\u5FEB\u6377\u9375' : 'Shortcuts';
|
|
269
|
-
console.log('');
|
|
270
|
-
console.log(` ${c.sage(shortcutTitle)}`);
|
|
271
|
-
console.log(c.dim(` [q] ${currentLang === 'zh-TW' ? '\u9000\u51FA' : 'Quit'} [h] ${currentLang === 'zh-TW' ? '\u8AAA\u660E' : 'Help'} [b] ${currentLang === 'zh-TW' ? '\u5207\u63DB\u8A9E\u8A00' : 'Toggle language'}`));
|
|
272
|
-
console.log('');
|
|
273
|
-
}
|
|
274
|
-
// ---------------------------------------------------------------------------
|
|
275
|
-
// Startup screen
|
|
276
|
-
// ---------------------------------------------------------------------------
|
|
277
|
-
function renderStartup() {
|
|
278
|
-
console.clear();
|
|
279
|
-
// Brand logo
|
|
280
|
-
renderLogo();
|
|
281
|
-
console.log('');
|
|
282
|
-
// Tagline + version
|
|
283
|
-
const tagline = c.dim(' AI-Powered Security Platform');
|
|
284
|
-
console.log(tagline);
|
|
285
|
-
console.log('');
|
|
286
|
-
// Status panel
|
|
287
|
-
renderStatusPanel();
|
|
288
|
-
console.log('');
|
|
289
|
-
// Menu
|
|
290
|
-
renderMenu();
|
|
291
|
-
// Footer
|
|
292
|
-
renderFooter();
|
|
293
|
-
}
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { c } from '@panguard-ai/core';
|
|
11
|
+
import { waitForMainInput, cleanupTerminal } from './menu.js';
|
|
12
|
+
import { checkFeatureAccess, showUpgradePrompt } from './auth-guard.js';
|
|
13
|
+
import { formatError } from './ux-helpers.js';
|
|
14
|
+
import { getLang, setLang, getConfigPath, detectLang, saveLang } from './interactive/lang.js';
|
|
15
|
+
import { MENU_DEFS } from './interactive/menu-defs.js';
|
|
16
|
+
import { renderStartup, showHelp, isMCPConfigured } from './interactive/render.js';
|
|
17
|
+
import { actionInit, actionMCPSetup } from './interactive/actions/setup.js';
|
|
18
|
+
import { actionScan } from './interactive/actions/scan.js';
|
|
19
|
+
import { actionGuard } from './interactive/actions/guard.js';
|
|
20
|
+
import { actionDemo } from './interactive/actions/demo.js';
|
|
21
|
+
import { actionAudit } from './interactive/actions/audit.js';
|
|
22
|
+
import { actionReport, actionTrap, actionChat, actionThreat, actionHardening, actionStatus, actionConfig, } from './interactive/actions/misc.js';
|
|
294
23
|
// ---------------------------------------------------------------------------
|
|
295
24
|
// Entry point
|
|
296
25
|
// ---------------------------------------------------------------------------
|
|
297
26
|
export async function startInteractive(lang) {
|
|
298
|
-
|
|
27
|
+
setLang(lang === 'en' ? 'en' : lang === 'zh-TW' ? 'zh-TW' : detectLang());
|
|
299
28
|
const exit = () => {
|
|
300
29
|
cleanupTerminal();
|
|
301
|
-
const msg =
|
|
30
|
+
const msg = getLang() === 'zh-TW' ? '\u611F\u8B1D\u4F7F\u7528 Panguard AI\uFF01' : 'Goodbye!';
|
|
302
31
|
console.log(`\n ${c.sage(msg)}\n`);
|
|
303
32
|
process.exit(0);
|
|
304
33
|
};
|
|
305
34
|
process.on('SIGINT', exit);
|
|
306
35
|
process.on('SIGTERM', exit);
|
|
307
|
-
renderStartup();
|
|
308
|
-
// First-time user hint
|
|
36
|
+
renderStartup(getLang());
|
|
37
|
+
// First-time user hint
|
|
309
38
|
const mcpConfigured = await isMCPConfigured();
|
|
310
|
-
if (!existsSync(
|
|
311
|
-
console.log(
|
|
39
|
+
if (!existsSync(getConfigPath())) {
|
|
40
|
+
console.log(getLang() === 'zh-TW'
|
|
312
41
|
? ` ${c.sage('\u25C6')} \u9996\u6B21\u4F7F\u7528\uFF1F\u5EFA\u8B70\u6D41\u7A0B\uFF1A`
|
|
313
42
|
: ` ${c.sage('\u25C6')} First time? Recommended flow:`);
|
|
314
|
-
console.log(
|
|
43
|
+
console.log(getLang() === 'zh-TW'
|
|
315
44
|
? ` ${c.sage('[0]')} \u521D\u59CB\u8A2D\u5B9A \u2192 \u81EA\u52D5\u9023\u63A5 AI \u4EE3\u7406 + \u6280\u80FD\u5BE9\u8A08 + \u6383\u63CF`
|
|
316
45
|
: ` ${c.sage('[0]')} Setup Wizard \u2192 auto-connect AI agents + skill audit + scan`);
|
|
317
|
-
console.log(
|
|
46
|
+
console.log(getLang() === 'zh-TW'
|
|
318
47
|
? ` ${c.sage('[8]')} \u6280\u80FD\u5BE9\u8A08 \u2192 \u5BE9\u8A08\u5DF2\u5B89\u88DD AI \u6280\u80FD\u7684\u5B89\u5168\u554F\u984C`
|
|
319
48
|
: ` ${c.sage('[8]')} Skill Auditor \u2192 check installed AI skills for security issues`);
|
|
320
49
|
console.log('');
|
|
321
50
|
}
|
|
322
51
|
else if (!mcpConfigured) {
|
|
323
|
-
console.log(
|
|
52
|
+
console.log(getLang() === 'zh-TW'
|
|
324
53
|
? ` ${c.sage('\u25C6')} AI \u4EE3\u7406\u5C1A\u672A\u9023\u63A5\u3002\u57F7\u884C ${c.sage('panguard setup')} \u6216\u6309 ${c.sage('[0]')} \u9023\u63A5 Claude Code\u3001Cursor \u7B49\u5E73\u53F0\u3002`
|
|
325
54
|
: ` ${c.sage('\u25C6')} AI agents not connected. Run ${c.sage('panguard setup')} or press ${c.sage('[0]')} to connect Claude Code, Cursor, etc.`);
|
|
326
55
|
console.log('');
|
|
@@ -335,22 +64,21 @@ export async function startInteractive(lang) {
|
|
|
335
64
|
exit();
|
|
336
65
|
return;
|
|
337
66
|
case 'help':
|
|
338
|
-
showHelp();
|
|
67
|
+
showHelp(getLang());
|
|
339
68
|
continue;
|
|
340
69
|
case 'lang_toggle':
|
|
341
|
-
|
|
342
|
-
saveLang(
|
|
343
|
-
renderStartup();
|
|
70
|
+
setLang(getLang() === 'zh-TW' ? 'en' : 'zh-TW');
|
|
71
|
+
saveLang(getLang());
|
|
72
|
+
renderStartup(getLang());
|
|
344
73
|
continue;
|
|
345
74
|
case 'number': {
|
|
346
75
|
const def = MENU_DEFS[input.index];
|
|
347
76
|
if (!def)
|
|
348
77
|
continue;
|
|
349
|
-
// Feature gate check
|
|
350
78
|
if (!checkFeatureAccess(def.featureKey)) {
|
|
351
|
-
showUpgradePrompt(def.featureKey,
|
|
79
|
+
showUpgradePrompt(def.featureKey, getLang());
|
|
352
80
|
await new Promise((r) => setTimeout(r, 500));
|
|
353
|
-
renderStartup();
|
|
81
|
+
renderStartup(getLang());
|
|
354
82
|
continue;
|
|
355
83
|
}
|
|
356
84
|
console.clear();
|
|
@@ -359,22 +87,21 @@ export async function startInteractive(lang) {
|
|
|
359
87
|
}
|
|
360
88
|
catch (err) {
|
|
361
89
|
console.log('');
|
|
362
|
-
console.log(formatError(err instanceof Error ? err.message : String(err), `${
|
|
90
|
+
console.log(formatError(err instanceof Error ? err.message : String(err), `${getLang() === 'zh-TW' ? '\u57F7\u884C' : 'Running'} ${def.key}`, getLang() === 'zh-TW'
|
|
363
91
|
? '\u8ACB\u67E5\u770B\u65E5\u8A8C\u6216\u91CD\u8A66'
|
|
364
92
|
: 'Check logs or retry'));
|
|
365
93
|
}
|
|
366
94
|
await new Promise((r) => setTimeout(r, 500));
|
|
367
|
-
renderStartup();
|
|
95
|
+
renderStartup(getLang());
|
|
368
96
|
continue;
|
|
369
97
|
}
|
|
370
98
|
case 'command': {
|
|
371
99
|
const text = input.text.toLowerCase();
|
|
372
100
|
if (!text)
|
|
373
101
|
continue;
|
|
374
|
-
// Handle text commands
|
|
375
102
|
const handled = await dispatchCommand(text);
|
|
376
103
|
if (!handled) {
|
|
377
|
-
console.log(c.dim(
|
|
104
|
+
console.log(c.dim(getLang() === 'zh-TW'
|
|
378
105
|
? ` \u672A\u77E5\u6307\u4EE4\u3002\u8F38\u5165 help \u67E5\u770B\u53EF\u7528\u6307\u4EE4\u3002`
|
|
379
106
|
: ` Unknown command. Type 'help' for available commands.`));
|
|
380
107
|
console.log('');
|
|
@@ -388,82 +115,77 @@ export async function startInteractive(lang) {
|
|
|
388
115
|
// Prompt command dispatch
|
|
389
116
|
// ---------------------------------------------------------------------------
|
|
390
117
|
async function dispatchCommand(text) {
|
|
118
|
+
const lang = getLang();
|
|
391
119
|
switch (text) {
|
|
392
120
|
case 'status':
|
|
393
121
|
console.clear();
|
|
394
|
-
await actionStatus();
|
|
122
|
+
await actionStatus(lang);
|
|
395
123
|
await new Promise((r) => setTimeout(r, 500));
|
|
396
|
-
renderStartup();
|
|
124
|
+
renderStartup(lang);
|
|
397
125
|
return true;
|
|
398
126
|
case 'login':
|
|
399
|
-
case 'logout':
|
|
127
|
+
case 'logout':
|
|
400
128
|
console.clear();
|
|
401
129
|
console.log('');
|
|
402
130
|
console.log(' Authentication removed. All features are free and open source.');
|
|
403
131
|
console.log('');
|
|
404
132
|
await new Promise((r) => setTimeout(r, 500));
|
|
405
|
-
renderStartup();
|
|
133
|
+
renderStartup(lang);
|
|
406
134
|
return true;
|
|
407
|
-
}
|
|
408
135
|
case 'config':
|
|
409
136
|
console.clear();
|
|
410
|
-
await actionConfig();
|
|
137
|
+
await actionConfig(lang);
|
|
411
138
|
await new Promise((r) => setTimeout(r, 500));
|
|
412
|
-
renderStartup();
|
|
139
|
+
renderStartup(lang);
|
|
413
140
|
return true;
|
|
414
|
-
case 'upgrade':
|
|
141
|
+
case 'upgrade':
|
|
415
142
|
console.clear();
|
|
416
143
|
console.log('');
|
|
417
144
|
console.log(' All features are free and open source.');
|
|
418
145
|
console.log('');
|
|
419
146
|
await new Promise((r) => setTimeout(r, 500));
|
|
420
|
-
renderStartup();
|
|
147
|
+
renderStartup(lang);
|
|
421
148
|
return true;
|
|
422
|
-
}
|
|
423
149
|
case 'hardening':
|
|
424
150
|
console.clear();
|
|
425
|
-
await actionHardening();
|
|
151
|
+
await actionHardening(lang);
|
|
426
152
|
await new Promise((r) => setTimeout(r, 500));
|
|
427
|
-
renderStartup();
|
|
153
|
+
renderStartup(lang);
|
|
428
154
|
return true;
|
|
429
155
|
case 'doctor':
|
|
430
156
|
console.clear();
|
|
431
157
|
try {
|
|
432
158
|
const { runDoctor } = await import('./commands/doctor.js');
|
|
433
|
-
await runDoctor(
|
|
159
|
+
await runDoctor(lang);
|
|
434
160
|
}
|
|
435
161
|
catch (err) {
|
|
436
|
-
console.log(formatError(err instanceof Error ? err.message : String(err),
|
|
437
|
-
? '\u57F7\u884C\u5065\u5EB7\u8A3A\u65B7'
|
|
438
|
-
: 'Running diagnostics', currentLang === 'zh-TW' ? '\u8ACB\u91CD\u8A66' : 'Please retry'));
|
|
162
|
+
console.log(formatError(err instanceof Error ? err.message : String(err), lang === 'zh-TW' ? '\u57F7\u884C\u5065\u5EB7\u8A3A\u65B7' : 'Running diagnostics', lang === 'zh-TW' ? '\u8ACB\u91CD\u8A66' : 'Please retry'));
|
|
439
163
|
}
|
|
440
164
|
await new Promise((r) => setTimeout(r, 500));
|
|
441
|
-
renderStartup();
|
|
165
|
+
renderStartup(lang);
|
|
442
166
|
return true;
|
|
443
|
-
case 'whoami':
|
|
167
|
+
case 'whoami':
|
|
444
168
|
console.clear();
|
|
445
169
|
console.log('');
|
|
446
170
|
console.log(' All features available (no login required).');
|
|
447
171
|
console.log('');
|
|
448
172
|
await new Promise((r) => setTimeout(r, 500));
|
|
449
|
-
renderStartup();
|
|
173
|
+
renderStartup(lang);
|
|
450
174
|
return true;
|
|
451
|
-
}
|
|
452
175
|
case 'audit':
|
|
453
176
|
console.clear();
|
|
454
|
-
await actionAudit();
|
|
177
|
+
await actionAudit(lang);
|
|
455
178
|
await new Promise((r) => setTimeout(r, 500));
|
|
456
|
-
renderStartup();
|
|
179
|
+
renderStartup(lang);
|
|
457
180
|
return true;
|
|
458
|
-
case 'setup':
|
|
181
|
+
case 'setup':
|
|
459
182
|
console.clear();
|
|
460
|
-
await actionMCPSetup();
|
|
183
|
+
await actionMCPSetup(lang);
|
|
461
184
|
await new Promise((r) => setTimeout(r, 500));
|
|
462
|
-
renderStartup();
|
|
185
|
+
renderStartup(lang);
|
|
463
186
|
return true;
|
|
464
|
-
}
|
|
465
187
|
case 'help':
|
|
466
|
-
showHelp();
|
|
188
|
+
showHelp(lang);
|
|
467
189
|
return true;
|
|
468
190
|
default:
|
|
469
191
|
return false;
|
|
@@ -473,1015 +195,35 @@ async function dispatchCommand(text) {
|
|
|
473
195
|
// Action dispatch (from numbered menu)
|
|
474
196
|
// ---------------------------------------------------------------------------
|
|
475
197
|
async function dispatch(key) {
|
|
198
|
+
const lang = getLang();
|
|
476
199
|
switch (key) {
|
|
477
200
|
case 'setup':
|
|
478
|
-
await actionInit();
|
|
201
|
+
await actionInit(lang);
|
|
479
202
|
break;
|
|
480
203
|
case 'scan':
|
|
481
|
-
await actionScan();
|
|
204
|
+
await actionScan(lang, () => actionGuard(lang), () => actionAudit(lang));
|
|
482
205
|
break;
|
|
483
206
|
case 'report':
|
|
484
|
-
await actionReport();
|
|
207
|
+
await actionReport(lang);
|
|
485
208
|
break;
|
|
486
209
|
case 'guard':
|
|
487
|
-
await actionGuard();
|
|
210
|
+
await actionGuard(lang);
|
|
488
211
|
break;
|
|
489
212
|
case 'trap':
|
|
490
|
-
await actionTrap();
|
|
213
|
+
await actionTrap(lang);
|
|
491
214
|
break;
|
|
492
215
|
case 'notify':
|
|
493
|
-
await actionChat();
|
|
216
|
+
await actionChat(lang);
|
|
494
217
|
break;
|
|
495
218
|
case 'threat-cloud':
|
|
496
|
-
await actionThreat();
|
|
219
|
+
await actionThreat(lang);
|
|
497
220
|
break;
|
|
498
221
|
case 'demo':
|
|
499
|
-
await actionDemo();
|
|
222
|
+
await actionDemo(lang);
|
|
500
223
|
break;
|
|
501
224
|
case 'audit':
|
|
502
|
-
await actionAudit();
|
|
503
|
-
break;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
// ---------------------------------------------------------------------------
|
|
507
|
-
// [0] Setup Wizard
|
|
508
|
-
// ---------------------------------------------------------------------------
|
|
509
|
-
async function actionInit() {
|
|
510
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u521D\u59CB\u8A2D\u5B9A' : 'Setup']);
|
|
511
|
-
const { runInitWizard } = await import('../init/index.js');
|
|
512
|
-
await runInitWizard(currentLang);
|
|
513
|
-
}
|
|
514
|
-
// ---------------------------------------------------------------------------
|
|
515
|
-
// Text command: setup (MCP-only, for users who already have config)
|
|
516
|
-
// ---------------------------------------------------------------------------
|
|
517
|
-
async function actionMCPSetup() {
|
|
518
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? 'MCP \u8A2D\u5B9A' : 'MCP Setup']);
|
|
519
|
-
const title = currentLang === 'zh-TW' ? 'AI \u4EE3\u7406\u9023\u63A5 (MCP)' : 'AI Agent Connection (MCP)';
|
|
520
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
521
|
-
console.log('');
|
|
522
|
-
const { spinner: sp } = await import('@panguard-ai/core');
|
|
523
|
-
const detectSp = sp(currentLang === 'zh-TW'
|
|
524
|
-
? '\u6B63\u5728\u5075\u6E2C AI \u4EE3\u7406\u5E73\u53F0...'
|
|
525
|
-
: 'Detecting AI agent platforms...');
|
|
526
|
-
try {
|
|
527
|
-
const mcpConfig = await import('@panguard-ai/panguard-mcp/config');
|
|
528
|
-
const platforms = await mcpConfig.detectPlatforms();
|
|
529
|
-
const detected = platforms.filter((p) => p.detected);
|
|
530
|
-
const unconfigured = detected.filter((p) => !p.alreadyConfigured);
|
|
531
|
-
if (detected.length === 0) {
|
|
532
|
-
detectSp.warn(currentLang === 'zh-TW'
|
|
533
|
-
? '\u672A\u5075\u6E2C\u5230\u4EFB\u4F55 AI \u4EE3\u7406\u5E73\u53F0'
|
|
534
|
-
: 'No AI agent platforms detected');
|
|
535
|
-
console.log('');
|
|
536
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
537
|
-
? ' \u652F\u63F4\u5E73\u53F0: Claude Code, Cursor, OpenClaw, Codex, WorkBuddy, NemoClaw, Claude Desktop'
|
|
538
|
-
: ' Supported: Claude Code, Cursor, OpenClaw, Codex, WorkBuddy, NemoClaw, Claude Desktop'));
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
detectSp.succeed(currentLang === 'zh-TW'
|
|
542
|
-
? `\u5075\u6E2C\u5230 ${detected.length} \u500B\u5E73\u53F0`
|
|
543
|
-
: `Found ${detected.length} platform(s)`);
|
|
544
|
-
console.log('');
|
|
545
|
-
// Show all platforms
|
|
546
|
-
for (const p of platforms) {
|
|
547
|
-
const status = p.detected
|
|
548
|
-
? p.alreadyConfigured
|
|
549
|
-
? c.safe('\u2713 configured')
|
|
550
|
-
: c.caution('~ not configured')
|
|
551
|
-
: c.dim('- not found');
|
|
552
|
-
console.log(` ${p.detected ? c.bold(p.name) : c.dim(p.name)} ${status}`);
|
|
553
|
-
}
|
|
554
|
-
console.log('');
|
|
555
|
-
if (unconfigured.length === 0) {
|
|
556
|
-
console.log(c.safe(currentLang === 'zh-TW'
|
|
557
|
-
? ` \u2713 \u6240\u6709\u5E73\u53F0\u5DF2\u8A2D\u5B9A\u5B8C\u6210\uFF01`
|
|
558
|
-
: ' \u2713 All platforms already configured!'));
|
|
559
|
-
console.log('');
|
|
560
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
561
|
-
? ' \u91CD\u555F AI \u4EE3\u7406\u5F8C\uFF0C\u8ACB\u6C42\u300Cpanguard_status\u300D\u5373\u53EF\u9A57\u8B49\u3002'
|
|
562
|
-
: ' Restart your AI agent, then ask "panguard_status" to verify.'));
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
// Configure unconfigured platforms
|
|
566
|
-
const configSp = sp(currentLang === 'zh-TW'
|
|
567
|
-
? `\u6B63\u5728\u8A2D\u5B9A ${unconfigured.length} \u500B\u5E73\u53F0...`
|
|
568
|
-
: `Configuring ${unconfigured.length} platform(s)...`);
|
|
569
|
-
let successCount = 0;
|
|
570
|
-
for (const p of unconfigured) {
|
|
571
|
-
const result = mcpConfig.injectMCPConfig(p.id);
|
|
572
|
-
if (result.success)
|
|
573
|
-
successCount++;
|
|
574
|
-
}
|
|
575
|
-
if (successCount === unconfigured.length) {
|
|
576
|
-
configSp.succeed(currentLang === 'zh-TW'
|
|
577
|
-
? `${successCount} \u500B\u5E73\u53F0\u8A2D\u5B9A\u5B8C\u6210`
|
|
578
|
-
: `${successCount} platform(s) configured`);
|
|
579
|
-
}
|
|
580
|
-
else {
|
|
581
|
-
configSp.warn(currentLang === 'zh-TW'
|
|
582
|
-
? `${successCount}/${unconfigured.length} \u5E73\u53F0\u8A2D\u5B9A\u6210\u529F`
|
|
583
|
-
: `${successCount}/${unconfigured.length} platform(s) configured`);
|
|
584
|
-
}
|
|
585
|
-
console.log('');
|
|
586
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
587
|
-
? ' \u91CD\u555F AI \u4EE3\u7406\u5F8C\u5373\u53EF\u4F7F\u7528 11 \u500B panguard_* MCP \u5DE5\u5177\uFF1A'
|
|
588
|
-
: ' Restart your AI agent to use 11 panguard_* MCP tools:'));
|
|
589
|
-
// Platform-specific restart instructions
|
|
590
|
-
const restartHints = {
|
|
591
|
-
'claude-code': {
|
|
592
|
-
en: 'Close and reopen your terminal',
|
|
593
|
-
zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F\u7D42\u7AEF\u6A5F',
|
|
594
|
-
},
|
|
595
|
-
'claude-desktop': {
|
|
596
|
-
en: 'Quit and reopen Claude Desktop',
|
|
597
|
-
zh: '\u9000\u51FA\u4E26\u91CD\u65B0\u958B\u555F Claude Desktop',
|
|
598
|
-
},
|
|
599
|
-
cursor: {
|
|
600
|
-
en: 'Cmd+Shift+P (or Ctrl+Shift+P) > "Reload Window"',
|
|
601
|
-
zh: 'Cmd+Shift+P > "Reload Window"',
|
|
602
|
-
},
|
|
603
|
-
openclaw: {
|
|
604
|
-
en: 'Close and reopen OpenClaw',
|
|
605
|
-
zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F OpenClaw',
|
|
606
|
-
},
|
|
607
|
-
codex: { en: 'Restart the Codex CLI session', zh: '\u91CD\u65B0\u555F\u52D5 Codex CLI' },
|
|
608
|
-
workbuddy: {
|
|
609
|
-
en: 'Close and reopen WorkBuddy',
|
|
610
|
-
zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F WorkBuddy',
|
|
611
|
-
},
|
|
612
|
-
nemoclaw: {
|
|
613
|
-
en: 'Close and reopen NemoClaw',
|
|
614
|
-
zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F NemoClaw',
|
|
615
|
-
},
|
|
616
|
-
};
|
|
617
|
-
for (const p of unconfigured) {
|
|
618
|
-
const hint = restartHints[p.id];
|
|
619
|
-
const text = hint
|
|
620
|
-
? currentLang === 'zh-TW'
|
|
621
|
-
? hint.zh
|
|
622
|
-
: hint.en
|
|
623
|
-
: currentLang === 'zh-TW'
|
|
624
|
-
? '\u91CD\u65B0\u555F\u52D5\u61C9\u7528\u7A0B\u5F0F'
|
|
625
|
-
: 'Restart the application';
|
|
626
|
-
console.log(c.dim(` ${p.name}: ${text}`));
|
|
627
|
-
}
|
|
628
|
-
console.log('');
|
|
629
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
630
|
-
? ' \u8A66\u8A66\u554F AI: \u300C\u5BE9\u8A08\u9019\u500B\u5C08\u6848\u7684\u6280\u80FD\u300D\u6216\u300C\u6383\u63CF\u6211\u7684\u7CFB\u7D71\u300D'
|
|
631
|
-
: ' Try asking your AI: "audit the skills in this project" or "scan my system"'));
|
|
632
|
-
}
|
|
633
|
-
catch (err) {
|
|
634
|
-
detectSp.fail(currentLang === 'zh-TW' ? 'MCP \u8A2D\u5B9A\u5931\u6557' : 'MCP setup failed');
|
|
635
|
-
console.log(formatError(err instanceof Error ? err.message : String(err), 'MCP Setup', currentLang === 'zh-TW' ? '\u8ACB\u91CD\u8A66' : 'Please retry'));
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
// ---------------------------------------------------------------------------
|
|
639
|
-
// [1] Security Scan
|
|
640
|
-
// ---------------------------------------------------------------------------
|
|
641
|
-
async function actionScan() {
|
|
642
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5B89\u5168\u6383\u63CF' : 'Security Scan']);
|
|
643
|
-
const scanTitle = currentLang === 'zh-TW' ? '\u5B89\u5168\u6383\u63CF' : 'Security Scan';
|
|
644
|
-
console.log(` ${theme.brandBold(scanTitle)}`);
|
|
645
|
-
console.log('');
|
|
646
|
-
const depthItems = [
|
|
647
|
-
{
|
|
648
|
-
key: '1',
|
|
649
|
-
label: currentLang === 'zh-TW'
|
|
650
|
-
? '\u5FEB\u901F\u6383\u63CF (~30 \u79D2)'
|
|
651
|
-
: 'Quick Scan (~30 seconds)',
|
|
652
|
-
},
|
|
653
|
-
{
|
|
654
|
-
key: '2',
|
|
655
|
-
label: currentLang === 'zh-TW' ? '\u5B8C\u6574\u6383\u63CF' : 'Full Scan',
|
|
656
|
-
},
|
|
657
|
-
];
|
|
658
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u6383\u63CF\u6A21\u5F0F' : 'Scan Mode', depthItems);
|
|
659
|
-
const choice = await waitForCompactChoice(depthItems, currentLang);
|
|
660
|
-
if (!choice)
|
|
661
|
-
return;
|
|
662
|
-
const depth = choice.key === '1' ? 'quick' : 'full';
|
|
663
|
-
console.log('');
|
|
664
|
-
const { runScan } = await import('@panguard-ai/panguard-scan');
|
|
665
|
-
const sp = spinner(currentLang === 'zh-TW'
|
|
666
|
-
? '\u6B63\u5728\u6383\u63CF\u7CFB\u7D71\u5B89\u5168...'
|
|
667
|
-
: 'Scanning system...');
|
|
668
|
-
const result = await runScan({ depth, lang: currentLang, verbose: false });
|
|
669
|
-
sp.succeed(currentLang === 'zh-TW'
|
|
670
|
-
? `\u6383\u63CF\u5B8C\u6210 ${c.dim(`(${formatDuration(result.scanDuration)})`)}`
|
|
671
|
-
: `Scan complete ${c.dim(`(${formatDuration(result.scanDuration)})`)}`);
|
|
672
|
-
const safetyScore = Math.max(0, 100 - result.riskScore);
|
|
673
|
-
const grade = safetyScore >= 90
|
|
674
|
-
? 'A'
|
|
675
|
-
: safetyScore >= 75
|
|
676
|
-
? 'B'
|
|
677
|
-
: safetyScore >= 60
|
|
678
|
-
? 'C'
|
|
679
|
-
: safetyScore >= 40
|
|
680
|
-
? 'D'
|
|
681
|
-
: 'F';
|
|
682
|
-
console.log('');
|
|
683
|
-
const scoreLabel = currentLang === 'zh-TW' ? '\u6383\u63CF\u5B8C\u6210' : 'Scan Complete';
|
|
684
|
-
console.log(` ${theme.title(scoreLabel)}${' '.repeat(30)}Score: ${c.bold(`${safetyScore}/100`)} (${grade})`);
|
|
685
|
-
console.log('');
|
|
686
|
-
const { tier: _tier } = getLicense();
|
|
687
|
-
let fixableCount = 0;
|
|
688
|
-
if (result.findings.length > 0) {
|
|
689
|
-
for (const f of result.findings) {
|
|
690
|
-
const sev = colorSeverity(f.severity).padEnd(10);
|
|
691
|
-
console.log(` ${sev} ${f.title}`);
|
|
692
|
-
// Show description if available
|
|
693
|
-
if (f.description) {
|
|
694
|
-
console.log(c.dim(` ${f.description}`));
|
|
695
|
-
}
|
|
696
|
-
if (f.manualFix && f.manualFix.length > 0) {
|
|
697
|
-
const fixLabel = currentLang === 'zh-TW' ? '\u4FEE\u5FA9:' : 'Fix:';
|
|
698
|
-
console.log(c.dim(` ${fixLabel}`));
|
|
699
|
-
for (const cmd of f.manualFix) {
|
|
700
|
-
console.log(c.dim(` $ ${cmd}`));
|
|
701
|
-
}
|
|
702
|
-
fixableCount++;
|
|
703
|
-
}
|
|
704
|
-
else if (f.remediation) {
|
|
705
|
-
// Show recommendation even if no auto-fix command
|
|
706
|
-
const recLabel = currentLang === 'zh-TW' ? '\u5EFA\u8B70:' : 'Recommendation:';
|
|
707
|
-
console.log(c.dim(` ${recLabel} ${f.remediation}`));
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
console.log('');
|
|
711
|
-
const issuesText = currentLang === 'zh-TW'
|
|
712
|
-
? `${result.findings.length} \u500B\u554F\u984C`
|
|
713
|
-
: `${result.findings.length} issue(s) found`;
|
|
714
|
-
console.log(c.dim(` ${issuesText}`));
|
|
715
|
-
if (fixableCount > 0) {
|
|
716
|
-
const upgradeLines = currentLang === 'zh-TW'
|
|
717
|
-
? [`\u53EF\u81EA\u52D5\u4FEE\u5FA9:`, `$ panguard scan --fix`]
|
|
718
|
-
: [`Auto-fix available:`, `$ panguard scan --fix`];
|
|
719
|
-
console.log('');
|
|
720
|
-
console.log(box(upgradeLines.join('\n'), { borderColor: c.sage }));
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
else {
|
|
724
|
-
const noIssues = currentLang === 'zh-TW'
|
|
725
|
-
? '\u672A\u767C\u73FE\u5B89\u5168\u554F\u984C'
|
|
726
|
-
: 'No security issues found';
|
|
727
|
-
console.log(` ${c.safe(noIssues)}`);
|
|
728
|
-
}
|
|
729
|
-
// Agent auto-suggestion: offer to activate Guard if not running
|
|
730
|
-
const guardRunning = isGuardRunning().running;
|
|
731
|
-
if (!guardRunning) {
|
|
732
|
-
console.log('');
|
|
733
|
-
const agentMsg = currentLang === 'zh-TW'
|
|
734
|
-
? `${c.sage('\u25C6')} \u5373\u6642\u9632\u8B77\u5C1A\u672A\u555F\u52D5\u3002\u8981\u73FE\u5728\u555F\u7528\u55CE\uFF1F`
|
|
735
|
-
: `${c.sage('\u25C6')} Real-time protection is not active. Enable now?`;
|
|
736
|
-
console.log(` ${agentMsg}`);
|
|
737
|
-
const guardItems = [
|
|
738
|
-
{
|
|
739
|
-
key: '1',
|
|
740
|
-
label: currentLang === 'zh-TW'
|
|
741
|
-
? '\u662F\uFF0C\u555F\u52D5 Guard \u9632\u8B77'
|
|
742
|
-
: 'Yes, start Guard protection',
|
|
743
|
-
},
|
|
744
|
-
{
|
|
745
|
-
key: '2',
|
|
746
|
-
label: currentLang === 'zh-TW'
|
|
747
|
-
? '\u5BE9\u8A08\u5DF2\u5B89\u88DD\u6280\u80FD\u7684\u5B89\u5168\u5A01\u8105'
|
|
748
|
-
: 'Audit installed skills for threats',
|
|
749
|
-
},
|
|
750
|
-
{
|
|
751
|
-
key: '3',
|
|
752
|
-
label: currentLang === 'zh-TW'
|
|
753
|
-
? '\u4E0D\u7528\uFF0C\u56DE\u4E3B\u9078\u55AE'
|
|
754
|
-
: 'No, return to main menu',
|
|
755
|
-
},
|
|
756
|
-
];
|
|
757
|
-
renderCompactMenu('', guardItems);
|
|
758
|
-
const guardChoice = await waitForCompactChoice(guardItems, currentLang);
|
|
759
|
-
if (guardChoice?.key === '1') {
|
|
760
|
-
await actionGuard();
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
if (guardChoice?.key === '2') {
|
|
764
|
-
await actionAudit();
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
else {
|
|
769
|
-
nextSteps(currentLang === 'zh-TW'
|
|
770
|
-
? [
|
|
771
|
-
{
|
|
772
|
-
cmd: '[8] \u6280\u80FD\u5BE9\u8A08',
|
|
773
|
-
desc: '\u5BE9\u8A08\u5DF2\u5B89\u88DD\u6280\u80FD\u7684\u5B89\u5168\u5A01\u8105',
|
|
774
|
-
},
|
|
775
|
-
{ cmd: 'scan --full', desc: '\u57F7\u884C\u5B8C\u6574\u6383\u63CF' },
|
|
776
|
-
{ cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
|
|
777
|
-
]
|
|
778
|
-
: [
|
|
779
|
-
{ cmd: '[8] Skill Auditor', desc: 'Audit installed skills for threats' },
|
|
780
|
-
{ cmd: 'scan --full', desc: 'Run a comprehensive scan' },
|
|
781
|
-
{ cmd: 'guard start', desc: 'Enable real-time protection' },
|
|
782
|
-
], currentLang);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
// ---------------------------------------------------------------------------
|
|
786
|
-
// [2] Compliance Report
|
|
787
|
-
// ---------------------------------------------------------------------------
|
|
788
|
-
async function actionReport() {
|
|
789
|
-
breadcrumb([
|
|
790
|
-
'Panguard',
|
|
791
|
-
currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Compliance Report',
|
|
792
|
-
]);
|
|
793
|
-
console.log('');
|
|
794
|
-
if (currentLang === 'zh-TW') {
|
|
795
|
-
console.log(' \u5408\u898F\u5831\u544A \u2014 \u5373\u5C07\u63A8\u51FA');
|
|
796
|
-
console.log('');
|
|
797
|
-
console.log(' ISO 27001\u3001SOC 2\u3001\u8CC7\u5B89\u7BA1\u7406\u6CD5\u5408\u898F\u5831\u544A\u529F\u80FD\u958B\u767C\u4E2D\u3002');
|
|
798
|
-
console.log(' \u8FFD\u8E64\u9032\u5EA6: https://github.com/panguard-ai/panguard-ai');
|
|
799
|
-
}
|
|
800
|
-
else {
|
|
801
|
-
console.log(' Compliance Report \u2014 Coming Soon');
|
|
802
|
-
console.log('');
|
|
803
|
-
console.log(' ISO 27001, SOC 2, and TW Cyber Security Act compliance');
|
|
804
|
-
console.log(' reports are under active development.');
|
|
805
|
-
console.log(' Follow progress: https://github.com/panguard-ai/panguard-ai');
|
|
806
|
-
}
|
|
807
|
-
console.log('');
|
|
808
|
-
}
|
|
809
|
-
// ---------------------------------------------------------------------------
|
|
810
|
-
// [3] Guard Engine
|
|
811
|
-
// ---------------------------------------------------------------------------
|
|
812
|
-
async function actionGuard() {
|
|
813
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard Engine']);
|
|
814
|
-
const title = currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard Engine';
|
|
815
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
816
|
-
const guardInfo = isGuardRunning();
|
|
817
|
-
const guardRunning = guardInfo.running;
|
|
818
|
-
const statusText = guardRunning
|
|
819
|
-
? c.safe(currentLang === 'zh-TW' ? '\u904B\u884C\u4E2D' : 'Running')
|
|
820
|
-
: c.caution(currentLang === 'zh-TW' ? '\u672A\u904B\u884C' : 'Not running');
|
|
821
|
-
console.log(` ${c.dim(currentLang === 'zh-TW' ? '\u72C0\u614B' : 'Status')} ${statusText}`);
|
|
822
|
-
console.log('');
|
|
823
|
-
const items = [
|
|
824
|
-
{ key: '1', label: currentLang === 'zh-TW' ? '\u555F\u52D5\u5F15\u64CE Start' : 'Start' },
|
|
825
|
-
{ key: '2', label: currentLang === 'zh-TW' ? '\u505C\u6B62\u5F15\u64CE Stop' : 'Stop' },
|
|
826
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u72C0\u614B Status' : 'Status' },
|
|
827
|
-
{ key: '4', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u65E5\u8A8C Logs' : 'Logs' },
|
|
828
|
-
];
|
|
829
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard Engine', items);
|
|
830
|
-
const choice = await waitForCompactChoice(items, currentLang);
|
|
831
|
-
if (!choice)
|
|
832
|
-
return;
|
|
833
|
-
console.log('');
|
|
834
|
-
const { runCLI } = await import('@panguard-ai/panguard-guard');
|
|
835
|
-
switch (choice.key) {
|
|
836
|
-
case '1': {
|
|
837
|
-
// Start guard engine
|
|
838
|
-
if (guardRunning) {
|
|
839
|
-
console.log(` ${c.safe('\u2713')} ${currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE\u5DF2\u5728\u904B\u884C\u4E2D' : 'Guard engine is already running'} ${c.dim(`(PID: ${guardInfo.pid})`)}`);
|
|
840
|
-
nextSteps(currentLang === 'zh-TW'
|
|
841
|
-
? [
|
|
842
|
-
{
|
|
843
|
-
cmd: 'guard > \u67E5\u770B\u72C0\u614B',
|
|
844
|
-
desc: '\u67E5\u770B\u8A73\u7D30\u72C0\u614B',
|
|
845
|
-
},
|
|
846
|
-
{ cmd: 'guard > \u505C\u6B62\u5F15\u64CE', desc: '\u505C\u6B62\u9632\u8B77' },
|
|
847
|
-
]
|
|
848
|
-
: [
|
|
849
|
-
{ cmd: 'guard > Status', desc: 'View detailed status' },
|
|
850
|
-
{ cmd: 'guard > Stop', desc: 'Stop protection' },
|
|
851
|
-
], currentLang);
|
|
852
|
-
break;
|
|
853
|
-
}
|
|
854
|
-
// Show Guard capabilities
|
|
855
|
-
console.log(` ${c.sage('\u25C6')} Guard active${' '.repeat(30)}All Layers \u00B7 Free`);
|
|
856
|
-
console.log('');
|
|
857
|
-
if (currentLang === 'zh-TW') {
|
|
858
|
-
console.log(` ${c.safe('\u2713')} \u5DF2\u77E5\u653B\u64CA\u6A21\u5F0F\u81EA\u52D5\u5C01\u9396`);
|
|
859
|
-
console.log(` ${c.safe('\u2713')} Threat Cloud \u5A01\u8105\u60C5\u5831`);
|
|
860
|
-
console.log(` ${c.safe('\u2713')} AI \u5206\u6790`);
|
|
861
|
-
console.log(` ${c.safe('\u2713')} \u901A\u77E5\u7CFB\u7D71`);
|
|
862
|
-
console.log(` ${c.safe('\u2713')} \u65E5\u8A8C\u4FDD\u7559`);
|
|
863
|
-
}
|
|
864
|
-
else {
|
|
865
|
-
console.log(` ${c.safe('\u2713')} Auto-blocking for known attack patterns`);
|
|
866
|
-
console.log(` ${c.safe('\u2713')} Threat Cloud intelligence`);
|
|
867
|
-
console.log(` ${c.safe('\u2713')} AI analysis`);
|
|
868
|
-
console.log(` ${c.safe('\u2713')} Notifications`);
|
|
869
|
-
console.log(` ${c.safe('\u2713')} Log retention`);
|
|
870
|
-
}
|
|
871
|
-
console.log('');
|
|
872
|
-
// Spawn guard as background process
|
|
873
|
-
const { spawn: spawnProcess } = await import('node:child_process');
|
|
874
|
-
const { fileURLToPath: toPath } = await import('node:url');
|
|
875
|
-
const guardMainUrl = import.meta.resolve('@panguard-ai/panguard-guard');
|
|
876
|
-
const guardCliScript = join(toPath(guardMainUrl), '..', 'cli', 'index.js');
|
|
877
|
-
const guardDataDir = join(homedir(), '.panguard-guard');
|
|
878
|
-
if (!existsSync(guardDataDir))
|
|
879
|
-
mkdirSync(guardDataDir, { recursive: true });
|
|
880
|
-
const logPath = join(guardDataDir, 'guard.log');
|
|
881
|
-
const logFd = openSync(logPath, 'a');
|
|
882
|
-
const guardSp = spinner(currentLang === 'zh-TW'
|
|
883
|
-
? '\u6B63\u5728\u555F\u52D5\u5B88\u8B77\u5F15\u64CE...'
|
|
884
|
-
: 'Starting guard engine...');
|
|
885
|
-
const child = spawnProcess(process.execPath, [guardCliScript, 'start'], {
|
|
886
|
-
detached: true,
|
|
887
|
-
stdio: ['ignore', logFd, logFd],
|
|
888
|
-
env: { ...process.env },
|
|
889
|
-
});
|
|
890
|
-
child.unref();
|
|
891
|
-
closeSync(logFd);
|
|
892
|
-
// Wait for PID file to confirm startup
|
|
893
|
-
const pidPath = join(guardDataDir, 'panguard-guard.pid');
|
|
894
|
-
let started = false;
|
|
895
|
-
const deadline = Date.now() + 5000;
|
|
896
|
-
while (Date.now() < deadline) {
|
|
897
|
-
if (existsSync(pidPath)) {
|
|
898
|
-
try {
|
|
899
|
-
const newPid = parseInt(readFileSync(pidPath, 'utf-8').trim(), 10);
|
|
900
|
-
process.kill(newPid, 0);
|
|
901
|
-
started = true;
|
|
902
|
-
break;
|
|
903
|
-
}
|
|
904
|
-
catch {
|
|
905
|
-
/* not yet */
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
909
|
-
}
|
|
910
|
-
if (started) {
|
|
911
|
-
const newPid = parseInt(readFileSync(pidPath, 'utf-8').trim(), 10);
|
|
912
|
-
guardSp.succeed(currentLang === 'zh-TW'
|
|
913
|
-
? '\u5B88\u8B77\u5F15\u64CE\u5DF2\u555F\u52D5'
|
|
914
|
-
: 'Guard engine started');
|
|
915
|
-
console.log('');
|
|
916
|
-
console.log(` ${c.safe('\u2713')} ${currentLang === 'zh-TW' ? '\u7CFB\u7D71\u5DF2\u53D7\u4FDD\u8B77' : 'System is now protected'} ${c.dim(`(PID: ${newPid})`)}`);
|
|
917
|
-
console.log(` ${c.dim(currentLang === 'zh-TW' ? ' \u65E5\u8A8C: ~/.panguard-guard/guard.log' : ' Logs: ~/.panguard-guard/guard.log')}`);
|
|
918
|
-
}
|
|
919
|
-
else {
|
|
920
|
-
guardSp.fail(currentLang === 'zh-TW'
|
|
921
|
-
? '\u5B88\u8B77\u5F15\u64CE\u555F\u52D5\u5931\u6557'
|
|
922
|
-
: 'Failed to start guard engine');
|
|
923
|
-
console.log(` ${c.dim(currentLang === 'zh-TW' ? ' \u67E5\u770B\u65E5\u8A8C: ~/.panguard-guard/guard.log' : ' Check logs: ~/.panguard-guard/guard.log')}`);
|
|
924
|
-
}
|
|
925
|
-
nextSteps(currentLang === 'zh-TW'
|
|
926
|
-
? [
|
|
927
|
-
{ cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
|
|
928
|
-
{ cmd: 'status', desc: '\u67E5\u770B\u7CFB\u7D71\u72C0\u614B' },
|
|
929
|
-
]
|
|
930
|
-
: [
|
|
931
|
-
{ cmd: 'scan', desc: 'Run a security scan' },
|
|
932
|
-
{ cmd: 'status', desc: 'Check system status' },
|
|
933
|
-
], currentLang);
|
|
934
|
-
break;
|
|
935
|
-
}
|
|
936
|
-
case '2': {
|
|
937
|
-
const confirmed = await confirmDestructive(currentLang === 'zh-TW'
|
|
938
|
-
? '\u78BA\u5B9A\u8981\u505C\u6B62\u5373\u6642\u9632\u8B77\uFF1F'
|
|
939
|
-
: 'Stop real-time protection?', currentLang);
|
|
940
|
-
if (confirmed) {
|
|
941
|
-
await runCLI(['stop']);
|
|
942
|
-
}
|
|
943
|
-
else {
|
|
944
|
-
console.log(c.dim(currentLang === 'zh-TW' ? ' \u5DF2\u53D6\u6D88' : ' Cancelled'));
|
|
945
|
-
}
|
|
946
|
-
break;
|
|
947
|
-
}
|
|
948
|
-
case '3':
|
|
949
|
-
await runCLI(['status']);
|
|
950
|
-
break;
|
|
951
|
-
case '4': {
|
|
952
|
-
const logFilePath = join(homedir(), '.panguard-guard', 'guard.log');
|
|
953
|
-
if (existsSync(logFilePath)) {
|
|
954
|
-
const content = readFileSync(logFilePath, 'utf-8');
|
|
955
|
-
const lines = content.trim().split('\n').slice(-30);
|
|
956
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
957
|
-
? ' \u6700\u8FD1 30 \u884C\u65E5\u8A8C:'
|
|
958
|
-
: ' Last 30 log lines:'));
|
|
959
|
-
console.log('');
|
|
960
|
-
for (const line of lines) {
|
|
961
|
-
console.log(` ${c.dim(line)}`);
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
else {
|
|
965
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
966
|
-
? ' \u5C1A\u7121\u65E5\u8A8C\u6A94\u6848'
|
|
967
|
-
: ' No log file found'));
|
|
968
|
-
}
|
|
969
|
-
break;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
// ---------------------------------------------------------------------------
|
|
974
|
-
// [4] Honeypot System
|
|
975
|
-
// ---------------------------------------------------------------------------
|
|
976
|
-
async function actionTrap() {
|
|
977
|
-
breadcrumb([
|
|
978
|
-
'Panguard',
|
|
979
|
-
currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System',
|
|
980
|
-
]);
|
|
981
|
-
console.log('');
|
|
982
|
-
if (currentLang === 'zh-TW') {
|
|
983
|
-
console.log(' \u871C\u7F50\u7CFB\u7D71 \u2014 \u5373\u5C07\u63A8\u51FA');
|
|
984
|
-
console.log('');
|
|
985
|
-
console.log(' \u8A98\u990C\u670D\u52D9\uFF08SSH\u3001HTTP\u3001FTP\uFF09\u5075\u6E2C\u8207\u5206\u6790\u653B\u64CA\u8005\u529F\u80FD\u958B\u767C\u4E2D\u3002');
|
|
986
|
-
console.log(' \u8FFD\u8E64\u9032\u5EA6: https://github.com/panguard-ai/panguard-ai');
|
|
987
|
-
}
|
|
988
|
-
else {
|
|
989
|
-
console.log(' Honeypot System \u2014 Coming Soon');
|
|
990
|
-
console.log('');
|
|
991
|
-
console.log(' Decoy services (SSH, HTTP, FTP) to detect and profile');
|
|
992
|
-
console.log(' attackers are under active development.');
|
|
993
|
-
console.log(' Follow progress: https://github.com/panguard-ai/panguard-ai');
|
|
994
|
-
}
|
|
995
|
-
console.log('');
|
|
996
|
-
}
|
|
997
|
-
// ---------------------------------------------------------------------------
|
|
998
|
-
// [5] Notifications
|
|
999
|
-
// ---------------------------------------------------------------------------
|
|
1000
|
-
async function actionChat() {
|
|
1001
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u901A\u77E5\u7CFB\u7D71' : 'Notifications']);
|
|
1002
|
-
const title = currentLang === 'zh-TW' ? '\u901A\u77E5\u7CFB\u7D71' : 'Notifications';
|
|
1003
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
1004
|
-
console.log('');
|
|
1005
|
-
const items = [
|
|
1006
|
-
{
|
|
1007
|
-
key: '1',
|
|
1008
|
-
label: currentLang === 'zh-TW' ? '\u8A2D\u5B9A\u901A\u77E5\u7BA1\u9053' : 'Setup Channels',
|
|
1009
|
-
},
|
|
1010
|
-
{ key: '2', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u72C0\u614B' : 'Status' },
|
|
1011
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\u6E2C\u8A66\u767C\u9001' : 'Test Send' },
|
|
1012
|
-
];
|
|
1013
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u901A\u77E5\u7CFB\u7D71' : 'Notifications', items);
|
|
1014
|
-
const choice = await waitForCompactChoice(items, currentLang);
|
|
1015
|
-
if (!choice)
|
|
1016
|
-
return;
|
|
1017
|
-
console.log('');
|
|
1018
|
-
const { runCLI: chatRunCLI } = await import('@panguard-ai/panguard-chat');
|
|
1019
|
-
switch (choice.key) {
|
|
1020
|
-
case '1':
|
|
1021
|
-
await chatRunCLI(['setup', '--lang', currentLang]);
|
|
1022
|
-
break;
|
|
1023
|
-
case '2':
|
|
1024
|
-
await chatRunCLI(['status']);
|
|
1025
|
-
break;
|
|
1026
|
-
case '3':
|
|
1027
|
-
await chatRunCLI(['config']);
|
|
1028
|
-
break;
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
// ---------------------------------------------------------------------------
|
|
1032
|
-
// [6] Threat Cloud
|
|
1033
|
-
// ---------------------------------------------------------------------------
|
|
1034
|
-
async function actionThreat() {
|
|
1035
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5A01\u8105\u60C5\u5831' : 'Threat Cloud']);
|
|
1036
|
-
const title = currentLang === 'zh-TW' ? '\u5A01\u8105\u60C5\u5831' : 'Threat Cloud';
|
|
1037
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
1038
|
-
console.log('');
|
|
1039
|
-
const port = 8080;
|
|
1040
|
-
const net = await import('node:net');
|
|
1041
|
-
const portAvailable = await new Promise((resolve) => {
|
|
1042
|
-
const tester = net.createServer();
|
|
1043
|
-
tester.once('error', () => resolve(false));
|
|
1044
|
-
tester.once('listening', () => {
|
|
1045
|
-
tester.close(() => resolve(true));
|
|
1046
|
-
});
|
|
1047
|
-
tester.listen(port, '127.0.0.1');
|
|
1048
|
-
});
|
|
1049
|
-
if (!portAvailable) {
|
|
1050
|
-
console.log(formatError(currentLang === 'zh-TW'
|
|
1051
|
-
? `\u9023\u63A5\u57E0 ${port} \u5DF2\u88AB\u4F54\u7528`
|
|
1052
|
-
: `Port ${port} is already in use`, `Port ${port}`, currentLang === 'zh-TW'
|
|
1053
|
-
? `\u91CB\u653E\u9023\u63A5\u57E0 ${port} \u5F8C\u518D\u8A66`
|
|
1054
|
-
: `Free port ${port} and try again`));
|
|
1055
|
-
return;
|
|
1056
|
-
}
|
|
1057
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1058
|
-
? ` \u5373\u5C07\u555F\u52D5\u5A01\u8105\u60C5\u5831 REST API \u4F3A\u670D\u5668 (port ${port})`
|
|
1059
|
-
: ` Starting Threat Cloud REST API server (port ${port})...`));
|
|
1060
|
-
console.log('');
|
|
1061
|
-
let ThreatCloudServer;
|
|
1062
|
-
try {
|
|
1063
|
-
const mod = '@panguard-ai/threat-cloud';
|
|
1064
|
-
const tc = await import(/* webpackIgnore: true */ mod);
|
|
1065
|
-
ThreatCloudServer = tc.ThreatCloudServer;
|
|
1066
|
-
}
|
|
1067
|
-
catch {
|
|
1068
|
-
console.log(c.red(' Threat Cloud server package is not available.'));
|
|
1069
|
-
console.log(c.dim(' Your Guard client connects to Threat Cloud automatically.'));
|
|
1070
|
-
return;
|
|
1071
|
-
}
|
|
1072
|
-
const sp = spinner(currentLang === 'zh-TW'
|
|
1073
|
-
? '\u6B63\u5728\u555F\u52D5 Threat Cloud API...'
|
|
1074
|
-
: 'Starting Threat Cloud API server...');
|
|
1075
|
-
const server = new ThreatCloudServer({
|
|
1076
|
-
port: 8080,
|
|
1077
|
-
host: '127.0.0.1',
|
|
1078
|
-
dbPath: './threat-cloud.db',
|
|
1079
|
-
apiKeyRequired: false,
|
|
1080
|
-
apiKeys: [],
|
|
1081
|
-
rateLimitPerMinute: 120,
|
|
1082
|
-
});
|
|
1083
|
-
try {
|
|
1084
|
-
await server.start();
|
|
1085
|
-
sp.succeed(currentLang === 'zh-TW' ? 'Threat Cloud API \u5DF2\u555F\u52D5' : 'Threat Cloud API started');
|
|
1086
|
-
console.log('');
|
|
1087
|
-
console.log(` URL ${c.underline('http://127.0.0.1:8080')}`);
|
|
1088
|
-
console.log(` Health ${c.sage('http://127.0.0.1:8080/health')}`);
|
|
1089
|
-
console.log(` API ${c.sage('http://127.0.0.1:8080/api/stats')}`);
|
|
1090
|
-
console.log('');
|
|
1091
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1092
|
-
? ' \u6309\u4EFB\u610F\u9375\u505C\u6B62\u4F3A\u670D\u5668...'
|
|
1093
|
-
: ' Press any key to stop...'));
|
|
1094
|
-
await pressAnyKey(currentLang);
|
|
1095
|
-
await server.stop();
|
|
1096
|
-
console.log(` ${c.safe(currentLang === 'zh-TW' ? 'Threat Cloud \u5DF2\u505C\u6B62' : 'Threat Cloud stopped')}`);
|
|
1097
|
-
}
|
|
1098
|
-
catch (err) {
|
|
1099
|
-
sp.fail(`${err instanceof Error ? err.message : String(err)}`);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
// ---------------------------------------------------------------------------
|
|
1103
|
-
// [7] Auto Demo
|
|
1104
|
-
// ---------------------------------------------------------------------------
|
|
1105
|
-
async function actionDemo() {
|
|
1106
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo']);
|
|
1107
|
-
const { runScan } = await import('@panguard-ai/panguard-scan');
|
|
1108
|
-
const title = currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo';
|
|
1109
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
1110
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1111
|
-
? ' \u6B63\u5728\u57F7\u884C\u6240\u6709\u5B89\u5168\u6A21\u7D44...'
|
|
1112
|
-
: ' Running through all security modules...'));
|
|
1113
|
-
console.log('');
|
|
1114
|
-
const results = [];
|
|
1115
|
-
// 1. Security Scan
|
|
1116
|
-
console.log(c.dim(currentLang === 'zh-TW' ? ' 1. \u5B89\u5168\u6383\u63CF' : ' 1. Security Scan'));
|
|
1117
|
-
console.log('');
|
|
1118
|
-
const scanSp = spinner(currentLang === 'zh-TW'
|
|
1119
|
-
? '\u6B63\u5728\u57F7\u884C\u5FEB\u901F\u6383\u63CF...'
|
|
1120
|
-
: 'Running quick scan...');
|
|
1121
|
-
try {
|
|
1122
|
-
const result = await runScan({ depth: 'quick', lang: currentLang, verbose: false });
|
|
1123
|
-
scanSp.succeed(`${currentLang === 'zh-TW' ? '\u6383\u63CF\u5B8C\u6210' : 'Scan complete'} ${c.dim(`(${formatDuration(result.scanDuration)})`)}`);
|
|
1124
|
-
const safetyScore = Math.max(0, 100 - result.riskScore);
|
|
1125
|
-
const grade = safetyScore >= 90
|
|
1126
|
-
? 'A'
|
|
1127
|
-
: safetyScore >= 75
|
|
1128
|
-
? 'B'
|
|
1129
|
-
: safetyScore >= 60
|
|
1130
|
-
? 'C'
|
|
1131
|
-
: safetyScore >= 40
|
|
1132
|
-
? 'D'
|
|
1133
|
-
: 'F';
|
|
1134
|
-
console.log(` Score: ${c.bold(`${safetyScore}/100`)} (${grade}) | Findings: ${result.findings.length}`);
|
|
1135
|
-
results.push({
|
|
1136
|
-
name: currentLang === 'zh-TW' ? '\u5B89\u5168\u6383\u63CF' : 'Scan',
|
|
1137
|
-
status: 'ok',
|
|
1138
|
-
});
|
|
1139
|
-
}
|
|
1140
|
-
catch (err) {
|
|
1141
|
-
scanSp.fail(`${err instanceof Error ? err.message : err}`);
|
|
1142
|
-
results.push({
|
|
1143
|
-
name: currentLang === 'zh-TW' ? '\u5B89\u5168\u6383\u63CF' : 'Scan',
|
|
1144
|
-
status: 'fail',
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
console.log('');
|
|
1148
|
-
// 2. Compliance Report (Coming Soon)
|
|
1149
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1150
|
-
? ' 2. \u5408\u898F\u5831\u544A \u2014 \u5373\u5C07\u63A8\u51FA'
|
|
1151
|
-
: ' 2. Compliance Report \u2014 Coming Soon'));
|
|
1152
|
-
results.push({
|
|
1153
|
-
name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
|
|
1154
|
-
status: 'warn',
|
|
1155
|
-
});
|
|
1156
|
-
console.log('');
|
|
1157
|
-
// 3. Guard Engine
|
|
1158
|
-
console.log(c.dim(currentLang === 'zh-TW' ? ' 3. \u5B88\u8B77\u5F15\u64CE' : ' 3. Guard Engine'));
|
|
1159
|
-
console.log('');
|
|
1160
|
-
try {
|
|
1161
|
-
const { runCLI: guardCLI } = await import('@panguard-ai/panguard-guard');
|
|
1162
|
-
await guardCLI(['status']);
|
|
1163
|
-
results.push({
|
|
1164
|
-
name: currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard',
|
|
1165
|
-
status: 'ok',
|
|
1166
|
-
});
|
|
1167
|
-
}
|
|
1168
|
-
catch {
|
|
1169
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1170
|
-
? ' \u5B88\u8B77\u5F15\u64CE: \u672A\u904B\u884C (\u5C55\u793A\u6B63\u5E38)'
|
|
1171
|
-
: ' Guard engine: not running (normal for demo)'));
|
|
1172
|
-
results.push({
|
|
1173
|
-
name: currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard',
|
|
1174
|
-
status: 'warn',
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
console.log('');
|
|
1178
|
-
// 4. Honeypot (Coming Soon)
|
|
1179
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1180
|
-
? ' 4. \u871C\u7F50\u7CFB\u7D71 \u2014 \u5373\u5C07\u63A8\u51FA'
|
|
1181
|
-
: ' 4. Honeypot System \u2014 Coming Soon'));
|
|
1182
|
-
results.push({
|
|
1183
|
-
name: currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Trap',
|
|
1184
|
-
status: 'warn',
|
|
1185
|
-
});
|
|
1186
|
-
console.log('');
|
|
1187
|
-
// 5. Notifications
|
|
1188
|
-
console.log(c.dim(currentLang === 'zh-TW' ? ' 5. \u901A\u77E5\u7CFB\u7D71' : ' 5. Notification System'));
|
|
1189
|
-
console.log('');
|
|
1190
|
-
try {
|
|
1191
|
-
const { runCLI: chatCLI } = await import('@panguard-ai/panguard-chat');
|
|
1192
|
-
await chatCLI(['status']);
|
|
1193
|
-
results.push({
|
|
1194
|
-
name: currentLang === 'zh-TW' ? '\u901A\u77E5\u7CFB\u7D71' : 'Notify',
|
|
1195
|
-
status: 'ok',
|
|
1196
|
-
});
|
|
1197
|
-
}
|
|
1198
|
-
catch {
|
|
1199
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1200
|
-
? ' \u901A\u77E5\u7CFB\u7D71: \u5C1A\u672A\u914D\u7F6E'
|
|
1201
|
-
: ' Notification system: not configured'));
|
|
1202
|
-
results.push({
|
|
1203
|
-
name: currentLang === 'zh-TW' ? '\u901A\u77E5\u7CFB\u7D71' : 'Notify',
|
|
1204
|
-
status: 'warn',
|
|
1205
|
-
});
|
|
1206
|
-
}
|
|
1207
|
-
console.log('');
|
|
1208
|
-
// Summary
|
|
1209
|
-
console.log(` ${theme.brandBold(currentLang === 'zh-TW' ? '\u5C55\u793A\u5B8C\u6210' : 'Demo Complete')}`);
|
|
1210
|
-
console.log('');
|
|
1211
|
-
for (const r of results) {
|
|
1212
|
-
const icon = r.status === 'ok'
|
|
1213
|
-
? c.safe('\u2713')
|
|
1214
|
-
: r.status === 'warn'
|
|
1215
|
-
? c.caution('~')
|
|
1216
|
-
: c.critical('\u2717');
|
|
1217
|
-
console.log(` ${icon} ${r.name}`);
|
|
1218
|
-
}
|
|
1219
|
-
nextSteps(currentLang === 'zh-TW'
|
|
1220
|
-
? [
|
|
1221
|
-
{ cmd: 'init', desc: '\u521D\u59CB\u8A2D\u5B9A\u60A8\u7684\u74B0\u5883' },
|
|
1222
|
-
{ cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
|
|
1223
|
-
{ cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
|
|
1224
|
-
]
|
|
1225
|
-
: [
|
|
1226
|
-
{ cmd: 'init', desc: 'Set up your environment' },
|
|
1227
|
-
{ cmd: 'scan', desc: 'Run a security scan' },
|
|
1228
|
-
{ cmd: 'guard start', desc: 'Enable real-time protection' },
|
|
1229
|
-
], currentLang);
|
|
1230
|
-
}
|
|
1231
|
-
// ---------------------------------------------------------------------------
|
|
1232
|
-
// [8] Skill Auditor
|
|
1233
|
-
// ---------------------------------------------------------------------------
|
|
1234
|
-
async function actionAudit() {
|
|
1235
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08' : 'Skill Auditor']);
|
|
1236
|
-
const title = currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08' : 'Skill Auditor';
|
|
1237
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
1238
|
-
console.log('');
|
|
1239
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1240
|
-
? ' \u6383\u63CF AI \u4EE3\u7406\u6280\u80FD\u76EE\u9304\uFF08\u5305\u542B SKILL.md\uFF09\u4EE5\u5075\u6E2C\u5B89\u5168\u554F\u984C\u3002'
|
|
1241
|
-
: ' Scan an AI agent skill directory (containing SKILL.md) for security issues.'));
|
|
1242
|
-
console.log('');
|
|
1243
|
-
// Ask for path to audit
|
|
1244
|
-
const pathItems = [
|
|
1245
|
-
{
|
|
1246
|
-
key: '1',
|
|
1247
|
-
label: currentLang === 'zh-TW'
|
|
1248
|
-
? '\u5BE9\u8A08\u7576\u524D\u76EE\u9304 (.)'
|
|
1249
|
-
: 'Audit current directory (.)',
|
|
1250
|
-
},
|
|
1251
|
-
{
|
|
1252
|
-
key: '2',
|
|
1253
|
-
label: currentLang === 'zh-TW' ? '\u8F38\u5165\u81EA\u5B9A\u8DEF\u5F91' : 'Enter custom path',
|
|
1254
|
-
},
|
|
1255
|
-
];
|
|
1256
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u9078\u64C7\u76EE\u6A19' : 'Select target', pathItems);
|
|
1257
|
-
const choice = await waitForCompactChoice(pathItems, currentLang);
|
|
1258
|
-
if (!choice)
|
|
1259
|
-
return;
|
|
1260
|
-
let targetPath = process.cwd();
|
|
1261
|
-
if (choice.key === '2') {
|
|
1262
|
-
// Read a custom path from the user via text prompt
|
|
1263
|
-
const readline = await import('node:readline');
|
|
1264
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1265
|
-
targetPath = await new Promise((resolve) => {
|
|
1266
|
-
const prompt = currentLang === 'zh-TW' ? ' \u8DEF\u5F91: ' : ' Path: ';
|
|
1267
|
-
rl.question(prompt, (answer) => {
|
|
1268
|
-
rl.close();
|
|
1269
|
-
resolve(answer.trim() || process.cwd());
|
|
1270
|
-
});
|
|
1271
|
-
});
|
|
1272
|
-
}
|
|
1273
|
-
const path = await import('node:path');
|
|
1274
|
-
const resolvedPath = path.resolve(targetPath);
|
|
1275
|
-
console.log('');
|
|
1276
|
-
const sp = spinner(currentLang === 'zh-TW'
|
|
1277
|
-
? `\u6B63\u5728\u5BE9\u8A08 ${resolvedPath}...`
|
|
1278
|
-
: `Auditing ${resolvedPath}...`);
|
|
1279
|
-
try {
|
|
1280
|
-
const { auditSkill } = await import('@panguard-ai/panguard-skill-auditor');
|
|
1281
|
-
const report = await auditSkill(resolvedPath);
|
|
1282
|
-
sp.succeed(currentLang === 'zh-TW' ? '\u5BE9\u8A08\u5B8C\u6210' : 'Audit complete');
|
|
1283
|
-
// Display results
|
|
1284
|
-
const levelColors = {
|
|
1285
|
-
LOW: c.safe,
|
|
1286
|
-
MEDIUM: c.caution,
|
|
1287
|
-
HIGH: c.critical,
|
|
1288
|
-
CRITICAL: (s) => c.bold(c.critical(s)),
|
|
1289
|
-
};
|
|
1290
|
-
const colorFn = levelColors[report.riskLevel] ?? c.dim;
|
|
1291
|
-
console.log('');
|
|
1292
|
-
console.log(box([
|
|
1293
|
-
`${c.bold(currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08\u5831\u544A' : 'Skill Audit Report')}`,
|
|
1294
|
-
'',
|
|
1295
|
-
`${currentLang === 'zh-TW' ? '\u6280\u80FD' : 'Skill'}: ${report.manifest?.name ?? 'Unknown'}${report.manifest?.metadata?.version ? ` v${report.manifest.metadata.version}` : ''}`,
|
|
1296
|
-
`${currentLang === 'zh-TW' ? '\u4F5C\u8005' : 'Author'}: ${report.manifest?.metadata?.author ?? 'Unknown'}`,
|
|
1297
|
-
`${currentLang === 'zh-TW' ? '\u98A8\u96AA\u5206\u6578' : 'Risk Score'}: ${colorFn(`${report.riskScore}/100 (${report.riskLevel})`)}`,
|
|
1298
|
-
`${currentLang === 'zh-TW' ? '\u6642\u9593' : 'Duration'}: ${report.durationMs}ms`,
|
|
1299
|
-
].join('\n'), { borderColor: c.sage }));
|
|
1300
|
-
console.log('');
|
|
1301
|
-
for (const check of report.checks) {
|
|
1302
|
-
const icon = check.status === 'pass'
|
|
1303
|
-
? c.safe('\u2713')
|
|
1304
|
-
: check.status === 'fail'
|
|
1305
|
-
? c.critical('\u2717')
|
|
1306
|
-
: check.status === 'warn'
|
|
1307
|
-
? c.caution('~')
|
|
1308
|
-
: c.dim('i');
|
|
1309
|
-
const statusLabel = check.status === 'pass'
|
|
1310
|
-
? 'PASS'
|
|
1311
|
-
: check.status === 'fail'
|
|
1312
|
-
? 'FAIL'
|
|
1313
|
-
: check.status === 'warn'
|
|
1314
|
-
? 'WARN'
|
|
1315
|
-
: 'INFO';
|
|
1316
|
-
console.log(` ${icon} [${statusLabel}] ${check.label}`);
|
|
1317
|
-
}
|
|
1318
|
-
console.log('');
|
|
1319
|
-
if (report.findings.length > 0) {
|
|
1320
|
-
console.log(c.bold(currentLang === 'zh-TW'
|
|
1321
|
-
? ` \u767C\u73FE ${report.findings.length} \u500B\u554F\u984C:`
|
|
1322
|
-
: ` ${report.findings.length} finding(s):`));
|
|
1323
|
-
console.log('');
|
|
1324
|
-
for (const finding of report.findings) {
|
|
1325
|
-
const sevColor = finding.severity === 'critical'
|
|
1326
|
-
? c.critical
|
|
1327
|
-
: finding.severity === 'high'
|
|
1328
|
-
? c.caution
|
|
1329
|
-
: c.dim;
|
|
1330
|
-
console.log(` ${sevColor(`[${finding.severity.toUpperCase()}]`)} ${finding.title}`);
|
|
1331
|
-
console.log(` ${c.dim(finding.description)}`);
|
|
1332
|
-
if (finding.location)
|
|
1333
|
-
console.log(` ${c.dim(`at ${finding.location}`)}`);
|
|
1334
|
-
console.log('');
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
nextSteps(currentLang === 'zh-TW'
|
|
1338
|
-
? [
|
|
1339
|
-
{ cmd: 'scan', desc: '\u57F7\u884C\u7CFB\u7D71\u5B89\u5168\u6383\u63CF' },
|
|
1340
|
-
{ cmd: 'guard start', desc: '\u555F\u52D5 24/7 \u5B88\u8B77\u9632\u8B77' },
|
|
1341
|
-
]
|
|
1342
|
-
: [
|
|
1343
|
-
{ cmd: 'scan', desc: 'Run a system security scan' },
|
|
1344
|
-
{ cmd: 'guard start', desc: 'Start 24/7 guard protection' },
|
|
1345
|
-
], currentLang);
|
|
1346
|
-
}
|
|
1347
|
-
catch (err) {
|
|
1348
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1349
|
-
const isNoSkill = errMsg.toLowerCase().includes('skill.md') ||
|
|
1350
|
-
errMsg.toLowerCase().includes('not found') ||
|
|
1351
|
-
errMsg.toLowerCase().includes('enoent');
|
|
1352
|
-
if (isNoSkill) {
|
|
1353
|
-
sp.warn(currentLang === 'zh-TW'
|
|
1354
|
-
? '\u6B64\u76EE\u9304\u672A\u627E\u5230 AI \u6280\u80FD (SKILL.md)'
|
|
1355
|
-
: 'No AI skills (SKILL.md) found in this directory');
|
|
1356
|
-
console.log('');
|
|
1357
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1358
|
-
? ' Skill Auditor \u6383\u63CF\u5305\u542B SKILL.md \u7684 AI \u6280\u80FD\u76EE\u9304\u3002'
|
|
1359
|
-
: ' Skill Auditor scans AI skill directories containing a SKILL.md file.'));
|
|
1360
|
-
console.log('');
|
|
1361
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1362
|
-
? ' \u5E38\u898B\u6280\u80FD\u4F4D\u7F6E\uFF1A'
|
|
1363
|
-
: ' Common skill locations:'));
|
|
1364
|
-
console.log(c.dim(' ~/.claude/skills/'));
|
|
1365
|
-
console.log(c.dim(' ./.claude/skills/'));
|
|
1366
|
-
console.log(c.dim(' ./.mcp/'));
|
|
1367
|
-
console.log(c.dim(' ./skills/'));
|
|
1368
|
-
console.log('');
|
|
1369
|
-
// Auto-scan known skill directories
|
|
1370
|
-
const fs = await import('node:fs');
|
|
1371
|
-
const path = await import('node:path');
|
|
1372
|
-
const skillDirs = [
|
|
1373
|
-
path.join(homedir(), '.claude', 'skills'),
|
|
1374
|
-
path.join(process.cwd(), '.claude', 'skills'),
|
|
1375
|
-
path.join(process.cwd(), '.mcp'),
|
|
1376
|
-
path.join(process.cwd(), 'skills'),
|
|
1377
|
-
];
|
|
1378
|
-
const foundDirs = [];
|
|
1379
|
-
for (const dir of skillDirs) {
|
|
1380
|
-
if (fs.existsSync(dir)) {
|
|
1381
|
-
// Check for subdirectories that might have SKILL.md
|
|
1382
|
-
try {
|
|
1383
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1384
|
-
for (const entry of entries) {
|
|
1385
|
-
if (entry.isDirectory()) {
|
|
1386
|
-
const skillMdPath = path.join(dir, entry.name, 'SKILL.md');
|
|
1387
|
-
if (fs.existsSync(skillMdPath)) {
|
|
1388
|
-
foundDirs.push(path.join(dir, entry.name));
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
// Also check the directory itself
|
|
1393
|
-
if (fs.existsSync(path.join(dir, 'SKILL.md'))) {
|
|
1394
|
-
foundDirs.push(dir);
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
catch {
|
|
1398
|
-
/* skip inaccessible dirs */
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
if (foundDirs.length > 0) {
|
|
1403
|
-
console.log(c.sage(currentLang === 'zh-TW'
|
|
1404
|
-
? ` \u5728\u7CFB\u7D71\u4E2D\u627E\u5230 ${foundDirs.length} \u500B\u6280\u80FD\uFF1A`
|
|
1405
|
-
: ` Found ${foundDirs.length} skill(s) on your system:`));
|
|
1406
|
-
for (const dir of foundDirs) {
|
|
1407
|
-
console.log(c.dim(` ${dir}`));
|
|
1408
|
-
}
|
|
1409
|
-
console.log('');
|
|
1410
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1411
|
-
? ` \u57F7\u884C\uFF1Apanguard audit skill <path> \u4F86\u5BE9\u8A08\u7279\u5B9A\u6280\u80FD`
|
|
1412
|
-
: ` Run: panguard audit skill <path> to audit a specific skill`));
|
|
1413
|
-
}
|
|
1414
|
-
else {
|
|
1415
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1416
|
-
? ' \u7CFB\u7D71\u4E2D\u672A\u627E\u5230\u5DF2\u5B89\u88DD\u7684 AI \u6280\u80FD\u3002'
|
|
1417
|
-
: ' No installed AI skills found on your system.'));
|
|
1418
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1419
|
-
? ' \u5728\u5305\u542B AI \u6280\u80FD\u7684\u5C08\u6848\u76EE\u9304\u4E2D\u57F7\u884C\u6B64\u6307\u4EE4\u3002'
|
|
1420
|
-
: ' Run this command inside a project directory that contains AI skills.'));
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
else {
|
|
1424
|
-
sp.fail(currentLang === 'zh-TW' ? '\u5BE9\u8A08\u5931\u6557' : 'Audit failed');
|
|
1425
|
-
console.log(formatError(errMsg, currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08' : 'Skill Auditor', currentLang === 'zh-TW'
|
|
1426
|
-
? '\u8ACB\u91CD\u8A66\u6216\u6AA2\u67E5\u65E5\u8A8C'
|
|
1427
|
-
: 'Please retry or check logs'));
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
// ---------------------------------------------------------------------------
|
|
1432
|
-
// Prompt commands: Hardening
|
|
1433
|
-
// ---------------------------------------------------------------------------
|
|
1434
|
-
async function actionHardening() {
|
|
1435
|
-
breadcrumb([
|
|
1436
|
-
'Panguard',
|
|
1437
|
-
currentLang === 'zh-TW' ? '\u5B89\u5168\u52A0\u56FA' : 'Security Hardening',
|
|
1438
|
-
]);
|
|
1439
|
-
const title = currentLang === 'zh-TW' ? '\u5B89\u5168\u52A0\u56FA' : 'Security Hardening';
|
|
1440
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
1441
|
-
console.log('');
|
|
1442
|
-
const items = [
|
|
1443
|
-
{
|
|
1444
|
-
key: '1',
|
|
1445
|
-
label: currentLang === 'zh-TW' ? '\u57F7\u884C\u52A0\u56FA\u6383\u63CF' : 'Run hardening audit',
|
|
1446
|
-
},
|
|
1447
|
-
{ key: '2', label: currentLang === 'zh-TW' ? '\u81EA\u52D5\u4FEE\u5FA9' : 'Auto-fix issues' },
|
|
1448
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u5831\u544A' : 'View report' },
|
|
1449
|
-
];
|
|
1450
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u5B89\u5168\u52A0\u56FA' : 'Hardening', items);
|
|
1451
|
-
const choice = await waitForCompactChoice(items, currentLang);
|
|
1452
|
-
if (!choice)
|
|
1453
|
-
return;
|
|
1454
|
-
console.log('');
|
|
1455
|
-
const { hardeningCommand } = await import('./commands/hardening.js');
|
|
1456
|
-
const cmd = hardeningCommand();
|
|
1457
|
-
switch (choice.key) {
|
|
1458
|
-
case '1':
|
|
1459
|
-
await cmd.parseAsync(['hardening', 'audit'], { from: 'user' });
|
|
1460
|
-
break;
|
|
1461
|
-
case '2':
|
|
1462
|
-
await cmd.parseAsync(['hardening', 'fix'], { from: 'user' });
|
|
1463
|
-
break;
|
|
1464
|
-
case '3':
|
|
1465
|
-
await cmd.parseAsync(['hardening', 'report'], { from: 'user' });
|
|
225
|
+
await actionAudit(lang);
|
|
1466
226
|
break;
|
|
1467
227
|
}
|
|
1468
228
|
}
|
|
1469
|
-
// ---------------------------------------------------------------------------
|
|
1470
|
-
// Prompt commands: Status
|
|
1471
|
-
// ---------------------------------------------------------------------------
|
|
1472
|
-
async function actionStatus() {
|
|
1473
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u7CFB\u7D71\u72C0\u614B' : 'Status']);
|
|
1474
|
-
const { statusCommand } = await import('./commands/status.js');
|
|
1475
|
-
const cmd = statusCommand();
|
|
1476
|
-
await cmd.parseAsync(['status'], { from: 'user' });
|
|
1477
|
-
}
|
|
1478
|
-
// ---------------------------------------------------------------------------
|
|
1479
|
-
// Prompt commands: Config
|
|
1480
|
-
// ---------------------------------------------------------------------------
|
|
1481
|
-
async function actionConfig() {
|
|
1482
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u8A2D\u5B9A\u7BA1\u7406' : 'Settings']);
|
|
1483
|
-
const { configCommand } = await import('./commands/config.js');
|
|
1484
|
-
const cmd = configCommand();
|
|
1485
|
-
await cmd.parseAsync(['config'], { from: 'user' });
|
|
1486
|
-
}
|
|
1487
229
|
//# sourceMappingURL=interactive.js.map
|