@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.
Files changed (69) hide show
  1. package/dist/cli/commands/audit.d.ts.map +1 -1
  2. package/dist/cli/commands/audit.js +28 -1
  3. package/dist/cli/commands/audit.js.map +1 -1
  4. package/dist/cli/commands/serve-admin.d.ts +11 -0
  5. package/dist/cli/commands/serve-admin.d.ts.map +1 -0
  6. package/dist/cli/commands/serve-admin.js +302 -0
  7. package/dist/cli/commands/serve-admin.js.map +1 -0
  8. package/dist/cli/commands/serve-auth.d.ts +11 -0
  9. package/dist/cli/commands/serve-auth.d.ts.map +1 -0
  10. package/dist/cli/commands/serve-auth.js +119 -0
  11. package/dist/cli/commands/serve-auth.js.map +1 -0
  12. package/dist/cli/commands/serve-core.d.ts +25 -0
  13. package/dist/cli/commands/serve-core.d.ts.map +1 -0
  14. package/dist/cli/commands/serve-core.js +258 -0
  15. package/dist/cli/commands/serve-core.js.map +1 -0
  16. package/dist/cli/commands/serve-tc.d.ts +12 -0
  17. package/dist/cli/commands/serve-tc.d.ts.map +1 -0
  18. package/dist/cli/commands/serve-tc.js +296 -0
  19. package/dist/cli/commands/serve-tc.js.map +1 -0
  20. package/dist/cli/commands/serve-types.d.ts +38 -0
  21. package/dist/cli/commands/serve-types.d.ts.map +1 -0
  22. package/dist/cli/commands/serve-types.js +108 -0
  23. package/dist/cli/commands/serve-types.js.map +1 -0
  24. package/dist/cli/commands/serve.d.ts +6 -0
  25. package/dist/cli/commands/serve.d.ts.map +1 -1
  26. package/dist/cli/commands/serve.js +41 -1144
  27. package/dist/cli/commands/serve.js.map +1 -1
  28. package/dist/cli/commands/setup.js +2 -2
  29. package/dist/cli/commands/setup.js.map +1 -1
  30. package/dist/cli/interactive/actions/audit.d.ts +7 -0
  31. package/dist/cli/interactive/actions/audit.d.ts.map +1 -0
  32. package/dist/cli/interactive/actions/audit.js +198 -0
  33. package/dist/cli/interactive/actions/audit.js.map +1 -0
  34. package/dist/cli/interactive/actions/demo.d.ts +7 -0
  35. package/dist/cli/interactive/actions/demo.d.ts.map +1 -0
  36. package/dist/cli/interactive/actions/demo.js +113 -0
  37. package/dist/cli/interactive/actions/demo.js.map +1 -0
  38. package/dist/cli/interactive/actions/guard.d.ts +7 -0
  39. package/dist/cli/interactive/actions/guard.d.ts.map +1 -0
  40. package/dist/cli/interactive/actions/guard.js +164 -0
  41. package/dist/cli/interactive/actions/guard.js.map +1 -0
  42. package/dist/cli/interactive/actions/misc.d.ts +13 -0
  43. package/dist/cli/interactive/actions/misc.d.ts.map +1 -0
  44. package/dist/cli/interactive/actions/misc.js +209 -0
  45. package/dist/cli/interactive/actions/misc.js.map +1 -0
  46. package/dist/cli/interactive/actions/scan.d.ts +7 -0
  47. package/dist/cli/interactive/actions/scan.d.ts.map +1 -0
  48. package/dist/cli/interactive/actions/scan.js +143 -0
  49. package/dist/cli/interactive/actions/scan.js.map +1 -0
  50. package/dist/cli/interactive/actions/setup.d.ts +8 -0
  51. package/dist/cli/interactive/actions/setup.d.ts.map +1 -0
  52. package/dist/cli/interactive/actions/setup.js +130 -0
  53. package/dist/cli/interactive/actions/setup.js.map +1 -0
  54. package/dist/cli/interactive/lang.d.ts +11 -0
  55. package/dist/cli/interactive/lang.d.ts.map +1 -0
  56. package/dist/cli/interactive/lang.js +51 -0
  57. package/dist/cli/interactive/lang.js.map +1 -0
  58. package/dist/cli/interactive/menu-defs.d.ts +17 -0
  59. package/dist/cli/interactive/menu-defs.d.ts.map +1 -0
  60. package/dist/cli/interactive/menu-defs.js +106 -0
  61. package/dist/cli/interactive/menu-defs.js.map +1 -0
  62. package/dist/cli/interactive/render.d.ts +16 -0
  63. package/dist/cli/interactive/render.d.ts.map +1 -0
  64. package/dist/cli/interactive/render.js +145 -0
  65. package/dist/cli/interactive/render.js.map +1 -0
  66. package/dist/cli/interactive.d.ts.map +1 -1
  67. package/dist/cli/interactive.js +64 -1322
  68. package/dist/cli/interactive.js.map +1 -1
  69. package/package.json +7 -6
@@ -6,321 +6,50 @@
6
6
  *
7
7
  * @module @panguard-ai/panguard/cli/interactive
8
8
  */
9
- import { existsSync, readFileSync, writeFileSync, mkdirSync, openSync, closeSync } from 'node:fs';
10
- import { join } from 'node:path';
11
- import { homedir } from 'node:os';
12
- import { c, spinner, colorSeverity, formatDuration, box } from '@panguard-ai/core';
13
- import { PANGUARD_VERSION } from '../index.js';
14
- import { renderLogo, theme } from './theme.js';
15
- import { waitForMainInput, pressAnyKey, cleanupTerminal, renderCompactMenu, waitForCompactChoice, } from './menu.js';
16
- import { checkFeatureAccess, showUpgradePrompt, getLicense } from './auth-guard.js';
17
- import { breadcrumb, nextSteps, confirmDestructive, moduleCountDisplay, formatError, } from './ux-helpers.js';
18
- // ---------------------------------------------------------------------------
19
- // Language management
20
- // ---------------------------------------------------------------------------
21
- const CONFIG_DIR = join(homedir(), '.panguard');
22
- const CONFIG_PATH = join(CONFIG_DIR, 'config.json');
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
- currentLang = lang === 'en' ? 'en' : lang === 'zh-TW' ? 'zh-TW' : detectLang();
27
+ setLang(lang === 'en' ? 'en' : lang === 'zh-TW' ? 'zh-TW' : detectLang());
299
28
  const exit = () => {
300
29
  cleanupTerminal();
301
- const msg = currentLang === 'zh-TW' ? '\u611F\u8B1D\u4F7F\u7528 Panguard AI\uFF01' : 'Goodbye!';
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 — detect MCP status and guide user
36
+ renderStartup(getLang());
37
+ // First-time user hint
309
38
  const mcpConfigured = await isMCPConfigured();
310
- if (!existsSync(CONFIG_PATH)) {
311
- console.log(currentLang === 'zh-TW'
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(currentLang === 'zh-TW'
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(currentLang === 'zh-TW'
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(currentLang === 'zh-TW'
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
- currentLang = currentLang === 'zh-TW' ? 'en' : 'zh-TW';
342
- saveLang(currentLang);
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, currentLang);
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), `${currentLang === 'zh-TW' ? '\u57F7\u884C' : 'Running'} ${def.key}`, currentLang === 'zh-TW'
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(currentLang === 'zh-TW'
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(currentLang);
159
+ await runDoctor(lang);
434
160
  }
435
161
  catch (err) {
436
- console.log(formatError(err instanceof Error ? err.message : String(err), currentLang === 'zh-TW'
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